Commit | Line | Data |
---|---|---|
0b61f8a4 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 | 2 | /* |
7b718769 NS |
3 | * Copyright (c) 2000-2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. | |
1da177e4 | 5 | */ |
1da177e4 | 6 | #include "xfs.h" |
a844f451 | 7 | #include "xfs_fs.h" |
70a9883c | 8 | #include "xfs_shared.h" |
239880ef DC |
9 | #include "xfs_format.h" |
10 | #include "xfs_log_format.h" | |
11 | #include "xfs_trans_resv.h" | |
1da177e4 | 12 | #include "xfs_mount.h" |
3ab78df2 | 13 | #include "xfs_defer.h" |
57062787 | 14 | #include "xfs_da_format.h" |
a844f451 | 15 | #include "xfs_da_btree.h" |
a844f451 | 16 | #include "xfs_attr_sf.h" |
1da177e4 | 17 | #include "xfs_inode.h" |
239880ef | 18 | #include "xfs_trans.h" |
1da177e4 | 19 | #include "xfs_bmap.h" |
a4fbe6ab | 20 | #include "xfs_bmap_btree.h" |
1da177e4 LT |
21 | #include "xfs_attr.h" |
22 | #include "xfs_attr_leaf.h" | |
95920cd6 | 23 | #include "xfs_attr_remote.h" |
1da177e4 | 24 | #include "xfs_quota.h" |
1da177e4 | 25 | #include "xfs_trans_space.h" |
0b1b213f | 26 | #include "xfs_trace.h" |
fd920008 | 27 | #include "xfs_attr_item.h" |
d9c61ccb | 28 | #include "xfs_xattr.h" |
fd920008 | 29 | |
e2c78949 | 30 | struct kmem_cache *xfs_attr_intent_cache; |
1da177e4 LT |
31 | |
32 | /* | |
33 | * xfs_attr.c | |
34 | * | |
35 | * Provide the external interfaces to manage attribute lists. | |
36 | */ | |
37 | ||
38 | /*======================================================================== | |
39 | * Function prototypes for the kernel. | |
40 | *========================================================================*/ | |
41 | ||
42 | /* | |
43 | * Internal routines when attribute list fits inside the inode. | |
44 | */ | |
45 | STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args); | |
46 | ||
47 | /* | |
48 | * Internal routines when attribute list is one block. | |
49 | */ | |
ba0f32d4 | 50 | STATIC int xfs_attr_leaf_get(xfs_da_args_t *args); |
1da177e4 | 51 | STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args); |
07120f1a | 52 | STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp); |
e53bcffa | 53 | STATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args); |
1da177e4 LT |
54 | |
55 | /* | |
56 | * Internal routines when attribute list is more than one block. | |
57 | */ | |
ba0f32d4 | 58 | STATIC int xfs_attr_node_get(xfs_da_args_t *args); |
5d954cc0 | 59 | STATIC void xfs_attr_restore_rmt_blk(struct xfs_da_args *args); |
e3c5de22 DW |
60 | static int xfs_attr_node_try_addname(struct xfs_attr_intent *attr); |
61 | STATIC int xfs_attr_node_addname_find_attr(struct xfs_attr_intent *attr); | |
62 | STATIC int xfs_attr_node_remove_attr(struct xfs_attr_intent *attr); | |
4d0cdd2b DW |
63 | STATIC int xfs_attr_node_lookup(struct xfs_da_args *args, |
64 | struct xfs_da_state *state); | |
1da177e4 | 65 | |
abec5f2b | 66 | int |
caf8aabd CH |
67 | xfs_inode_hasattr( |
68 | struct xfs_inode *ip) | |
69 | { | |
932b42c6 | 70 | if (!xfs_inode_has_attr_fork(ip)) |
fdaf1bb3 | 71 | return 0; |
2ed5b09b DW |
72 | if (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS && |
73 | ip->i_af.if_nextents == 0) | |
caf8aabd CH |
74 | return 0; |
75 | return 1; | |
76 | } | |
77 | ||
2ac131df CH |
78 | /* |
79 | * Returns true if the there is exactly only block in the attr fork, in which | |
80 | * case the attribute fork consists of a single leaf block entry. | |
81 | */ | |
82 | bool | |
83 | xfs_attr_is_leaf( | |
84 | struct xfs_inode *ip) | |
85 | { | |
2ed5b09b | 86 | struct xfs_ifork *ifp = &ip->i_af; |
2ac131df CH |
87 | struct xfs_iext_cursor icur; |
88 | struct xfs_bmbt_irec imap; | |
89 | ||
90 | if (ifp->if_nextents != 1 || ifp->if_format != XFS_DINODE_FMT_EXTENTS) | |
91 | return false; | |
92 | ||
93 | xfs_iext_first(ifp, &icur); | |
94 | xfs_iext_get_extent(ifp, &icur, &imap); | |
95 | return imap.br_startoff == 0 && imap.br_blockcount == 1; | |
96 | } | |
97 | ||
59782a23 DC |
98 | /* |
99 | * XXX (dchinner): name path state saving and refilling is an optimisation to | |
100 | * avoid needing to look up name entries after rolling transactions removing | |
101 | * remote xattr blocks between the name entry lookup and name entry removal. | |
102 | * This optimisation got sidelined when combining the set and remove state | |
103 | * machines, but the code has been left in place because it is worthwhile to | |
104 | * restore the optimisation once the combined state machine paths have settled. | |
105 | * | |
106 | * This comment is a public service announcement to remind Future Dave that he | |
107 | * still needs to restore this code to working order. | |
108 | */ | |
109 | #if 0 | |
110 | /* | |
111 | * Fill in the disk block numbers in the state structure for the buffers | |
112 | * that are attached to the state structure. | |
113 | * This is done so that we can quickly reattach ourselves to those buffers | |
114 | * after some set of transaction commits have released these buffers. | |
115 | */ | |
116 | static int | |
117 | xfs_attr_fillstate(xfs_da_state_t *state) | |
118 | { | |
119 | xfs_da_state_path_t *path; | |
120 | xfs_da_state_blk_t *blk; | |
121 | int level; | |
122 | ||
123 | trace_xfs_attr_fillstate(state->args); | |
124 | ||
125 | /* | |
126 | * Roll down the "path" in the state structure, storing the on-disk | |
127 | * block number for those buffers in the "path". | |
128 | */ | |
129 | path = &state->path; | |
130 | ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); | |
131 | for (blk = path->blk, level = 0; level < path->active; blk++, level++) { | |
132 | if (blk->bp) { | |
133 | blk->disk_blkno = xfs_buf_daddr(blk->bp); | |
134 | blk->bp = NULL; | |
135 | } else { | |
136 | blk->disk_blkno = 0; | |
137 | } | |
138 | } | |
139 | ||
140 | /* | |
141 | * Roll down the "altpath" in the state structure, storing the on-disk | |
142 | * block number for those buffers in the "altpath". | |
143 | */ | |
144 | path = &state->altpath; | |
145 | ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); | |
146 | for (blk = path->blk, level = 0; level < path->active; blk++, level++) { | |
147 | if (blk->bp) { | |
148 | blk->disk_blkno = xfs_buf_daddr(blk->bp); | |
149 | blk->bp = NULL; | |
150 | } else { | |
151 | blk->disk_blkno = 0; | |
152 | } | |
153 | } | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | /* | |
159 | * Reattach the buffers to the state structure based on the disk block | |
160 | * numbers stored in the state structure. | |
161 | * This is done after some set of transaction commits have released those | |
162 | * buffers from our grip. | |
163 | */ | |
164 | static int | |
165 | xfs_attr_refillstate(xfs_da_state_t *state) | |
166 | { | |
167 | xfs_da_state_path_t *path; | |
168 | xfs_da_state_blk_t *blk; | |
169 | int level, error; | |
170 | ||
171 | trace_xfs_attr_refillstate(state->args); | |
172 | ||
173 | /* | |
174 | * Roll down the "path" in the state structure, storing the on-disk | |
175 | * block number for those buffers in the "path". | |
176 | */ | |
177 | path = &state->path; | |
178 | ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); | |
179 | for (blk = path->blk, level = 0; level < path->active; blk++, level++) { | |
180 | if (blk->disk_blkno) { | |
181 | error = xfs_da3_node_read_mapped(state->args->trans, | |
182 | state->args->dp, blk->disk_blkno, | |
183 | &blk->bp, XFS_ATTR_FORK); | |
184 | if (error) | |
185 | return error; | |
186 | } else { | |
187 | blk->bp = NULL; | |
188 | } | |
189 | } | |
190 | ||
191 | /* | |
192 | * Roll down the "altpath" in the state structure, storing the on-disk | |
193 | * block number for those buffers in the "altpath". | |
194 | */ | |
195 | path = &state->altpath; | |
196 | ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); | |
197 | for (blk = path->blk, level = 0; level < path->active; blk++, level++) { | |
198 | if (blk->disk_blkno) { | |
199 | error = xfs_da3_node_read_mapped(state->args->trans, | |
200 | state->args->dp, blk->disk_blkno, | |
201 | &blk->bp, XFS_ATTR_FORK); | |
202 | if (error) | |
203 | return error; | |
204 | } else { | |
205 | blk->bp = NULL; | |
206 | } | |
207 | } | |
208 | ||
209 | return 0; | |
210 | } | |
211 | #else | |
212 | static int xfs_attr_fillstate(xfs_da_state_t *state) { return 0; } | |
213 | #endif | |
214 | ||
1da177e4 LT |
215 | /*======================================================================== |
216 | * Overall external interface routines. | |
217 | *========================================================================*/ | |
218 | ||
728bcaa3 DC |
219 | /* |
220 | * Retrieve an extended attribute and its value. Must have ilock. | |
221 | * Returns 0 on successful retrieval, otherwise an error. | |
222 | */ | |
ad017f65 DW |
223 | int |
224 | xfs_attr_get_ilocked( | |
ad017f65 DW |
225 | struct xfs_da_args *args) |
226 | { | |
3fed24ff | 227 | xfs_assert_ilocked(args->dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL); |
cf69f824 | 228 | |
c36f533f | 229 | if (!xfs_inode_hasattr(args->dp)) |
ad017f65 | 230 | return -ENOATTR; |
c36f533f | 231 | |
2ed5b09b | 232 | if (args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL) |
ad017f65 | 233 | return xfs_attr_shortform_getvalue(args); |
2ac131df | 234 | if (xfs_attr_is_leaf(args->dp)) |
ad017f65 | 235 | return xfs_attr_leaf_get(args); |
c36f533f | 236 | return xfs_attr_node_get(args); |
ad017f65 DW |
237 | } |
238 | ||
ddbca70c DC |
239 | /* |
240 | * Retrieve an extended attribute by name, and its value if requested. | |
241 | * | |
e513e25c CH |
242 | * If args->valuelen is zero, then the caller does not want the value, just an |
243 | * indication whether the attribute exists and the size of the value if it | |
244 | * exists. The size is returned in args.valuelen. | |
ddbca70c | 245 | * |
d49db18b CH |
246 | * If args->value is NULL but args->valuelen is non-zero, allocate the buffer |
247 | * for the value after existence of the attribute has been determined. The | |
248 | * caller always has to free args->value if it is set, no matter if this | |
249 | * function was successful or not. | |
250 | * | |
ddbca70c | 251 | * If the attribute is found, but exceeds the size limit set by the caller in |
e5171d7e CH |
252 | * args->valuelen, return -ERANGE with the size of the attribute that was found |
253 | * in args->valuelen. | |
ddbca70c | 254 | */ |
b87d022c CH |
255 | int |
256 | xfs_attr_get( | |
e5171d7e | 257 | struct xfs_da_args *args) |
1da177e4 | 258 | { |
b87d022c CH |
259 | uint lock_mode; |
260 | int error; | |
261 | ||
e5171d7e | 262 | XFS_STATS_INC(args->dp->i_mount, xs_attr_get); |
b87d022c | 263 | |
75c8c50f | 264 | if (xfs_is_shutdown(args->dp->i_mount)) |
2451337d | 265 | return -EIO; |
1da177e4 | 266 | |
e5171d7e CH |
267 | args->geo = args->dp->i_mount->m_attr_geo; |
268 | args->whichfork = XFS_ATTR_FORK; | |
269 | args->hashval = xfs_da_hashname(args->name, args->namelen); | |
b87d022c | 270 | |
c400ee3e | 271 | /* Entirely possible to look up a name which doesn't exist */ |
e5171d7e | 272 | args->op_flags = XFS_DA_OP_OKNOENT; |
1da177e4 | 273 | |
e5171d7e | 274 | lock_mode = xfs_ilock_attr_map_shared(args->dp); |
c36f533f | 275 | error = xfs_attr_get_ilocked(args); |
e5171d7e | 276 | xfs_iunlock(args->dp, lock_mode); |
ddbca70c | 277 | |
e5171d7e | 278 | return error; |
1da177e4 LT |
279 | } |
280 | ||
5e9da7b7 NS |
281 | /* |
282 | * Calculate how many blocks we need for the new attribute, | |
283 | */ | |
fd920008 | 284 | int |
5e9da7b7 | 285 | xfs_attr_calc_size( |
6c888af0 | 286 | struct xfs_da_args *args, |
5e9da7b7 NS |
287 | int *local) |
288 | { | |
6c888af0 | 289 | struct xfs_mount *mp = args->dp->i_mount; |
5e9da7b7 NS |
290 | int size; |
291 | int nblks; | |
292 | ||
293 | /* | |
294 | * Determine space new attribute will use, and if it would be | |
295 | * "local" or "remote" (note: local != inline). | |
296 | */ | |
c59f0ad2 | 297 | size = xfs_attr_leaf_newentsize(args, local); |
5e9da7b7 NS |
298 | nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK); |
299 | if (*local) { | |
33a60390 | 300 | if (size > (args->geo->blksize / 2)) { |
5e9da7b7 NS |
301 | /* Double split possible */ |
302 | nblks *= 2; | |
303 | } | |
304 | } else { | |
305 | /* | |
306 | * Out of line attribute, cannot double split, but | |
307 | * make room for the attribute value itself. | |
308 | */ | |
2d6dcc6d | 309 | uint dblocks = xfs_attr3_rmt_blocks(mp, args->valuelen); |
5e9da7b7 NS |
310 | nblks += dblocks; |
311 | nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK); | |
312 | } | |
313 | ||
314 | return nblks; | |
315 | } | |
316 | ||
c3546cf5 AH |
317 | /* Initialize transaction reservation for attr operations */ |
318 | void | |
319 | xfs_init_attr_trans( | |
320 | struct xfs_da_args *args, | |
321 | struct xfs_trans_res *tres, | |
322 | unsigned int *total) | |
323 | { | |
324 | struct xfs_mount *mp = args->dp->i_mount; | |
325 | ||
326 | if (args->value) { | |
327 | tres->tr_logres = M_RES(mp)->tr_attrsetm.tr_logres + | |
328 | M_RES(mp)->tr_attrsetrt.tr_logres * | |
329 | args->total; | |
330 | tres->tr_logcount = XFS_ATTRSET_LOG_COUNT; | |
331 | tres->tr_logflags = XFS_TRANS_PERM_LOG_RES; | |
332 | *total = args->total; | |
333 | } else { | |
334 | *tres = M_RES(mp)->tr_attrrm; | |
335 | *total = XFS_ATTRRM_SPACE_RES(mp); | |
336 | } | |
337 | } | |
338 | ||
e0c41089 DC |
339 | /* |
340 | * Add an attr to a shortform fork. If there is no space, | |
341 | * xfs_attr_shortform_addname() will convert to leaf format and return -ENOSPC. | |
342 | * to use. | |
343 | */ | |
4c74a56b AH |
344 | STATIC int |
345 | xfs_attr_try_sf_addname( | |
346 | struct xfs_inode *dp, | |
347 | struct xfs_da_args *args) | |
348 | { | |
349 | ||
6cc5b5f8 AC |
350 | int error; |
351 | ||
352 | /* | |
353 | * Build initial attribute list (if required). | |
354 | */ | |
2ed5b09b | 355 | if (dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS) |
6cc5b5f8 | 356 | xfs_attr_shortform_create(args); |
4c74a56b AH |
357 | |
358 | error = xfs_attr_shortform_addname(args); | |
359 | if (error == -ENOSPC) | |
360 | return error; | |
361 | ||
362 | /* | |
363 | * Commit the shortform mods, and we're done. | |
364 | * NOTE: this is also the error path (EEXIST, etc). | |
365 | */ | |
1d733019 | 366 | if (!error && !(args->op_flags & XFS_DA_OP_NOTIME)) |
4c74a56b AH |
367 | xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG); |
368 | ||
0560f31a | 369 | if (xfs_has_wsync(dp->i_mount)) |
4c74a56b AH |
370 | xfs_trans_set_sync(args->trans); |
371 | ||
6cc5b5f8 | 372 | return error; |
4c74a56b AH |
373 | } |
374 | ||
e0c41089 | 375 | static int |
816c8e39 | 376 | xfs_attr_sf_addname( |
e3c5de22 | 377 | struct xfs_attr_intent *attr) |
db1a28cc | 378 | { |
d68c51e9 | 379 | struct xfs_da_args *args = attr->xattri_da_args; |
8f502a40 AH |
380 | struct xfs_inode *dp = args->dp; |
381 | int error = 0; | |
db1a28cc | 382 | |
db1a28cc | 383 | error = xfs_attr_try_sf_addname(dp, args); |
e0c41089 DC |
384 | if (error != -ENOSPC) { |
385 | ASSERT(!error || error == -EEXIST); | |
386 | attr->xattri_dela_state = XFS_DAS_DONE; | |
387 | goto out; | |
388 | } | |
6286514b | 389 | |
db1a28cc AC |
390 | /* |
391 | * It won't fit in the shortform, transform to a leaf block. GROT: | |
392 | * another possible req'mt for a double-split btree op. | |
393 | */ | |
e53bcffa | 394 | error = xfs_attr_shortform_to_leaf(args); |
db1a28cc AC |
395 | if (error) |
396 | return error; | |
397 | ||
e0c41089 | 398 | attr->xattri_dela_state = XFS_DAS_LEAF_ADD; |
e0c41089 DC |
399 | out: |
400 | trace_xfs_attr_sf_addname_return(attr->xattri_dela_state, args->dp); | |
401 | return error; | |
db1a28cc AC |
402 | } |
403 | ||
411b434a | 404 | /* |
fdaf1bb3 DC |
405 | * Handle the state change on completion of a multi-state attr operation. |
406 | * | |
407 | * If the XFS_DA_OP_REPLACE flag is set, this means the operation was the first | |
408 | * modification in a attr replace operation and we still have to do the second | |
409 | * state, indicated by @replace_state. | |
410 | * | |
411 | * We consume the XFS_DA_OP_REPLACE flag so that when we are called again on | |
412 | * completion of the second half of the attr replace operation we correctly | |
413 | * signal that it is done. | |
411b434a | 414 | */ |
fdaf1bb3 DC |
415 | static enum xfs_delattr_state |
416 | xfs_attr_complete_op( | |
e3c5de22 | 417 | struct xfs_attr_intent *attr, |
fdaf1bb3 | 418 | enum xfs_delattr_state replace_state) |
411b434a DC |
419 | { |
420 | struct xfs_da_args *args = attr->xattri_da_args; | |
fdaf1bb3 | 421 | bool do_replace = args->op_flags & XFS_DA_OP_REPLACE; |
411b434a | 422 | |
fdaf1bb3 | 423 | args->op_flags &= ~XFS_DA_OP_REPLACE; |
82ef1a53 AA |
424 | args->attr_filter &= ~XFS_ATTR_INCOMPLETE; |
425 | if (do_replace) | |
fdaf1bb3 | 426 | return replace_state; |
82ef1a53 | 427 | |
fdaf1bb3 | 428 | return XFS_DAS_DONE; |
411b434a DC |
429 | } |
430 | ||
e0c41089 | 431 | static int |
cd1549d6 | 432 | xfs_attr_leaf_addname( |
e3c5de22 | 433 | struct xfs_attr_intent *attr) |
cd1549d6 AH |
434 | { |
435 | struct xfs_da_args *args = attr->xattri_da_args; | |
cd1549d6 AH |
436 | int error; |
437 | ||
e0c41089 | 438 | ASSERT(xfs_attr_is_leaf(args->dp)); |
cd1549d6 | 439 | |
e0c41089 DC |
440 | /* |
441 | * Use the leaf buffer we may already hold locked as a result of | |
e53bcffa | 442 | * a sf-to-leaf conversion. |
e0c41089 | 443 | */ |
e53bcffa | 444 | error = xfs_attr_leaf_try_add(args); |
e22b88de | 445 | |
e0c41089 DC |
446 | if (error == -ENOSPC) { |
447 | error = xfs_attr3_leaf_to_node(args); | |
cd1549d6 AH |
448 | if (error) |
449 | return error; | |
450 | ||
e0c41089 DC |
451 | /* |
452 | * We're not in leaf format anymore, so roll the transaction and | |
453 | * retry the add to the newly allocated node block. | |
454 | */ | |
455 | attr->xattri_dela_state = XFS_DAS_NODE_ADD; | |
e0c41089 | 456 | goto out; |
a4b8917b DC |
457 | } |
458 | if (error) | |
459 | return error; | |
cd1549d6 | 460 | |
a4b8917b DC |
461 | /* |
462 | * We need to commit and roll if we need to allocate remote xattr blocks | |
463 | * or perform more xattr manipulations. Otherwise there is nothing more | |
464 | * to do and we can return success. | |
465 | */ | |
4e3d96a5 | 466 | if (args->rmtblkno) |
7d035336 | 467 | attr->xattri_dela_state = XFS_DAS_LEAF_SET_RMT; |
4e3d96a5 | 468 | else |
fdaf1bb3 DC |
469 | attr->xattri_dela_state = xfs_attr_complete_op(attr, |
470 | XFS_DAS_LEAF_REPLACE); | |
a4b8917b | 471 | out: |
cd1549d6 | 472 | trace_xfs_attr_leaf_addname_return(attr->xattri_dela_state, args->dp); |
a4b8917b | 473 | return error; |
cd1549d6 AH |
474 | } |
475 | ||
4e3d96a5 DC |
476 | /* |
477 | * Add an entry to a node format attr tree. | |
478 | * | |
479 | * Note that we might still have a leaf here - xfs_attr_is_leaf() cannot tell | |
480 | * the difference between leaf + remote attr blocks and a node format tree, | |
481 | * so we may still end up having to convert from leaf to node format here. | |
482 | */ | |
e0c41089 DC |
483 | static int |
484 | xfs_attr_node_addname( | |
e3c5de22 | 485 | struct xfs_attr_intent *attr) |
e0c41089 DC |
486 | { |
487 | struct xfs_da_args *args = attr->xattri_da_args; | |
488 | int error; | |
489 | ||
e0c41089 DC |
490 | error = xfs_attr_node_addname_find_attr(attr); |
491 | if (error) | |
492 | return error; | |
493 | ||
494 | error = xfs_attr_node_try_addname(attr); | |
4e3d96a5 DC |
495 | if (error == -ENOSPC) { |
496 | error = xfs_attr3_leaf_to_node(args); | |
497 | if (error) | |
498 | return error; | |
499 | /* | |
500 | * No state change, we really are in node form now | |
501 | * but we need the transaction rolled to continue. | |
502 | */ | |
503 | goto out; | |
504 | } | |
e0c41089 DC |
505 | if (error) |
506 | return error; | |
507 | ||
4e3d96a5 | 508 | if (args->rmtblkno) |
7d035336 | 509 | attr->xattri_dela_state = XFS_DAS_NODE_SET_RMT; |
4e3d96a5 | 510 | else |
fdaf1bb3 DC |
511 | attr->xattri_dela_state = xfs_attr_complete_op(attr, |
512 | XFS_DAS_NODE_REPLACE); | |
4e3d96a5 | 513 | out: |
e0c41089 DC |
514 | trace_xfs_attr_node_addname_return(attr->xattri_dela_state, args->dp); |
515 | return error; | |
516 | } | |
517 | ||
7d035336 DC |
518 | static int |
519 | xfs_attr_rmtval_alloc( | |
e3c5de22 | 520 | struct xfs_attr_intent *attr) |
7d035336 DC |
521 | { |
522 | struct xfs_da_args *args = attr->xattri_da_args; | |
523 | int error = 0; | |
524 | ||
525 | /* | |
526 | * If there was an out-of-line value, allocate the blocks we | |
527 | * identified for its storage and copy the value. This is done | |
528 | * after we create the attribute so that we don't overflow the | |
529 | * maximum size of a transaction and/or hit a deadlock. | |
530 | */ | |
531 | if (attr->xattri_blkcnt > 0) { | |
532 | error = xfs_attr_rmtval_set_blk(attr); | |
533 | if (error) | |
534 | return error; | |
411b434a | 535 | /* Roll the transaction only if there is more to allocate. */ |
4e3d96a5 | 536 | if (attr->xattri_blkcnt > 0) |
411b434a | 537 | goto out; |
7d035336 DC |
538 | } |
539 | ||
540 | error = xfs_attr_rmtval_set_value(args); | |
541 | if (error) | |
542 | return error; | |
543 | ||
fdaf1bb3 DC |
544 | attr->xattri_dela_state = xfs_attr_complete_op(attr, |
545 | ++attr->xattri_dela_state); | |
546 | /* | |
547 | * If we are not doing a rename, we've finished the operation but still | |
548 | * have to clear the incomplete flag protecting the new attr from | |
549 | * exposing partially initialised state if we crash during creation. | |
550 | */ | |
551 | if (attr->xattri_dela_state == XFS_DAS_DONE) | |
7d035336 | 552 | error = xfs_attr3_leaf_clearflag(args); |
7d035336 DC |
553 | out: |
554 | trace_xfs_attr_rmtval_alloc(attr->xattri_dela_state, args->dp); | |
555 | return error; | |
556 | } | |
e0c41089 | 557 | |
e5d5596a DC |
558 | /* |
559 | * Mark an attribute entry INCOMPLETE and save pointers to the relevant buffers | |
560 | * for later deletion of the entry. | |
561 | */ | |
562 | static int | |
563 | xfs_attr_leaf_mark_incomplete( | |
564 | struct xfs_da_args *args, | |
565 | struct xfs_da_state *state) | |
566 | { | |
567 | int error; | |
568 | ||
569 | /* | |
570 | * Fill in disk block numbers in the state structure | |
571 | * so that we can get the buffers back after we commit | |
572 | * several transactions in the following calls. | |
573 | */ | |
574 | error = xfs_attr_fillstate(state); | |
575 | if (error) | |
576 | return error; | |
577 | ||
578 | /* | |
579 | * Mark the attribute as INCOMPLETE | |
580 | */ | |
581 | return xfs_attr3_leaf_setflag(args); | |
582 | } | |
583 | ||
4d0cdd2b DW |
584 | /* Ensure the da state of an xattr deferred work item is ready to go. */ |
585 | static inline void | |
586 | xfs_attr_item_init_da_state( | |
e3c5de22 | 587 | struct xfs_attr_intent *attr) |
4d0cdd2b DW |
588 | { |
589 | struct xfs_da_args *args = attr->xattri_da_args; | |
590 | ||
591 | if (!attr->xattri_da_state) | |
592 | attr->xattri_da_state = xfs_da_state_alloc(args); | |
593 | else | |
594 | xfs_da_state_reset(attr->xattri_da_state, args); | |
595 | } | |
596 | ||
e5d5596a DC |
597 | /* |
598 | * Initial setup for xfs_attr_node_removename. Make sure the attr is there and | |
599 | * the blocks are valid. Attr keys with remote blocks will be marked | |
600 | * incomplete. | |
601 | */ | |
602 | static | |
603 | int xfs_attr_node_removename_setup( | |
e3c5de22 | 604 | struct xfs_attr_intent *attr) |
e5d5596a DC |
605 | { |
606 | struct xfs_da_args *args = attr->xattri_da_args; | |
309001c2 | 607 | struct xfs_da_state *state; |
e5d5596a DC |
608 | int error; |
609 | ||
4d0cdd2b DW |
610 | xfs_attr_item_init_da_state(attr); |
611 | error = xfs_attr_node_lookup(args, attr->xattri_da_state); | |
e5d5596a DC |
612 | if (error != -EEXIST) |
613 | goto out; | |
614 | error = 0; | |
615 | ||
309001c2 DW |
616 | state = attr->xattri_da_state; |
617 | ASSERT(state->path.blk[state->path.active - 1].bp != NULL); | |
618 | ASSERT(state->path.blk[state->path.active - 1].magic == | |
e5d5596a DC |
619 | XFS_ATTR_LEAF_MAGIC); |
620 | ||
309001c2 | 621 | error = xfs_attr_leaf_mark_incomplete(args, state); |
4b9879b1 DC |
622 | if (error) |
623 | goto out; | |
624 | if (args->rmtblkno > 0) | |
e5d5596a | 625 | error = xfs_attr_rmtval_invalidate(args); |
e5d5596a | 626 | out: |
309001c2 DW |
627 | if (error) { |
628 | xfs_da_state_free(attr->xattri_da_state); | |
629 | attr->xattri_da_state = NULL; | |
630 | } | |
e5d5596a DC |
631 | |
632 | return error; | |
633 | } | |
634 | ||
b11fa61b DC |
635 | /* |
636 | * Remove the original attr we have just replaced. This is dependent on the | |
637 | * original lookup and insert placing the old attr in args->blkno/args->index | |
638 | * and the new attr in args->blkno2/args->index2. | |
639 | */ | |
640 | static int | |
641 | xfs_attr_leaf_remove_attr( | |
e3c5de22 | 642 | struct xfs_attr_intent *attr) |
b11fa61b DC |
643 | { |
644 | struct xfs_da_args *args = attr->xattri_da_args; | |
645 | struct xfs_inode *dp = args->dp; | |
646 | struct xfs_buf *bp = NULL; | |
647 | int forkoff; | |
648 | int error; | |
649 | ||
650 | error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, | |
651 | &bp); | |
652 | if (error) | |
653 | return error; | |
654 | ||
655 | xfs_attr3_leaf_remove(bp, args); | |
656 | ||
657 | forkoff = xfs_attr_shortform_allfit(bp, dp); | |
658 | if (forkoff) | |
659 | error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); | |
660 | /* bp is gone due to xfs_da_shrink_inode */ | |
661 | ||
662 | return error; | |
663 | } | |
664 | ||
665 | /* | |
666 | * Shrink an attribute from leaf to shortform. Used by the node format remove | |
667 | * path when the node format collapses to a single block and so we have to check | |
668 | * if it can be collapsed further. | |
669 | */ | |
670 | static int | |
671 | xfs_attr_leaf_shrink( | |
59782a23 | 672 | struct xfs_da_args *args) |
b11fa61b DC |
673 | { |
674 | struct xfs_inode *dp = args->dp; | |
b11fa61b | 675 | struct xfs_buf *bp; |
59782a23 DC |
676 | int forkoff; |
677 | int error; | |
b11fa61b DC |
678 | |
679 | if (!xfs_attr_is_leaf(dp)) | |
680 | return 0; | |
681 | ||
b11fa61b DC |
682 | error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp); |
683 | if (error) | |
684 | return error; | |
685 | ||
686 | forkoff = xfs_attr_shortform_allfit(bp, dp); | |
687 | if (forkoff) { | |
688 | error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); | |
689 | /* bp is gone due to xfs_da_shrink_inode */ | |
690 | } else { | |
691 | xfs_trans_brelse(args->trans, bp); | |
692 | } | |
693 | ||
694 | return error; | |
695 | } | |
696 | ||
2f3cd809 | 697 | /* |
4e3d96a5 DC |
698 | * Run the attribute operation specified in @attr. |
699 | * | |
700 | * This routine is meant to function as a delayed operation and will set the | |
701 | * state to XFS_DAS_DONE when the operation is complete. Calling functions will | |
702 | * need to handle this, and recall the function until either an error or | |
703 | * XFS_DAS_DONE is detected. | |
2f3cd809 AH |
704 | */ |
705 | int | |
8f502a40 | 706 | xfs_attr_set_iter( |
e3c5de22 | 707 | struct xfs_attr_intent *attr) |
2f3cd809 | 708 | { |
d68c51e9 | 709 | struct xfs_da_args *args = attr->xattri_da_args; |
b11fa61b | 710 | int error = 0; |
8f502a40 AH |
711 | |
712 | /* State machine switch */ | |
251b29c8 | 713 | next_state: |
d68c51e9 | 714 | switch (attr->xattri_dela_state) { |
8f502a40 | 715 | case XFS_DAS_UNINIT: |
e0c41089 DC |
716 | ASSERT(0); |
717 | return -EFSCORRUPTED; | |
718 | case XFS_DAS_SF_ADD: | |
719 | return xfs_attr_sf_addname(attr); | |
720 | case XFS_DAS_LEAF_ADD: | |
cd1549d6 | 721 | return xfs_attr_leaf_addname(attr); |
e0c41089 DC |
722 | case XFS_DAS_NODE_ADD: |
723 | return xfs_attr_node_addname(attr); | |
3f562d09 | 724 | |
e5d5596a | 725 | case XFS_DAS_SF_REMOVE: |
fdaf1bb3 DC |
726 | error = xfs_attr_sf_removename(args); |
727 | attr->xattri_dela_state = xfs_attr_complete_op(attr, | |
728 | xfs_attr_init_add_state(args)); | |
729 | break; | |
e5d5596a | 730 | case XFS_DAS_LEAF_REMOVE: |
fdaf1bb3 DC |
731 | error = xfs_attr_leaf_removename(args); |
732 | attr->xattri_dela_state = xfs_attr_complete_op(attr, | |
733 | xfs_attr_init_add_state(args)); | |
734 | break; | |
e5d5596a DC |
735 | case XFS_DAS_NODE_REMOVE: |
736 | error = xfs_attr_node_removename_setup(attr); | |
fdaf1bb3 DC |
737 | if (error == -ENOATTR && |
738 | (args->op_flags & XFS_DA_OP_RECOVERY)) { | |
739 | attr->xattri_dela_state = xfs_attr_complete_op(attr, | |
740 | xfs_attr_init_add_state(args)); | |
741 | error = 0; | |
742 | break; | |
743 | } | |
e5d5596a DC |
744 | if (error) |
745 | return error; | |
746 | attr->xattri_dela_state = XFS_DAS_NODE_REMOVE_RMT; | |
747 | if (args->rmtblkno == 0) | |
748 | attr->xattri_dela_state++; | |
749 | break; | |
750 | ||
7d035336 DC |
751 | case XFS_DAS_LEAF_SET_RMT: |
752 | case XFS_DAS_NODE_SET_RMT: | |
753 | error = xfs_attr_rmtval_find_space(attr); | |
754 | if (error) | |
755 | return error; | |
251b29c8 | 756 | attr->xattri_dela_state++; |
2157d169 | 757 | fallthrough; |
7d035336 | 758 | |
2157d169 | 759 | case XFS_DAS_LEAF_ALLOC_RMT: |
251b29c8 | 760 | case XFS_DAS_NODE_ALLOC_RMT: |
7d035336 DC |
761 | error = xfs_attr_rmtval_alloc(attr); |
762 | if (error) | |
7c93d4a8 | 763 | return error; |
7d035336 DC |
764 | if (attr->xattri_dela_state == XFS_DAS_DONE) |
765 | break; | |
411b434a | 766 | goto next_state; |
7c93d4a8 | 767 | |
7d035336 DC |
768 | case XFS_DAS_LEAF_REPLACE: |
769 | case XFS_DAS_NODE_REPLACE: | |
83c6e707 | 770 | /* |
411b434a DC |
771 | * We must "flip" the incomplete flags on the "new" and "old" |
772 | * attribute/value pairs so that one disappears and one appears | |
2e7ef218 | 773 | * atomically. |
83c6e707 | 774 | */ |
411b434a DC |
775 | error = xfs_attr3_leaf_flipflags(args); |
776 | if (error) | |
777 | return error; | |
778 | /* | |
2e7ef218 DC |
779 | * We must commit the flag value change now to make it atomic |
780 | * and then we can start the next trans in series at REMOVE_OLD. | |
411b434a | 781 | */ |
251b29c8 | 782 | attr->xattri_dela_state++; |
411b434a DC |
783 | break; |
784 | ||
785 | case XFS_DAS_LEAF_REMOVE_OLD: | |
786 | case XFS_DAS_NODE_REMOVE_OLD: | |
83c6e707 | 787 | /* |
2e7ef218 DC |
788 | * If we have a remote attr, start the process of removing it |
789 | * by invalidating any cached buffers. | |
790 | * | |
791 | * If we don't have a remote attr, we skip the remote block | |
792 | * removal state altogether with a second state increment. | |
83c6e707 AH |
793 | */ |
794 | xfs_attr_restore_rmt_blk(args); | |
83c6e707 | 795 | if (args->rmtblkno) { |
2e7ef218 | 796 | error = xfs_attr_rmtval_invalidate(args); |
83c6e707 AH |
797 | if (error) |
798 | return error; | |
2e7ef218 DC |
799 | } else { |
800 | attr->xattri_dela_state++; | |
83c6e707 AH |
801 | } |
802 | ||
2e7ef218 DC |
803 | attr->xattri_dela_state++; |
804 | goto next_state; | |
805 | ||
806 | case XFS_DAS_LEAF_REMOVE_RMT: | |
807 | case XFS_DAS_NODE_REMOVE_RMT: | |
808 | error = xfs_attr_rmtval_remove(attr); | |
4e3d96a5 DC |
809 | if (error == -EAGAIN) { |
810 | error = 0; | |
2e7ef218 | 811 | break; |
4e3d96a5 | 812 | } |
2e7ef218 DC |
813 | if (error) |
814 | return error; | |
815 | ||
251b29c8 | 816 | /* |
2e7ef218 DC |
817 | * We've finished removing the remote attr blocks, so commit the |
818 | * transaction and move on to removing the attr name from the | |
819 | * leaf/node block. Removing the attr might require a full | |
820 | * transaction reservation for btree block freeing, so we | |
821 | * can't do that in the same transaction where we removed the | |
822 | * remote attr blocks. | |
251b29c8 DC |
823 | */ |
824 | attr->xattri_dela_state++; | |
2e7ef218 | 825 | break; |
251b29c8 | 826 | |
b11fa61b DC |
827 | case XFS_DAS_LEAF_REMOVE_ATTR: |
828 | error = xfs_attr_leaf_remove_attr(attr); | |
fdaf1bb3 DC |
829 | attr->xattri_dela_state = xfs_attr_complete_op(attr, |
830 | xfs_attr_init_add_state(args)); | |
b11fa61b | 831 | break; |
7c93d4a8 | 832 | |
b11fa61b DC |
833 | case XFS_DAS_NODE_REMOVE_ATTR: |
834 | error = xfs_attr_node_remove_attr(attr); | |
835 | if (!error) | |
59782a23 | 836 | error = xfs_attr_leaf_shrink(args); |
fdaf1bb3 DC |
837 | attr->xattri_dela_state = xfs_attr_complete_op(attr, |
838 | xfs_attr_init_add_state(args)); | |
8f502a40 AH |
839 | break; |
840 | default: | |
4a4957c1 | 841 | ASSERT(0); |
8f502a40 AH |
842 | break; |
843 | } | |
7d035336 DC |
844 | |
845 | trace_xfs_attr_set_iter_return(attr->xattri_dela_state, args->dp); | |
6ca5a4a1 | 846 | return error; |
2f3cd809 AH |
847 | } |
848 | ||
8f502a40 | 849 | |
07120f1a AC |
850 | /* |
851 | * Return EEXIST if attr is found, or ENOATTR if not | |
852 | */ | |
51b495eb DC |
853 | static int |
854 | xfs_attr_lookup( | |
07120f1a AC |
855 | struct xfs_da_args *args) |
856 | { | |
857 | struct xfs_inode *dp = args->dp; | |
858 | struct xfs_buf *bp = NULL; | |
4d0cdd2b | 859 | struct xfs_da_state *state; |
07120f1a AC |
860 | int error; |
861 | ||
862 | if (!xfs_inode_hasattr(dp)) | |
863 | return -ENOATTR; | |
864 | ||
6c8d169b CH |
865 | if (dp->i_af.if_format == XFS_DINODE_FMT_LOCAL) { |
866 | if (xfs_attr_sf_findname(args)) | |
867 | return -EEXIST; | |
868 | return -ENOATTR; | |
869 | } | |
07120f1a | 870 | |
2ac131df | 871 | if (xfs_attr_is_leaf(dp)) { |
07120f1a AC |
872 | error = xfs_attr_leaf_hasname(args, &bp); |
873 | ||
874 | if (bp) | |
875 | xfs_trans_brelse(args->trans, bp); | |
876 | ||
877 | return error; | |
878 | } | |
879 | ||
4d0cdd2b DW |
880 | state = xfs_da_state_alloc(args); |
881 | error = xfs_attr_node_lookup(args, state); | |
882 | xfs_da_state_free(state); | |
883 | return error; | |
07120f1a AC |
884 | } |
885 | ||
c00eebd0 CH |
886 | static void |
887 | xfs_attr_defer_add( | |
709c8632 | 888 | struct xfs_da_args *args, |
c00eebd0 | 889 | unsigned int op_flags) |
709c8632 DC |
890 | { |
891 | ||
e3c5de22 | 892 | struct xfs_attr_intent *new; |
709c8632 | 893 | |
0b3a76e9 DC |
894 | new = kmem_cache_zalloc(xfs_attr_intent_cache, |
895 | GFP_KERNEL | __GFP_NOFAIL); | |
709c8632 DC |
896 | new->xattri_op_flags = op_flags; |
897 | new->xattri_da_args = args; | |
898 | ||
c00eebd0 CH |
899 | switch (op_flags) { |
900 | case XFS_ATTRI_OP_FLAGS_SET: | |
901 | new->xattri_dela_state = xfs_attr_init_add_state(args); | |
902 | break; | |
903 | case XFS_ATTRI_OP_FLAGS_REPLACE: | |
904 | new->xattri_dela_state = xfs_attr_init_replace_state(args); | |
905 | break; | |
906 | case XFS_ATTRI_OP_FLAGS_REMOVE: | |
907 | new->xattri_dela_state = xfs_attr_init_remove_state(args); | |
908 | break; | |
909 | default: | |
910 | ASSERT(0); | |
911 | } | |
709c8632 | 912 | |
603ce8ab | 913 | xfs_defer_add(args->trans, &new->xattri_list, &xfs_attr_defer_type); |
709c8632 | 914 | trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp); |
709c8632 DC |
915 | } |
916 | ||
0eb81a5f | 917 | /* |
a2544622 | 918 | * Note: If args->value is NULL the attribute will be removed, just like the |
0eb81a5f CH |
919 | * Linux ->setattr API. |
920 | */ | |
c5b4ac39 CH |
921 | int |
922 | xfs_attr_set( | |
a2544622 | 923 | struct xfs_da_args *args) |
1da177e4 | 924 | { |
a2544622 | 925 | struct xfs_inode *dp = args->dp; |
3d3c8b52 JL |
926 | struct xfs_mount *mp = dp->i_mount; |
927 | struct xfs_trans_res tres; | |
d5f0f49a | 928 | bool rsvd = (args->attr_filter & XFS_ATTR_ROOT); |
4c74a56b | 929 | int error, local; |
3a19bb14 | 930 | int rmt_blks = 0; |
0eb81a5f | 931 | unsigned int total; |
c5b4ac39 | 932 | |
75c8c50f | 933 | if (xfs_is_shutdown(dp->i_mount)) |
2451337d | 934 | return -EIO; |
c5b4ac39 | 935 | |
0eb81a5f CH |
936 | error = xfs_qm_dqattach(dp); |
937 | if (error) | |
938 | return error; | |
939 | ||
a2544622 CH |
940 | args->geo = mp->m_attr_geo; |
941 | args->whichfork = XFS_ATTR_FORK; | |
942 | args->hashval = xfs_da_hashname(args->name, args->namelen); | |
1da177e4 LT |
943 | |
944 | /* | |
0eb81a5f CH |
945 | * We have no control over the attribute names that userspace passes us |
946 | * to remove, so we have to allow the name lookup prior to attribute | |
f4288f01 DW |
947 | * removal to fail as well. Preserve the logged flag, since we need |
948 | * to pass that through to the logging code. | |
1da177e4 | 949 | */ |
f4288f01 DW |
950 | args->op_flags = XFS_DA_OP_OKNOENT | |
951 | (args->op_flags & XFS_DA_OP_LOGGED); | |
e5889e90 | 952 | |
a2544622 | 953 | if (args->value) { |
0eb81a5f | 954 | XFS_STATS_INC(mp, xs_attr_set); |
a2544622 | 955 | args->total = xfs_attr_calc_size(args, &local); |
0eb81a5f CH |
956 | |
957 | /* | |
958 | * If the inode doesn't have an attribute fork, add one. | |
959 | * (inode must not be locked when we call this routine) | |
960 | */ | |
932b42c6 | 961 | if (xfs_inode_has_attr_fork(dp) == 0) { |
0eb81a5f | 962 | int sf_size = sizeof(struct xfs_attr_sf_hdr) + |
e01b7eed | 963 | xfs_attr_sf_entsize_byname(args->namelen, |
a2544622 | 964 | args->valuelen); |
0eb81a5f CH |
965 | |
966 | error = xfs_bmap_add_attrfork(dp, sf_size, rsvd); | |
967 | if (error) | |
968 | return error; | |
969 | } | |
970 | ||
3a19bb14 CB |
971 | if (!local) |
972 | rmt_blks = xfs_attr3_rmt_blocks(mp, args->valuelen); | |
0eb81a5f CH |
973 | } else { |
974 | XFS_STATS_INC(mp, xs_attr_remove); | |
3a19bb14 | 975 | rmt_blks = xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX); |
0eb81a5f | 976 | } |
1da177e4 LT |
977 | |
978 | /* | |
979 | * Root fork attributes can use reserved data blocks for this | |
980 | * operation if necessary | |
981 | */ | |
c3546cf5 | 982 | xfs_init_attr_trans(args, &tres, &total); |
3de4eb10 | 983 | error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans); |
253f4911 | 984 | if (error) |
efc2efeb | 985 | return error; |
1da177e4 | 986 | |
3a19bb14 CB |
987 | if (args->value || xfs_inode_hasattr(dp)) { |
988 | error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK, | |
989 | XFS_IEXT_ATTR_MANIP_CNT(rmt_blks)); | |
4f86bb4b CB |
990 | if (error == -EFBIG) |
991 | error = xfs_iext_count_upgrade(args->trans, dp, | |
992 | XFS_IEXT_ATTR_MANIP_CNT(rmt_blks)); | |
3a19bb14 CB |
993 | if (error) |
994 | goto out_trans_cancel; | |
995 | } | |
996 | ||
51b495eb | 997 | error = xfs_attr_lookup(args); |
709c8632 DC |
998 | switch (error) { |
999 | case -EEXIST: | |
709c8632 | 1000 | if (!args->value) { |
c00eebd0 CH |
1001 | /* if no value, we are performing a remove operation */ |
1002 | xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_REMOVE); | |
709c8632 DC |
1003 | break; |
1004 | } | |
c00eebd0 | 1005 | |
709c8632 DC |
1006 | /* Pure create fails if the attr already exists */ |
1007 | if (args->attr_flags & XATTR_CREATE) | |
deed9512 | 1008 | goto out_trans_cancel; |
c00eebd0 | 1009 | xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_REPLACE); |
709c8632 DC |
1010 | break; |
1011 | case -ENOATTR: | |
1012 | /* Can't remove what isn't there. */ | |
1013 | if (!args->value) | |
0eb81a5f | 1014 | goto out_trans_cancel; |
f3f36c89 | 1015 | |
709c8632 DC |
1016 | /* Pure replace fails if no existing attr to replace. */ |
1017 | if (args->attr_flags & XATTR_REPLACE) | |
deed9512 | 1018 | goto out_trans_cancel; |
c00eebd0 | 1019 | xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_SET); |
709c8632 DC |
1020 | break; |
1021 | default: | |
1022 | goto out_trans_cancel; | |
2f3cd809 | 1023 | } |
1da177e4 LT |
1024 | |
1025 | /* | |
1026 | * If this is a synchronous mount, make sure that the | |
1027 | * transaction goes to disk before returning to the user. | |
1028 | */ | |
0560f31a | 1029 | if (xfs_has_wsync(mp)) |
a2544622 | 1030 | xfs_trans_set_sync(args->trans); |
1da177e4 | 1031 | |
1d733019 | 1032 | if (!(args->op_flags & XFS_DA_OP_NOTIME)) |
a2544622 | 1033 | xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG); |
dcd79a14 | 1034 | |
1da177e4 LT |
1035 | /* |
1036 | * Commit the last in the sequence of transactions. | |
1037 | */ | |
a2544622 CH |
1038 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); |
1039 | error = xfs_trans_commit(args->trans); | |
2f3cd809 | 1040 | out_unlock: |
1da177e4 | 1041 | xfs_iunlock(dp, XFS_ILOCK_EXCL); |
c5b4ac39 | 1042 | return error; |
1da177e4 | 1043 | |
2f3cd809 | 1044 | out_trans_cancel: |
a2544622 CH |
1045 | if (args->trans) |
1046 | xfs_trans_cancel(args->trans); | |
2f3cd809 | 1047 | goto out_unlock; |
1da177e4 LT |
1048 | } |
1049 | ||
1da177e4 LT |
1050 | /*======================================================================== |
1051 | * External routines when attribute list is inside the inode | |
1052 | *========================================================================*/ | |
1053 | ||
e01b7eed CM |
1054 | static inline int xfs_attr_sf_totsize(struct xfs_inode *dp) |
1055 | { | |
41414722 | 1056 | struct xfs_attr_sf_hdr *sf = dp->i_af.if_data; |
e01b7eed | 1057 | |
41414722 | 1058 | return be16_to_cpu(sf->totsize); |
e01b7eed CM |
1059 | } |
1060 | ||
1da177e4 LT |
1061 | /* |
1062 | * Add a name to the shortform attribute list structure | |
1063 | * This is the external routine. | |
1064 | */ | |
e7f358de DC |
1065 | static int |
1066 | xfs_attr_shortform_addname( | |
1067 | struct xfs_da_args *args) | |
1da177e4 | 1068 | { |
e7f358de | 1069 | int newsize, forkoff; |
1da177e4 | 1070 | |
5a5881cd DC |
1071 | trace_xfs_attr_sf_addname(args); |
1072 | ||
22b7b1f5 CH |
1073 | if (xfs_attr_sf_findname(args)) { |
1074 | int error; | |
1075 | ||
378b6aef | 1076 | ASSERT(args->op_flags & XFS_DA_OP_REPLACE); |
e7f358de DC |
1077 | |
1078 | error = xfs_attr_sf_removename(args); | |
1079 | if (error) | |
1080 | return error; | |
1081 | ||
7b38460d | 1082 | /* |
e7f358de DC |
1083 | * Since we have removed the old attr, clear XFS_DA_OP_REPLACE |
1084 | * so that the new attr doesn't fit in shortform format, the | |
1085 | * leaf format add routine won't trip over the attr not being | |
1086 | * around. | |
7b38460d | 1087 | */ |
e7f358de | 1088 | args->op_flags &= ~XFS_DA_OP_REPLACE; |
22b7b1f5 | 1089 | } else { |
378b6aef | 1090 | ASSERT(!(args->op_flags & XFS_DA_OP_REPLACE)); |
1da177e4 LT |
1091 | } |
1092 | ||
d8cc890d NS |
1093 | if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX || |
1094 | args->valuelen >= XFS_ATTR_SF_ENTSIZE_MAX) | |
2451337d | 1095 | return -ENOSPC; |
d8cc890d | 1096 | |
e01b7eed CM |
1097 | newsize = xfs_attr_sf_totsize(args->dp); |
1098 | newsize += xfs_attr_sf_entsize_byname(args->namelen, args->valuelen); | |
d8cc890d NS |
1099 | |
1100 | forkoff = xfs_attr_shortform_bytesfit(args->dp, newsize); | |
1101 | if (!forkoff) | |
2451337d | 1102 | return -ENOSPC; |
d8cc890d NS |
1103 | |
1104 | xfs_attr_shortform_add(args, forkoff); | |
d99831ff | 1105 | return 0; |
1da177e4 LT |
1106 | } |
1107 | ||
1108 | ||
1109 | /*======================================================================== | |
1110 | * External routines when attribute list is one block | |
1111 | *========================================================================*/ | |
1112 | ||
e7f358de DC |
1113 | /* Save the current remote block info and clear the current pointers. */ |
1114 | static void | |
410c1988 AC |
1115 | xfs_attr_save_rmt_blk( |
1116 | struct xfs_da_args *args) | |
1117 | { | |
1118 | args->blkno2 = args->blkno; | |
1119 | args->index2 = args->index; | |
1120 | args->rmtblkno2 = args->rmtblkno; | |
1121 | args->rmtblkcnt2 = args->rmtblkcnt; | |
1122 | args->rmtvaluelen2 = args->rmtvaluelen; | |
e7f358de DC |
1123 | args->rmtblkno = 0; |
1124 | args->rmtblkcnt = 0; | |
1125 | args->rmtvaluelen = 0; | |
410c1988 AC |
1126 | } |
1127 | ||
1128 | /* Set stored info about a remote block */ | |
e7f358de | 1129 | static void |
410c1988 AC |
1130 | xfs_attr_restore_rmt_blk( |
1131 | struct xfs_da_args *args) | |
1132 | { | |
1133 | args->blkno = args->blkno2; | |
1134 | args->index = args->index2; | |
1135 | args->rmtblkno = args->rmtblkno2; | |
1136 | args->rmtblkcnt = args->rmtblkcnt2; | |
1137 | args->rmtvaluelen = args->rmtvaluelen2; | |
1138 | } | |
1139 | ||
1da177e4 | 1140 | /* |
7c93d4a8 | 1141 | * Tries to add an attribute to an inode in leaf form |
1da177e4 | 1142 | * |
7c93d4a8 AC |
1143 | * This function is meant to execute as part of a delayed operation and leaves |
1144 | * the transaction handling to the caller. On success the attribute is added | |
1145 | * and the inode and transaction are left dirty. If there is not enough space, | |
1146 | * the attr data is converted to node format and -ENOSPC is returned. Caller is | |
1147 | * responsible for handling the dirty inode and transaction or adding the attr | |
1148 | * in node format. | |
1da177e4 | 1149 | */ |
a8272ce0 | 1150 | STATIC int |
7c93d4a8 | 1151 | xfs_attr_leaf_try_add( |
e53bcffa | 1152 | struct xfs_da_args *args) |
1da177e4 | 1153 | { |
e53bcffa | 1154 | struct xfs_buf *bp; |
e22b88de | 1155 | int error; |
5a5881cd | 1156 | |
e53bcffa DW |
1157 | error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp); |
1158 | if (error) | |
1159 | return error; | |
e22b88de DC |
1160 | |
1161 | /* | |
1162 | * Look up the xattr name to set the insertion point for the new xattr. | |
1163 | */ | |
1164 | error = xfs_attr3_leaf_lookup_int(bp, args); | |
e7f358de DC |
1165 | switch (error) { |
1166 | case -ENOATTR: | |
1167 | if (args->op_flags & XFS_DA_OP_REPLACE) | |
1168 | goto out_brelse; | |
1169 | break; | |
1170 | case -EEXIST: | |
1171 | if (!(args->op_flags & XFS_DA_OP_REPLACE)) | |
f3e93d95 | 1172 | goto out_brelse; |
5a5881cd DC |
1173 | |
1174 | trace_xfs_attr_leaf_replace(args); | |
8275cdd0 | 1175 | /* |
e7f358de DC |
1176 | * Save the existing remote attr state so that the current |
1177 | * values reflect the state of the new attribute we are about to | |
8275cdd0 DC |
1178 | * add, not the attribute we just found and will remove later. |
1179 | */ | |
e7f358de DC |
1180 | xfs_attr_save_rmt_blk(args); |
1181 | break; | |
1182 | case 0: | |
1183 | break; | |
1184 | default: | |
1185 | goto out_brelse; | |
1da177e4 LT |
1186 | } |
1187 | ||
0f89edcd AC |
1188 | return xfs_attr3_leaf_add(bp, args); |
1189 | ||
7c93d4a8 AC |
1190 | out_brelse: |
1191 | xfs_trans_brelse(args->trans, bp); | |
e22b88de | 1192 | return error; |
7c93d4a8 | 1193 | } |
1da177e4 | 1194 | |
07120f1a AC |
1195 | /* |
1196 | * Return EEXIST if attr is found, or ENOATTR if not | |
1197 | */ | |
1198 | STATIC int | |
1199 | xfs_attr_leaf_hasname( | |
1200 | struct xfs_da_args *args, | |
1201 | struct xfs_buf **bp) | |
1202 | { | |
1203 | int error = 0; | |
1204 | ||
1205 | error = xfs_attr3_leaf_read(args->trans, args->dp, 0, bp); | |
1206 | if (error) | |
1207 | return error; | |
1208 | ||
1209 | error = xfs_attr3_leaf_lookup_int(*bp, args); | |
1210 | if (error != -ENOATTR && error != -EEXIST) | |
1211 | xfs_trans_brelse(args->trans, *bp); | |
1212 | ||
1213 | return error; | |
1214 | } | |
1215 | ||
1da177e4 LT |
1216 | /* |
1217 | * Remove a name from the leaf attribute list structure | |
1218 | * | |
1219 | * This leaf block cannot have a "remote" value, we only call this routine | |
1220 | * if bmap_one_block() says there is only one block (ie: no remote blks). | |
1221 | */ | |
1222 | STATIC int | |
32a9b7c6 BF |
1223 | xfs_attr_leaf_removename( |
1224 | struct xfs_da_args *args) | |
1da177e4 | 1225 | { |
32a9b7c6 BF |
1226 | struct xfs_inode *dp; |
1227 | struct xfs_buf *bp; | |
1228 | int error, forkoff; | |
1da177e4 | 1229 | |
5a5881cd DC |
1230 | trace_xfs_attr_leaf_removename(args); |
1231 | ||
1da177e4 LT |
1232 | /* |
1233 | * Remove the attribute. | |
1234 | */ | |
1235 | dp = args->dp; | |
1da177e4 | 1236 | |
07120f1a | 1237 | error = xfs_attr_leaf_hasname(args, &bp); |
2451337d | 1238 | if (error == -ENOATTR) { |
1d9025e5 | 1239 | xfs_trans_brelse(args->trans, bp); |
fdaf1bb3 DC |
1240 | if (args->op_flags & XFS_DA_OP_RECOVERY) |
1241 | return 0; | |
517c2220 | 1242 | return error; |
07120f1a AC |
1243 | } else if (error != -EEXIST) |
1244 | return error; | |
1da177e4 | 1245 | |
517c2220 | 1246 | xfs_attr3_leaf_remove(bp, args); |
1da177e4 LT |
1247 | |
1248 | /* | |
1249 | * If the result is small enough, shrink it all into the inode. | |
1250 | */ | |
0feaef17 AC |
1251 | forkoff = xfs_attr_shortform_allfit(bp, dp); |
1252 | if (forkoff) | |
1253 | return xfs_attr3_leaf_to_shortform(bp, args, forkoff); | |
1da177e4 | 1254 | /* bp is gone due to xfs_da_shrink_inode */ |
0feaef17 | 1255 | |
517c2220 | 1256 | return 0; |
1da177e4 LT |
1257 | } |
1258 | ||
1259 | /* | |
1260 | * Look up a name in a leaf attribute list structure. | |
1261 | * | |
1262 | * This leaf block cannot have a "remote" value, we only call this routine | |
1263 | * if bmap_one_block() says there is only one block (ie: no remote blks). | |
728bcaa3 DC |
1264 | * |
1265 | * Returns 0 on successful retrieval, otherwise an error. | |
1da177e4 | 1266 | */ |
ba0f32d4 | 1267 | STATIC int |
1da177e4 LT |
1268 | xfs_attr_leaf_get(xfs_da_args_t *args) |
1269 | { | |
1d9025e5 | 1270 | struct xfs_buf *bp; |
1da177e4 LT |
1271 | int error; |
1272 | ||
ee73259b DC |
1273 | trace_xfs_attr_leaf_get(args); |
1274 | ||
07120f1a | 1275 | error = xfs_attr_leaf_hasname(args, &bp); |
1da177e4 | 1276 | |
07120f1a | 1277 | if (error == -ENOATTR) { |
1d9025e5 | 1278 | xfs_trans_brelse(args->trans, bp); |
517c2220 | 1279 | return error; |
07120f1a AC |
1280 | } else if (error != -EEXIST) |
1281 | return error; | |
1282 | ||
1283 | ||
517c2220 | 1284 | error = xfs_attr3_leaf_getvalue(bp, args); |
1d9025e5 | 1285 | xfs_trans_brelse(args->trans, bp); |
e3cc4554 | 1286 | return error; |
1da177e4 LT |
1287 | } |
1288 | ||
4d0cdd2b | 1289 | /* Return EEXIST if attr is found, or ENOATTR if not. */ |
07120f1a | 1290 | STATIC int |
4d0cdd2b | 1291 | xfs_attr_node_lookup( |
07120f1a | 1292 | struct xfs_da_args *args, |
4d0cdd2b | 1293 | struct xfs_da_state *state) |
07120f1a | 1294 | { |
07120f1a AC |
1295 | int retval, error; |
1296 | ||
07120f1a AC |
1297 | /* |
1298 | * Search to see if name exists, and get back a pointer to it. | |
1299 | */ | |
1300 | error = xfs_da3_node_lookup_int(state, &retval); | |
a1de97fe | 1301 | if (error) |
4d0cdd2b | 1302 | return error; |
a1de97fe | 1303 | |
07120f1a AC |
1304 | return retval; |
1305 | } | |
1306 | ||
1da177e4 | 1307 | /*======================================================================== |
c2c4c477 | 1308 | * External routines when attribute list size > geo->blksize |
1da177e4 LT |
1309 | *========================================================================*/ |
1310 | ||
1da177e4 | 1311 | STATIC int |
6ca5a4a1 | 1312 | xfs_attr_node_addname_find_attr( |
e3c5de22 | 1313 | struct xfs_attr_intent *attr) |
1da177e4 | 1314 | { |
e7f358de DC |
1315 | struct xfs_da_args *args = attr->xattri_da_args; |
1316 | int error; | |
5a5881cd | 1317 | |
1da177e4 LT |
1318 | /* |
1319 | * Search to see if name already exists, and get back a pointer | |
1320 | * to where it should go. | |
1321 | */ | |
4d0cdd2b DW |
1322 | xfs_attr_item_init_da_state(attr); |
1323 | error = xfs_attr_node_lookup(args, attr->xattri_da_state); | |
e7f358de DC |
1324 | switch (error) { |
1325 | case -ENOATTR: | |
1326 | if (args->op_flags & XFS_DA_OP_REPLACE) | |
1327 | goto error; | |
1328 | break; | |
1329 | case -EEXIST: | |
1330 | if (!(args->op_flags & XFS_DA_OP_REPLACE)) | |
6ca5a4a1 | 1331 | goto error; |
5a5881cd | 1332 | |
8275cdd0 | 1333 | |
e7f358de | 1334 | trace_xfs_attr_node_replace(args); |
8275cdd0 | 1335 | /* |
e7f358de DC |
1336 | * Save the existing remote attr state so that the current |
1337 | * values reflect the state of the new attribute we are about to | |
8275cdd0 DC |
1338 | * add, not the attribute we just found and will remove later. |
1339 | */ | |
e7f358de DC |
1340 | xfs_attr_save_rmt_blk(args); |
1341 | break; | |
1342 | case 0: | |
1343 | break; | |
1344 | default: | |
1345 | goto error; | |
1da177e4 LT |
1346 | } |
1347 | ||
6ca5a4a1 AH |
1348 | return 0; |
1349 | error: | |
309001c2 | 1350 | if (attr->xattri_da_state) { |
d68c51e9 | 1351 | xfs_da_state_free(attr->xattri_da_state); |
309001c2 DW |
1352 | attr->xattri_da_state = NULL; |
1353 | } | |
e7f358de | 1354 | return error; |
6ca5a4a1 AH |
1355 | } |
1356 | ||
1357 | /* | |
1358 | * Add a name to a Btree-format attribute list. | |
1359 | * | |
1360 | * This will involve walking down the Btree, and may involve splitting | |
1361 | * leaf nodes and even splitting intermediate nodes up to and including | |
1362 | * the root node (a special case of an intermediate node). | |
6ca5a4a1 | 1363 | */ |
e0c41089 DC |
1364 | static int |
1365 | xfs_attr_node_try_addname( | |
e3c5de22 | 1366 | struct xfs_attr_intent *attr) |
6ca5a4a1 | 1367 | { |
d68c51e9 | 1368 | struct xfs_da_state *state = attr->xattri_da_state; |
8f502a40 AH |
1369 | struct xfs_da_state_blk *blk; |
1370 | int error; | |
6ca5a4a1 | 1371 | |
10930b25 | 1372 | trace_xfs_attr_node_addname(state->args); |
6ca5a4a1 | 1373 | |
6ca5a4a1 AH |
1374 | blk = &state->path.blk[state->path.active-1]; |
1375 | ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); | |
1376 | ||
5d954cc0 AH |
1377 | error = xfs_attr3_leaf_add(blk->bp, state->args); |
1378 | if (error == -ENOSPC) { | |
1da177e4 LT |
1379 | if (state->path.active == 1) { |
1380 | /* | |
1381 | * Its really a single leaf node, but it had | |
1382 | * out-of-line values so it looked like it *might* | |
4e3d96a5 | 1383 | * have been a b-tree. Let the caller deal with this. |
1da177e4 | 1384 | */ |
4e3d96a5 | 1385 | goto out; |
1da177e4 LT |
1386 | } |
1387 | ||
1388 | /* | |
1389 | * Split as many Btree elements as required. | |
1390 | * This code tracks the new and old attr's location | |
1391 | * in the index/blkno/rmtblkno/rmtblkcnt fields and | |
1392 | * in the index2/blkno2/rmtblkno2/rmtblkcnt2 fields. | |
1393 | */ | |
f5ea1100 | 1394 | error = xfs_da3_split(state); |
8ad7c629 | 1395 | if (error) |
d5a2e289 | 1396 | goto out; |
1da177e4 LT |
1397 | } else { |
1398 | /* | |
1399 | * Addition succeeded, update Btree hashvals. | |
1400 | */ | |
f5ea1100 | 1401 | xfs_da3_fixhashpath(state, &state->path); |
1da177e4 LT |
1402 | } |
1403 | ||
f0f7c502 | 1404 | out: |
4e3d96a5 | 1405 | xfs_da_state_free(state); |
309001c2 | 1406 | attr->xattri_da_state = NULL; |
5d954cc0 | 1407 | return error; |
f0f7c502 AH |
1408 | } |
1409 | ||
59782a23 DC |
1410 | static int |
1411 | xfs_attr_node_removename( | |
1412 | struct xfs_da_args *args, | |
1413 | struct xfs_da_state *state) | |
1414 | { | |
1415 | struct xfs_da_state_blk *blk; | |
1416 | int retval; | |
1417 | ||
1418 | /* | |
1419 | * Remove the name and update the hashvals in the tree. | |
1420 | */ | |
1421 | blk = &state->path.blk[state->path.active-1]; | |
1422 | ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); | |
1423 | retval = xfs_attr3_leaf_remove(blk->bp, args); | |
1424 | xfs_da3_fixhashpath(state, &state->path); | |
1425 | ||
1426 | return retval; | |
1427 | } | |
f0f7c502 | 1428 | |
b11fa61b DC |
1429 | static int |
1430 | xfs_attr_node_remove_attr( | |
e3c5de22 | 1431 | struct xfs_attr_intent *attr) |
f0f7c502 | 1432 | { |
d68c51e9 | 1433 | struct xfs_da_args *args = attr->xattri_da_args; |
3768f698 | 1434 | struct xfs_da_state *state = xfs_da_state_alloc(args); |
f0f7c502 AH |
1435 | int retval = 0; |
1436 | int error = 0; | |
1437 | ||
bf4a5cff | 1438 | /* |
4b9879b1 DC |
1439 | * The attr we are removing has already been marked incomplete, so |
1440 | * we need to set the filter appropriately to re-find the "old" | |
1441 | * attribute entry after any split ops. | |
bf4a5cff | 1442 | */ |
4b9879b1 | 1443 | args->attr_filter |= XFS_ATTR_INCOMPLETE; |
bf4a5cff AC |
1444 | error = xfs_da3_node_lookup_int(state, &retval); |
1445 | if (error) | |
1446 | goto out; | |
1da177e4 | 1447 | |
816c8e39 | 1448 | error = xfs_attr_node_removename(args, state); |
1da177e4 | 1449 | |
bf4a5cff AC |
1450 | /* |
1451 | * Check to see if the tree needs to be collapsed. | |
1452 | */ | |
1453 | if (retval && (state->path.active > 1)) { | |
1454 | error = xfs_da3_join(state); | |
1da177e4 LT |
1455 | if (error) |
1456 | goto out; | |
1457 | } | |
1458 | retval = error = 0; | |
1459 | ||
1460 | out: | |
3768f698 | 1461 | xfs_da_state_free(state); |
1da177e4 | 1462 | if (error) |
d99831ff ES |
1463 | return error; |
1464 | return retval; | |
1da177e4 LT |
1465 | } |
1466 | ||
1da177e4 | 1467 | /* |
728bcaa3 | 1468 | * Retrieve the attribute data from a node attribute list. |
1da177e4 LT |
1469 | * |
1470 | * This routine gets called for any attribute fork that has more than one | |
1471 | * block, ie: both true Btree attr lists and for single-leaf-blocks with | |
1472 | * "remote" values taking up more blocks. | |
728bcaa3 DC |
1473 | * |
1474 | * Returns 0 on successful retrieval, otherwise an error. | |
1da177e4 | 1475 | */ |
ba0f32d4 | 1476 | STATIC int |
07120f1a AC |
1477 | xfs_attr_node_get( |
1478 | struct xfs_da_args *args) | |
1da177e4 | 1479 | { |
4d0cdd2b | 1480 | struct xfs_da_state *state; |
07120f1a AC |
1481 | struct xfs_da_state_blk *blk; |
1482 | int i; | |
1483 | int error; | |
1da177e4 | 1484 | |
ee73259b DC |
1485 | trace_xfs_attr_node_get(args); |
1486 | ||
1da177e4 LT |
1487 | /* |
1488 | * Search to see if name exists, and get back a pointer to it. | |
1489 | */ | |
4d0cdd2b DW |
1490 | state = xfs_da_state_alloc(args); |
1491 | error = xfs_attr_node_lookup(args, state); | |
07120f1a | 1492 | if (error != -EEXIST) |
728bcaa3 DC |
1493 | goto out_release; |
1494 | ||
1495 | /* | |
1496 | * Get the value, local or "remote" | |
1497 | */ | |
1498 | blk = &state->path.blk[state->path.active - 1]; | |
07120f1a | 1499 | error = xfs_attr3_leaf_getvalue(blk->bp, args); |
1da177e4 LT |
1500 | |
1501 | /* | |
1502 | * If not in a transaction, we have to release all the buffers. | |
1503 | */ | |
728bcaa3 | 1504 | out_release: |
0f38063d | 1505 | for (i = 0; i < state->path.active; i++) { |
1d9025e5 | 1506 | xfs_trans_brelse(args->trans, state->path.blk[i].bp); |
1da177e4 LT |
1507 | state->path.blk[i].bp = NULL; |
1508 | } | |
1509 | ||
4d0cdd2b | 1510 | xfs_da_state_free(state); |
07120f1a | 1511 | return error; |
1da177e4 | 1512 | } |
65480536 DW |
1513 | |
1514 | /* Returns true if the attribute entry name is valid. */ | |
1515 | bool | |
1516 | xfs_attr_namecheck( | |
1517 | const void *name, | |
1518 | size_t length) | |
1519 | { | |
1520 | /* | |
1521 | * MAXNAMELEN includes the trailing null, but (name/length) leave it | |
1522 | * out, so use >= for the length check. | |
1523 | */ | |
1524 | if (length >= MAXNAMELEN) | |
1525 | return false; | |
1526 | ||
1527 | /* There shouldn't be any nulls here */ | |
1528 | return !memchr(name, 0, length); | |
1529 | } | |
e2c78949 DW |
1530 | |
1531 | int __init | |
1532 | xfs_attr_intent_init_cache(void) | |
1533 | { | |
e3c5de22 DW |
1534 | xfs_attr_intent_cache = kmem_cache_create("xfs_attr_intent", |
1535 | sizeof(struct xfs_attr_intent), | |
e2c78949 DW |
1536 | 0, 0, NULL); |
1537 | ||
1538 | return xfs_attr_intent_cache != NULL ? 0 : -ENOMEM; | |
1539 | } | |
1540 | ||
1541 | void | |
1542 | xfs_attr_intent_destroy_cache(void) | |
1543 | { | |
1544 | kmem_cache_destroy(xfs_attr_intent_cache); | |
1545 | xfs_attr_intent_cache = NULL; | |
1546 | } |