1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* |
3 | * Copyright (c) 2000,2002-2003,2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. |
5 | */ |
6 | #ifndef __XFS_ATTR_H__ |
7 | #define __XFS_ATTR_H__ |
8 | |
9 | struct xfs_inode; |
10 | struct xfs_da_args; |
11 | struct xfs_attr_list_context; |
12 | |
13 | /* |
14 | * Large attribute lists are structured around Btrees where all the data |
15 | * elements are in the leaf nodes. Attribute names are hashed into an int, |
16 | * then that int is used as the index into the Btree. Since the hashval |
17 | * of an attribute name may not be unique, we may have duplicate keys. |
18 | * The internal links in the Btree are logical block offsets into the file. |
19 | * |
20 | * Small attribute lists use a different format and are packed as tightly |
21 | * as possible so as to fit into the literal area of the inode. |
22 | */ |
23 | |
24 | /* |
25 | * The maximum size (into the kernel or returned from the kernel) of an |
26 | * attribute value or the buffer used for an attr_list() call. Larger |
27 | * sizes will result in an ERANGE return code. |
28 | */ |
29 | #define ATTR_MAX_VALUELEN (64*1024) /* max length of a value */ |
30 | |
31 | /* |
32 | * Kernel-internal version of the attrlist cursor. |
33 | */ |
34 | struct xfs_attrlist_cursor_kern { |
35 | __u32 hashval; /* hash value of next entry to add */ |
36 | __u32 blkno; /* block containing entry (suggestion) */ |
37 | __u32 offset; /* offset in list of equal-hashvals */ |
38 | __u16 pad1; /* padding to match user-level */ |
39 | __u8 pad2; /* padding to match user-level */ |
40 | __u8 initted; /* T/F: cursor has been initialized */ |
41 | }; |
42 | |
43 | |
44 | /*======================================================================== |
45 | * Structure used to pass context around among the routines. |
46 | *========================================================================*/ |
47 | |
48 | |
49 | /* void; state communicated via *context */ |
50 | typedef void (*put_listent_func_t)(struct xfs_attr_list_context *, int, |
51 | unsigned char *, int, int); |
52 | |
53 | struct xfs_attr_list_context { |
54 | struct xfs_trans *tp; |
55 | struct xfs_inode *dp; /* inode */ |
56 | struct xfs_attrlist_cursor_kern cursor; /* position in list */ |
57 | void *buffer; /* output buffer */ |
58 | |
59 | /* |
60 | * Abort attribute list iteration if non-zero. Can be used to pass |
61 | * error values to the xfs_attr_list caller. |
62 | */ |
63 | int seen_enough; |
64 | bool allow_incomplete; |
65 | |
66 | ssize_t count; /* num used entries */ |
67 | int dupcnt; /* count dup hashvals seen */ |
68 | int bufsize; /* total buffer size */ |
69 | int firstu; /* first used byte in buffer */ |
70 | unsigned int attr_filter; /* XFS_ATTR_{ROOT,SECURE} */ |
71 | int resynch; /* T/F: resynch with cursor */ |
72 | put_listent_func_t put_listent; /* list output fmt function */ |
73 | int index; /* index into output buffer */ |
74 | }; |
75 | |
76 | |
77 | /* |
78 | * ======================================================================== |
79 | * Structure used to pass context around among the delayed routines. |
80 | * ======================================================================== |
81 | */ |
82 | |
83 | /* |
84 | * Below is a state machine diagram for attr remove operations. The XFS_DAS_* |
85 | * states indicate places where the function would return -EAGAIN, and then |
86 | * immediately resume from after being called by the calling function. States |
87 | * marked as a "subroutine state" indicate that they belong to a subroutine, and |
88 | * so the calling function needs to pass them back to that subroutine to allow |
89 | * it to finish where it left off. But they otherwise do not have a role in the |
90 | * calling function other than just passing through. |
91 | * |
92 | * xfs_attr_remove_iter() |
93 | * │ |
94 | * v |
95 | * have attr to remove? ──n──> done |
96 | * │ |
97 | * y |
98 | * │ |
99 | * v |
100 | * are we short form? ──y──> xfs_attr_shortform_remove ──> done |
101 | * │ |
102 | * n |
103 | * │ |
104 | * V |
105 | * are we leaf form? ──y──> xfs_attr_leaf_removename ──> done |
106 | * │ |
107 | * n |
108 | * │ |
109 | * V |
110 | * ┌── need to setup state? |
111 | * │ │ |
112 | * n y |
113 | * │ │ |
114 | * │ v |
115 | * │ find attr and get state |
116 | * │ attr has remote blks? ──n─┐ |
117 | * │ │ v |
118 | * │ │ find and invalidate |
119 | * │ y the remote blocks. |
120 | * │ │ mark attr incomplete |
121 | * │ ├────────────────┘ |
122 | * └──────────┤ |
123 | * │ |
124 | * v |
125 | * Have remote blks to remove? ───y─────┐ |
126 | * │ ^ remove the blks |
127 | * │ │ │ |
128 | * │ │ v |
129 | * │ XFS_DAS_RMTBLK <─n── done? |
130 | * │ re-enter with │ |
131 | * │ one less blk to y |
132 | * │ remove │ |
133 | * │ V |
134 | * │ refill the state |
135 | * n │ |
136 | * │ v |
137 | * │ XFS_DAS_RM_NAME |
138 | * │ │ |
139 | * ├─────────────────────────┘ |
140 | * │ |
141 | * v |
142 | * remove leaf and |
143 | * update hash with |
144 | * xfs_attr_node_remove_cleanup |
145 | * │ |
146 | * v |
147 | * need to |
148 | * shrink tree? ─n─┐ |
149 | * │ │ |
150 | * y │ |
151 | * │ │ |
152 | * v │ |
153 | * join leaf │ |
154 | * │ │ |
155 | * v │ |
156 | * XFS_DAS_RM_SHRINK │ |
157 | * │ │ |
158 | * v │ |
159 | * do the shrink │ |
160 | * │ │ |
161 | * v │ |
162 | * free state <──┘ |
163 | * │ |
164 | * v |
165 | * done |
166 | * |
167 | * |
168 | * Below is a state machine diagram for attr set operations. |
169 | * |
170 | * It seems the challenge with understanding this system comes from trying to |
171 | * absorb the state machine all at once, when really one should only be looking |
172 | * at it with in the context of a single function. Once a state sensitive |
173 | * function is called, the idea is that it "takes ownership" of the |
174 | * state machine. It isn't concerned with the states that may have belonged to |
175 | * it's calling parent. Only the states relevant to itself or any other |
176 | * subroutines there in. Once a calling function hands off the state machine to |
177 | * a subroutine, it needs to respect the simple rule that it doesn't "own" the |
178 | * state machine anymore, and it's the responsibility of that calling function |
179 | * to propagate the -EAGAIN back up the call stack. Upon reentry, it is |
180 | * committed to re-calling that subroutine until it returns something other than |
181 | * -EAGAIN. Once that subroutine signals completion (by returning anything other |
182 | * than -EAGAIN), the calling function can resume using the state machine. |
183 | * |
184 | * xfs_attr_set_iter() |
185 | * │ |
186 | * v |
187 | * ┌─y─ has an attr fork? |
188 | * │ | |
189 | * │ n |
190 | * │ | |
191 | * │ V |
192 | * │ add a fork |
193 | * │ │ |
194 | * └──────────┤ |
195 | * │ |
196 | * V |
197 | * ┌─── is shortform? |
198 | * │ │ |
199 | * │ y |
200 | * │ │ |
201 | * │ V |
202 | * │ xfs_attr_set_fmt |
203 | * │ | |
204 | * │ V |
205 | * │ xfs_attr_try_sf_addname |
206 | * │ │ |
207 | * │ V |
208 | * │ had enough ──y──> done |
209 | * │ space? |
210 | * n │ |
211 | * │ n |
212 | * │ │ |
213 | * │ V |
214 | * │ transform to leaf |
215 | * │ │ |
216 | * │ V |
217 | * │ hold the leaf buffer |
218 | * │ │ |
219 | * │ V |
220 | * │ return -EAGAIN |
221 | * │ Re-enter in |
222 | * │ leaf form |
223 | * │ |
224 | * └─> release leaf buffer |
225 | * if needed |
226 | * │ |
227 | * V |
228 | * ┌───n── fork has |
229 | * │ only 1 blk? |
230 | * │ │ |
231 | * │ y |
232 | * │ │ |
233 | * │ v |
234 | * │ xfs_attr_leaf_try_add() |
235 | * │ │ |
236 | * │ v |
237 | * │ had enough ──────────────y─────────────┐ |
238 | * │ space? │ |
239 | * │ │ │ |
240 | * │ n │ |
241 | * │ │ │ |
242 | * │ v │ |
243 | * │ return -EAGAIN │ |
244 | * │ re-enter in │ |
245 | * │ node form │ |
246 | * │ │ │ |
247 | * └──────────┤ │ |
248 | * │ │ |
249 | * V │ |
250 | * xfs_attr_node_addname_find_attr │ |
251 | * determines if this │ |
252 | * is create or rename │ |
253 | * find space to store attr │ |
254 | * │ │ |
255 | * v │ |
256 | * xfs_attr_node_addname │ |
257 | * │ │ |
258 | * v │ |
259 | * fits in a node leaf? ────n─────┐ │ |
260 | * │ ^ v │ |
261 | * │ │ single leaf node? │ |
262 | * │ │ │ │ │ |
263 | * y │ y n │ |
264 | * │ │ │ │ │ |
265 | * v │ v v │ |
266 | * update │ grow the leaf split if │ |
267 | * hashvals └── return -EAGAIN needed │ |
268 | * │ retry leaf add │ │ |
269 | * │ on reentry │ │ |
270 | * ├────────────────────────────┘ │ |
271 | * │ │ |
272 | * v │ |
273 | * need to alloc │ |
274 | * ┌─y── or flip flag? │ |
275 | * │ │ │ |
276 | * │ n │ |
277 | * │ │ │ |
278 | * │ v │ |
279 | * │ done │ |
280 | * │ │ |
281 | * │ │ |
282 | * │ XFS_DAS_FOUND_LBLK <────────────────┘ |
283 | * │ │ |
284 | * │ V |
285 | * │ xfs_attr_leaf_addname() |
286 | * │ │ |
287 | * │ v |
288 | * │ ┌──first time through? |
289 | * │ │ │ |
290 | * │ │ y |
291 | * │ │ │ |
292 | * │ n v |
293 | * │ │ if we have rmt blks |
294 | * │ │ find space for them |
295 | * │ │ │ |
296 | * │ └──────────┤ |
297 | * │ │ |
298 | * │ v |
299 | * │ still have |
300 | * │ ┌─n─ blks to alloc? <──┐ |
301 | * │ │ │ │ |
302 | * │ │ y │ |
303 | * │ │ │ │ |
304 | * │ │ v │ |
305 | * │ │ alloc one blk │ |
306 | * │ │ return -EAGAIN ──┘ |
307 | * │ │ re-enter with one |
308 | * │ │ less blk to alloc |
309 | * │ │ |
310 | * │ │ |
311 | * │ └───> set the rmt |
312 | * │ value |
313 | * │ │ |
314 | * │ v |
315 | * │ was this |
316 | * │ a rename? ──n─┐ |
317 | * │ │ │ |
318 | * │ y │ |
319 | * │ │ │ |
320 | * │ v │ |
321 | * │ flip incomplete │ |
322 | * │ flag │ |
323 | * │ │ │ |
324 | * │ v │ |
325 | * │ XFS_DAS_FLIP_LFLAG │ |
326 | * │ │ │ |
327 | * │ v │ |
328 | * │ need to remove │ |
329 | * │ old bks? ──n──┤ |
330 | * │ │ │ |
331 | * │ y │ |
332 | * │ │ │ |
333 | * │ V │ |
334 | * │ remove │ |
335 | * │ ┌───> old blks │ |
336 | * │ │ │ │ |
337 | * │ XFS_DAS_RM_LBLK │ │ |
338 | * │ ^ │ │ |
339 | * │ │ v │ |
340 | * │ └──y── more to │ |
341 | * │ remove? │ |
342 | * │ │ │ |
343 | * │ n │ |
344 | * │ │ │ |
345 | * │ v │ |
346 | * │ XFS_DAS_RD_LEAF │ |
347 | * │ │ │ |
348 | * │ v │ |
349 | * │ remove leaf │ |
350 | * │ │ │ |
351 | * │ v │ |
352 | * │ shrink to sf │ |
353 | * │ if needed │ |
354 | * │ │ │ |
355 | * │ v │ |
356 | * │ done <──────┘ |
357 | * │ |
358 | * └──────> XFS_DAS_FOUND_NBLK |
359 | * │ |
360 | * v |
361 | * ┌─────n── need to |
362 | * │ alloc blks? |
363 | * │ │ |
364 | * │ y |
365 | * │ │ |
366 | * │ v |
367 | * │ find space |
368 | * │ │ |
369 | * │ v |
370 | * │ ┌─>XFS_DAS_ALLOC_NODE |
371 | * │ │ │ |
372 | * │ │ v |
373 | * │ │ alloc blk |
374 | * │ │ │ |
375 | * │ │ v |
376 | * │ └──y── need to alloc |
377 | * │ more blocks? |
378 | * │ │ |
379 | * │ n |
380 | * │ │ |
381 | * │ v |
382 | * │ set the rmt value |
383 | * │ │ |
384 | * │ v |
385 | * │ was this |
386 | * └────────> a rename? ──n─┐ |
387 | * │ │ |
388 | * y │ |
389 | * │ │ |
390 | * v │ |
391 | * flip incomplete │ |
392 | * flag │ |
393 | * │ │ |
394 | * v │ |
395 | * XFS_DAS_FLIP_NFLAG │ |
396 | * │ │ |
397 | * v │ |
398 | * need to │ |
399 | * remove blks? ─n──┤ |
400 | * │ │ |
401 | * y │ |
402 | * │ │ |
403 | * v │ |
404 | * remove │ |
405 | * ┌────────> old blks │ |
406 | * │ │ │ |
407 | * XFS_DAS_RM_NBLK │ │ |
408 | * ^ │ │ |
409 | * │ v │ |
410 | * └──────y── more to │ |
411 | * remove │ |
412 | * │ │ |
413 | * n │ |
414 | * │ │ |
415 | * v │ |
416 | * XFS_DAS_CLR_FLAG │ |
417 | * │ │ |
418 | * v │ |
419 | * clear flags │ |
420 | * │ │ |
421 | * ├──────────┘ |
422 | * │ |
423 | * v |
424 | * done |
425 | */ |
426 | |
427 | /* |
428 | * Enum values for xfs_attr_intent.xattri_da_state |
429 | * |
430 | * These values are used by delayed attribute operations to keep track of where |
431 | * they were before they returned -EAGAIN. A return code of -EAGAIN signals the |
432 | * calling function to roll the transaction, and then call the subroutine to |
433 | * finish the operation. The enum is then used by the subroutine to jump back |
434 | * to where it was and resume executing where it left off. |
435 | */ |
436 | enum xfs_delattr_state { |
437 | XFS_DAS_UNINIT = 0, /* No state has been set yet */ |
438 | |
439 | /* |
440 | * Initial sequence states. The replace setup code relies on the |
441 | * ADD and REMOVE states for a specific format to be sequential so |
442 | * that we can transform the initial operation to be performed |
443 | * according to the xfs_has_larp() state easily. |
444 | */ |
445 | XFS_DAS_SF_ADD, /* Initial sf add state */ |
446 | XFS_DAS_SF_REMOVE, /* Initial sf replace/remove state */ |
447 | |
448 | XFS_DAS_LEAF_ADD, /* Initial leaf add state */ |
449 | XFS_DAS_LEAF_REMOVE, /* Initial leaf replace/remove state */ |
450 | |
451 | XFS_DAS_NODE_ADD, /* Initial node add state */ |
452 | XFS_DAS_NODE_REMOVE, /* Initial node replace/remove state */ |
453 | |
454 | /* Leaf state set/replace/remove sequence */ |
455 | XFS_DAS_LEAF_SET_RMT, /* set a remote xattr from a leaf */ |
456 | XFS_DAS_LEAF_ALLOC_RMT, /* We are allocating remote blocks */ |
457 | XFS_DAS_LEAF_REPLACE, /* Perform replace ops on a leaf */ |
458 | XFS_DAS_LEAF_REMOVE_OLD, /* Start removing old attr from leaf */ |
459 | XFS_DAS_LEAF_REMOVE_RMT, /* A rename is removing remote blocks */ |
460 | XFS_DAS_LEAF_REMOVE_ATTR, /* Remove the old attr from a leaf */ |
461 | |
462 | /* Node state sequence, must match leaf state above */ |
463 | XFS_DAS_NODE_SET_RMT, /* set a remote xattr from a node */ |
464 | XFS_DAS_NODE_ALLOC_RMT, /* We are allocating remote blocks */ |
465 | XFS_DAS_NODE_REPLACE, /* Perform replace ops on a node */ |
466 | XFS_DAS_NODE_REMOVE_OLD, /* Start removing old attr from node */ |
467 | XFS_DAS_NODE_REMOVE_RMT, /* A rename is removing remote blocks */ |
468 | XFS_DAS_NODE_REMOVE_ATTR, /* Remove the old attr from a node */ |
469 | |
470 | XFS_DAS_DONE, /* finished operation */ |
471 | }; |
472 | |
473 | #define XFS_DAS_STRINGS \ |
474 | { XFS_DAS_UNINIT, "XFS_DAS_UNINIT" }, \ |
475 | { XFS_DAS_SF_ADD, "XFS_DAS_SF_ADD" }, \ |
476 | { XFS_DAS_SF_REMOVE, "XFS_DAS_SF_REMOVE" }, \ |
477 | { XFS_DAS_LEAF_ADD, "XFS_DAS_LEAF_ADD" }, \ |
478 | { XFS_DAS_LEAF_REMOVE, "XFS_DAS_LEAF_REMOVE" }, \ |
479 | { XFS_DAS_NODE_ADD, "XFS_DAS_NODE_ADD" }, \ |
480 | { XFS_DAS_NODE_REMOVE, "XFS_DAS_NODE_REMOVE" }, \ |
481 | { XFS_DAS_LEAF_SET_RMT, "XFS_DAS_LEAF_SET_RMT" }, \ |
482 | { XFS_DAS_LEAF_ALLOC_RMT, "XFS_DAS_LEAF_ALLOC_RMT" }, \ |
483 | { XFS_DAS_LEAF_REPLACE, "XFS_DAS_LEAF_REPLACE" }, \ |
484 | { XFS_DAS_LEAF_REMOVE_OLD, "XFS_DAS_LEAF_REMOVE_OLD" }, \ |
485 | { XFS_DAS_LEAF_REMOVE_RMT, "XFS_DAS_LEAF_REMOVE_RMT" }, \ |
486 | { XFS_DAS_LEAF_REMOVE_ATTR, "XFS_DAS_LEAF_REMOVE_ATTR" }, \ |
487 | { XFS_DAS_NODE_SET_RMT, "XFS_DAS_NODE_SET_RMT" }, \ |
488 | { XFS_DAS_NODE_ALLOC_RMT, "XFS_DAS_NODE_ALLOC_RMT" }, \ |
489 | { XFS_DAS_NODE_REPLACE, "XFS_DAS_NODE_REPLACE" }, \ |
490 | { XFS_DAS_NODE_REMOVE_OLD, "XFS_DAS_NODE_REMOVE_OLD" }, \ |
491 | { XFS_DAS_NODE_REMOVE_RMT, "XFS_DAS_NODE_REMOVE_RMT" }, \ |
492 | { XFS_DAS_NODE_REMOVE_ATTR, "XFS_DAS_NODE_REMOVE_ATTR" }, \ |
493 | { XFS_DAS_DONE, "XFS_DAS_DONE" } |
494 | |
495 | struct xfs_attri_log_nameval; |
496 | |
497 | /* |
498 | * Context used for keeping track of delayed attribute operations |
499 | */ |
500 | struct xfs_attr_intent { |
501 | /* |
502 | * used to log this item to an intent containing a list of attrs to |
503 | * commit later |
504 | */ |
505 | struct list_head xattri_list; |
506 | |
507 | /* Used in xfs_attr_node_removename to roll through removing blocks */ |
508 | struct xfs_da_state *xattri_da_state; |
509 | |
510 | struct xfs_da_args *xattri_da_args; |
511 | |
512 | /* |
513 | * Shared buffer containing the attr name and value so that the logging |
514 | * code can share large memory buffers between log items. |
515 | */ |
516 | struct xfs_attri_log_nameval *xattri_nameval; |
517 | |
518 | /* Used to keep track of current state of delayed operation */ |
519 | enum xfs_delattr_state xattri_dela_state; |
520 | |
521 | /* |
522 | * Attr operation being performed - XFS_ATTRI_OP_FLAGS_* |
523 | */ |
524 | unsigned int xattri_op_flags; |
525 | |
526 | /* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */ |
527 | xfs_dablk_t xattri_lblkno; |
528 | int xattri_blkcnt; |
529 | struct xfs_bmbt_irec xattri_map; |
530 | }; |
531 | |
532 | |
533 | /*======================================================================== |
534 | * Function prototypes for the kernel. |
535 | *========================================================================*/ |
536 | |
537 | /* |
538 | * Overall external interface routines. |
539 | */ |
540 | int xfs_attr_inactive(struct xfs_inode *dp); |
541 | int xfs_attr_list_ilocked(struct xfs_attr_list_context *); |
542 | int xfs_attr_list(struct xfs_attr_list_context *); |
543 | int xfs_inode_hasattr(struct xfs_inode *ip); |
544 | bool xfs_attr_is_leaf(struct xfs_inode *ip); |
545 | int xfs_attr_get_ilocked(struct xfs_da_args *args); |
546 | int xfs_attr_get(struct xfs_da_args *args); |
547 | int xfs_attr_set(struct xfs_da_args *args); |
548 | int xfs_attr_set_iter(struct xfs_attr_intent *attr); |
549 | int xfs_attr_remove_iter(struct xfs_attr_intent *attr); |
550 | bool xfs_attr_namecheck(const void *name, size_t length); |
551 | int xfs_attr_calc_size(struct xfs_da_args *args, int *local); |
552 | void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres, |
553 | unsigned int *total); |
554 | |
555 | /* |
556 | * Check to see if the attr should be upgraded from non-existent or shortform to |
557 | * single-leaf-block attribute list. |
558 | */ |
559 | static inline bool |
560 | xfs_attr_is_shortform( |
561 | struct xfs_inode *ip) |
562 | { |
563 | return ip->i_af.if_format == XFS_DINODE_FMT_LOCAL || |
564 | (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS && |
565 | ip->i_af.if_nextents == 0); |
566 | } |
567 | |
568 | static inline enum xfs_delattr_state |
569 | xfs_attr_init_add_state(struct xfs_da_args *args) |
570 | { |
571 | /* |
572 | * When called from the completion of a attr remove to determine the |
573 | * next state, the attribute fork may be null. This can occur only occur |
574 | * on a pure remove, but we grab the next state before we check if a |
575 | * replace operation is being performed. If we are called from any other |
576 | * context, i_af is guaranteed to exist. Hence if the attr fork is |
577 | * null, we were called from a pure remove operation and so we are done. |
578 | */ |
579 | if (!xfs_inode_has_attr_fork(args->dp)) |
580 | return XFS_DAS_DONE; |
581 | |
582 | args->op_flags |= XFS_DA_OP_ADDNAME; |
583 | if (xfs_attr_is_shortform(args->dp)) |
584 | return XFS_DAS_SF_ADD; |
585 | if (xfs_attr_is_leaf(args->dp)) |
586 | return XFS_DAS_LEAF_ADD; |
587 | return XFS_DAS_NODE_ADD; |
588 | } |
589 | |
590 | static inline enum xfs_delattr_state |
591 | xfs_attr_init_remove_state(struct xfs_da_args *args) |
592 | { |
593 | args->op_flags |= XFS_DA_OP_REMOVE; |
594 | if (xfs_attr_is_shortform(args->dp)) |
595 | return XFS_DAS_SF_REMOVE; |
596 | if (xfs_attr_is_leaf(args->dp)) |
597 | return XFS_DAS_LEAF_REMOVE; |
598 | return XFS_DAS_NODE_REMOVE; |
599 | } |
600 | |
601 | /* |
602 | * If we are logging the attributes, then we have to start with removal of the |
603 | * old attribute so that there is always consistent state that we can recover |
604 | * from if the system goes down part way through. We always log the new attr |
605 | * value, so even when we remove the attr first we still have the information in |
606 | * the log to finish the replace operation atomically. |
607 | */ |
608 | static inline enum xfs_delattr_state |
609 | xfs_attr_init_replace_state(struct xfs_da_args *args) |
610 | { |
611 | args->op_flags |= XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE; |
612 | if (args->op_flags & XFS_DA_OP_LOGGED) |
613 | return xfs_attr_init_remove_state(args); |
614 | return xfs_attr_init_add_state(args); |
615 | } |
616 | |
617 | extern struct kmem_cache *xfs_attr_intent_cache; |
618 | int __init xfs_attr_intent_init_cache(void); |
619 | void xfs_attr_intent_destroy_cache(void); |
620 | |
621 | #endif /* __XFS_ATTR_H__ */ |
622 | |