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