]>
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 NS |
27 | |
28 | /* | |
29 | * xfs_attr.c | |
30 | * | |
31 | * Provide the external interfaces to manage attribute lists. | |
32 | */ | |
33 | ||
34 | /*======================================================================== | |
35 | * Function prototypes for the kernel. | |
36 | *========================================================================*/ | |
37 | ||
38 | /* | |
39 | * Internal routines when attribute list fits inside the inode. | |
40 | */ | |
41 | STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args); | |
42 | ||
43 | /* | |
44 | * Internal routines when attribute list is one block. | |
45 | */ | |
46 | STATIC int xfs_attr_leaf_get(xfs_da_args_t *args); | |
47 | STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args); | |
48 | STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args); | |
57c9fccb NS |
49 | |
50 | /* | |
51 | * Internal routines when attribute list is more than one block. | |
52 | */ | |
53 | STATIC int xfs_attr_node_get(xfs_da_args_t *args); | |
54 | STATIC int xfs_attr_node_addname(xfs_da_args_t *args); | |
55 | STATIC int xfs_attr_node_removename(xfs_da_args_t *args); | |
57c9fccb NS |
56 | STATIC int xfs_attr_fillstate(xfs_da_state_t *state); |
57 | STATIC int xfs_attr_refillstate(xfs_da_state_t *state); | |
58 | ||
78d6585c | 59 | int |
5e656dbb BN |
60 | xfs_inode_hasattr( |
61 | struct xfs_inode *ip) | |
62 | { | |
63 | if (!XFS_IFORK_Q(ip) || | |
d967a68d | 64 | (ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS && |
87c472b7 | 65 | ip->i_afp->if_nextents == 0)) |
5e656dbb BN |
66 | return 0; |
67 | return 1; | |
68 | } | |
69 | ||
57c9fccb NS |
70 | /*======================================================================== |
71 | * Overall external interface routines. | |
72 | *========================================================================*/ | |
73 | ||
42a383ab DC |
74 | /* |
75 | * Retrieve an extended attribute and its value. Must have ilock. | |
76 | * Returns 0 on successful retrieval, otherwise an error. | |
77 | */ | |
b2d5ffd5 DW |
78 | int |
79 | xfs_attr_get_ilocked( | |
b2d5ffd5 DW |
80 | struct xfs_da_args *args) |
81 | { | |
7e66363b | 82 | ASSERT(xfs_isilocked(args->dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)); |
baebed82 | 83 | |
7e66363b | 84 | if (!xfs_inode_hasattr(args->dp)) |
b2d5ffd5 | 85 | return -ENOATTR; |
7e66363b | 86 | |
d967a68d | 87 | if (args->dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) |
b2d5ffd5 | 88 | return xfs_attr_shortform_getvalue(args); |
7e66363b | 89 | if (xfs_bmap_one_block(args->dp, XFS_ATTR_FORK)) |
b2d5ffd5 | 90 | return xfs_attr_leaf_get(args); |
7e66363b | 91 | return xfs_attr_node_get(args); |
b2d5ffd5 DW |
92 | } |
93 | ||
b4b9ad30 DC |
94 | /* |
95 | * Retrieve an extended attribute by name, and its value if requested. | |
96 | * | |
c1a80a3b CH |
97 | * If args->valuelen is zero, then the caller does not want the value, just an |
98 | * indication whether the attribute exists and the size of the value if it | |
99 | * exists. The size is returned in args.valuelen. | |
b4b9ad30 | 100 | * |
8be2ffd0 CH |
101 | * If args->value is NULL but args->valuelen is non-zero, allocate the buffer |
102 | * for the value after existence of the attribute has been determined. The | |
103 | * caller always has to free args->value if it is set, no matter if this | |
104 | * function was successful or not. | |
105 | * | |
b4b9ad30 | 106 | * If the attribute is found, but exceeds the size limit set by the caller in |
5a137356 CH |
107 | * args->valuelen, return -ERANGE with the size of the attribute that was found |
108 | * in args->valuelen. | |
b4b9ad30 | 109 | */ |
ff105f75 DC |
110 | int |
111 | xfs_attr_get( | |
5a137356 | 112 | struct xfs_da_args *args) |
5e656dbb | 113 | { |
ff105f75 DC |
114 | uint lock_mode; |
115 | int error; | |
116 | ||
5a137356 | 117 | XFS_STATS_INC(args->dp->i_mount, xs_attr_get); |
ff105f75 | 118 | |
5a137356 | 119 | if (XFS_FORCED_SHUTDOWN(args->dp->i_mount)) |
12b53197 | 120 | return -EIO; |
5e656dbb | 121 | |
5a137356 CH |
122 | args->geo = args->dp->i_mount->m_attr_geo; |
123 | args->whichfork = XFS_ATTR_FORK; | |
124 | args->hashval = xfs_da_hashname(args->name, args->namelen); | |
ff105f75 | 125 | |
cd9f2a5d | 126 | /* Entirely possible to look up a name which doesn't exist */ |
5a137356 | 127 | args->op_flags = XFS_DA_OP_OKNOENT; |
5e656dbb | 128 | |
5a137356 | 129 | lock_mode = xfs_ilock_attr_map_shared(args->dp); |
7e66363b | 130 | error = xfs_attr_get_ilocked(args); |
5a137356 | 131 | xfs_iunlock(args->dp, lock_mode); |
b4b9ad30 | 132 | |
5a137356 | 133 | return error; |
5e656dbb BN |
134 | } |
135 | ||
136 | /* | |
137 | * Calculate how many blocks we need for the new attribute, | |
138 | */ | |
56b2de80 | 139 | STATIC int |
5e656dbb | 140 | xfs_attr_calc_size( |
ff105f75 | 141 | struct xfs_da_args *args, |
5e656dbb BN |
142 | int *local) |
143 | { | |
ff105f75 | 144 | struct xfs_mount *mp = args->dp->i_mount; |
5e656dbb BN |
145 | int size; |
146 | int nblks; | |
147 | ||
148 | /* | |
149 | * Determine space new attribute will use, and if it would be | |
150 | * "local" or "remote" (note: local != inline). | |
151 | */ | |
ff105f75 | 152 | size = xfs_attr_leaf_newentsize(args, local); |
5e656dbb BN |
153 | nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK); |
154 | if (*local) { | |
ff105f75 | 155 | if (size > (args->geo->blksize / 2)) { |
5e656dbb BN |
156 | /* Double split possible */ |
157 | nblks *= 2; | |
158 | } | |
159 | } else { | |
160 | /* | |
161 | * Out of line attribute, cannot double split, but | |
162 | * make room for the attribute value itself. | |
163 | */ | |
ff105f75 | 164 | uint dblocks = xfs_attr3_rmt_blocks(mp, args->valuelen); |
5e656dbb BN |
165 | nblks += dblocks; |
166 | nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK); | |
167 | } | |
168 | ||
169 | return nblks; | |
170 | } | |
171 | ||
6db48a68 AH |
172 | STATIC int |
173 | xfs_attr_try_sf_addname( | |
174 | struct xfs_inode *dp, | |
175 | struct xfs_da_args *args) | |
176 | { | |
177 | ||
178 | struct xfs_mount *mp = dp->i_mount; | |
179 | int error, error2; | |
180 | ||
181 | error = xfs_attr_shortform_addname(args); | |
182 | if (error == -ENOSPC) | |
183 | return error; | |
184 | ||
185 | /* | |
186 | * Commit the shortform mods, and we're done. | |
187 | * NOTE: this is also the error path (EEXIST, etc). | |
188 | */ | |
69e818c2 | 189 | if (!error && !(args->op_flags & XFS_DA_OP_NOTIME)) |
6db48a68 AH |
190 | xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG); |
191 | ||
192 | if (mp->m_flags & XFS_MOUNT_WSYNC) | |
193 | xfs_trans_set_sync(args->trans); | |
194 | ||
195 | error2 = xfs_trans_commit(args->trans); | |
932b563e | 196 | args->trans = NULL; |
6db48a68 AH |
197 | return error ? error : error2; |
198 | } | |
199 | ||
932b563e AH |
200 | /* |
201 | * Set the attribute specified in @args. | |
202 | */ | |
203 | int | |
204 | xfs_attr_set_args( | |
9833c1a4 | 205 | struct xfs_da_args *args) |
932b563e AH |
206 | { |
207 | struct xfs_inode *dp = args->dp; | |
9833c1a4 | 208 | struct xfs_buf *leaf_bp = NULL; |
932b563e AH |
209 | int error; |
210 | ||
211 | /* | |
212 | * If the attribute list is non-existent or a shortform list, | |
213 | * upgrade it to a single-leaf-block attribute list. | |
214 | */ | |
d967a68d CH |
215 | if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL || |
216 | (dp->i_afp->if_format == XFS_DINODE_FMT_EXTENTS && | |
87c472b7 | 217 | dp->i_afp->if_nextents == 0)) { |
932b563e AH |
218 | |
219 | /* | |
220 | * Build initial attribute list (if required). | |
221 | */ | |
d967a68d | 222 | if (dp->i_afp->if_format == XFS_DINODE_FMT_EXTENTS) |
932b563e AH |
223 | xfs_attr_shortform_create(args); |
224 | ||
225 | /* | |
226 | * Try to add the attr to the attribute list in the inode. | |
227 | */ | |
228 | error = xfs_attr_try_sf_addname(dp, args); | |
229 | if (error != -ENOSPC) | |
230 | return error; | |
231 | ||
232 | /* | |
233 | * It won't fit in the shortform, transform to a leaf block. | |
234 | * GROT: another possible req'mt for a double-split btree op. | |
235 | */ | |
9833c1a4 | 236 | error = xfs_attr_shortform_to_leaf(args, &leaf_bp); |
932b563e AH |
237 | if (error) |
238 | return error; | |
239 | ||
240 | /* | |
241 | * Prevent the leaf buffer from being unlocked so that a | |
242 | * concurrent AIL push cannot grab the half-baked leaf | |
243 | * buffer and run into problems with the write verifier. | |
9833c1a4 DW |
244 | * Once we're done rolling the transaction we can release |
245 | * the hold and add the attr to the leaf. | |
932b563e | 246 | */ |
9833c1a4 | 247 | xfs_trans_bhold(args->trans, leaf_bp); |
932b563e | 248 | error = xfs_defer_finish(&args->trans); |
9833c1a4 DW |
249 | xfs_trans_bhold_release(args->trans, leaf_bp); |
250 | if (error) { | |
251 | xfs_trans_brelse(args->trans, leaf_bp); | |
932b563e | 252 | return error; |
9833c1a4 | 253 | } |
932b563e AH |
254 | } |
255 | ||
256 | if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) | |
257 | error = xfs_attr_leaf_addname(args); | |
258 | else | |
259 | error = xfs_attr_node_addname(args); | |
260 | return error; | |
261 | } | |
262 | ||
05b0a4ac AH |
263 | /* |
264 | * Remove the attribute specified in @args. | |
265 | */ | |
266 | int | |
267 | xfs_attr_remove_args( | |
268 | struct xfs_da_args *args) | |
269 | { | |
270 | struct xfs_inode *dp = args->dp; | |
271 | int error; | |
272 | ||
273 | if (!xfs_inode_hasattr(dp)) { | |
274 | error = -ENOATTR; | |
d967a68d | 275 | } else if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) { |
05b0a4ac AH |
276 | ASSERT(dp->i_afp->if_flags & XFS_IFINLINE); |
277 | error = xfs_attr_shortform_remove(args); | |
278 | } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { | |
279 | error = xfs_attr_leaf_removename(args); | |
280 | } else { | |
281 | error = xfs_attr_node_removename(args); | |
282 | } | |
283 | ||
284 | return error; | |
285 | } | |
286 | ||
ed7ea3ba | 287 | /* |
b8e89b44 | 288 | * Note: If args->value is NULL the attribute will be removed, just like the |
ed7ea3ba CH |
289 | * Linux ->setattr API. |
290 | */ | |
ff105f75 DC |
291 | int |
292 | xfs_attr_set( | |
b8e89b44 | 293 | struct xfs_da_args *args) |
57c9fccb | 294 | { |
b8e89b44 | 295 | struct xfs_inode *dp = args->dp; |
48ea6cb9 DC |
296 | struct xfs_mount *mp = dp->i_mount; |
297 | struct xfs_trans_res tres; | |
a392fd5a | 298 | bool rsvd = (args->attr_filter & XFS_ATTR_ROOT); |
6db48a68 | 299 | int error, local; |
ed7ea3ba | 300 | unsigned int total; |
ff105f75 DC |
301 | |
302 | if (XFS_FORCED_SHUTDOWN(dp->i_mount)) | |
12b53197 | 303 | return -EIO; |
ff105f75 | 304 | |
ed7ea3ba CH |
305 | error = xfs_qm_dqattach(dp); |
306 | if (error) | |
307 | return error; | |
308 | ||
b8e89b44 CH |
309 | args->geo = mp->m_attr_geo; |
310 | args->whichfork = XFS_ATTR_FORK; | |
311 | args->hashval = xfs_da_hashname(args->name, args->namelen); | |
ca86e759 | 312 | |
57c9fccb | 313 | /* |
ed7ea3ba CH |
314 | * We have no control over the attribute names that userspace passes us |
315 | * to remove, so we have to allow the name lookup prior to attribute | |
316 | * removal to fail as well. | |
57c9fccb | 317 | */ |
b8e89b44 | 318 | args->op_flags = XFS_DA_OP_OKNOENT; |
5e656dbb | 319 | |
b8e89b44 | 320 | if (args->value) { |
ed7ea3ba CH |
321 | XFS_STATS_INC(mp, xs_attr_set); |
322 | ||
b8e89b44 CH |
323 | args->op_flags |= XFS_DA_OP_ADDNAME; |
324 | args->total = xfs_attr_calc_size(args, &local); | |
ed7ea3ba CH |
325 | |
326 | /* | |
327 | * If the inode doesn't have an attribute fork, add one. | |
328 | * (inode must not be locked when we call this routine) | |
329 | */ | |
330 | if (XFS_IFORK_Q(dp) == 0) { | |
331 | int sf_size = sizeof(struct xfs_attr_sf_hdr) + | |
b8e89b44 CH |
332 | XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, |
333 | args->valuelen); | |
ed7ea3ba CH |
334 | |
335 | error = xfs_bmap_add_attrfork(dp, sf_size, rsvd); | |
336 | if (error) | |
337 | return error; | |
338 | } | |
339 | ||
340 | tres.tr_logres = M_RES(mp)->tr_attrsetm.tr_logres + | |
b8e89b44 CH |
341 | M_RES(mp)->tr_attrsetrt.tr_logres * |
342 | args->total; | |
ed7ea3ba CH |
343 | tres.tr_logcount = XFS_ATTRSET_LOG_COUNT; |
344 | tres.tr_logflags = XFS_TRANS_PERM_LOG_RES; | |
b8e89b44 | 345 | total = args->total; |
ed7ea3ba CH |
346 | } else { |
347 | XFS_STATS_INC(mp, xs_attr_remove); | |
57c9fccb | 348 | |
ed7ea3ba CH |
349 | tres = M_RES(mp)->tr_attrrm; |
350 | total = XFS_ATTRRM_SPACE_RES(mp); | |
351 | } | |
57c9fccb NS |
352 | |
353 | /* | |
354 | * Root fork attributes can use reserved data blocks for this | |
355 | * operation if necessary | |
356 | */ | |
ed7ea3ba | 357 | error = xfs_trans_alloc(mp, &tres, total, 0, |
b8e89b44 | 358 | rsvd ? XFS_TRANS_RESERVE : 0, &args->trans); |
9074815c | 359 | if (error) |
ff105f75 | 360 | return error; |
57c9fccb | 361 | |
9074815c | 362 | xfs_ilock(dp, XFS_ILOCK_EXCL); |
b8e89b44 CH |
363 | xfs_trans_ijoin(args->trans, dp, 0); |
364 | if (args->value) { | |
ed7ea3ba CH |
365 | unsigned int quota_flags = XFS_QMOPT_RES_REGBLKS; |
366 | ||
367 | if (rsvd) | |
368 | quota_flags |= XFS_QMOPT_FORCE_RES; | |
b8e89b44 CH |
369 | error = xfs_trans_reserve_quota_nblks(args->trans, dp, |
370 | args->total, 0, quota_flags); | |
ed7ea3ba CH |
371 | if (error) |
372 | goto out_trans_cancel; | |
b8e89b44 | 373 | error = xfs_attr_set_args(args); |
ed7ea3ba CH |
374 | if (error) |
375 | goto out_trans_cancel; | |
932b563e | 376 | /* shortform attribute has already been committed */ |
b8e89b44 | 377 | if (!args->trans) |
ed7ea3ba CH |
378 | goto out_unlock; |
379 | } else { | |
b8e89b44 | 380 | error = xfs_attr_remove_args(args); |
ed7ea3ba CH |
381 | if (error) |
382 | goto out_trans_cancel; | |
932b563e | 383 | } |
57c9fccb NS |
384 | |
385 | /* | |
386 | * If this is a synchronous mount, make sure that the | |
387 | * transaction goes to disk before returning to the user. | |
388 | */ | |
ff105f75 | 389 | if (mp->m_flags & XFS_MOUNT_WSYNC) |
b8e89b44 | 390 | xfs_trans_set_sync(args->trans); |
57c9fccb | 391 | |
69e818c2 | 392 | if (!(args->op_flags & XFS_DA_OP_NOTIME)) |
b8e89b44 | 393 | xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG); |
56b2de80 | 394 | |
57c9fccb NS |
395 | /* |
396 | * Commit the last in the sequence of transactions. | |
397 | */ | |
b8e89b44 CH |
398 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); |
399 | error = xfs_trans_commit(args->trans); | |
932b563e | 400 | out_unlock: |
57c9fccb | 401 | xfs_iunlock(dp, XFS_ILOCK_EXCL); |
ff105f75 | 402 | return error; |
57c9fccb | 403 | |
932b563e | 404 | out_trans_cancel: |
b8e89b44 CH |
405 | if (args->trans) |
406 | xfs_trans_cancel(args->trans); | |
932b563e | 407 | goto out_unlock; |
57c9fccb NS |
408 | } |
409 | ||
57c9fccb NS |
410 | /*======================================================================== |
411 | * External routines when attribute list is inside the inode | |
412 | *========================================================================*/ | |
413 | ||
414 | /* | |
415 | * Add a name to the shortform attribute list structure | |
416 | * This is the external routine. | |
417 | */ | |
418 | STATIC int | |
419 | xfs_attr_shortform_addname(xfs_da_args_t *args) | |
420 | { | |
ca86e759 | 421 | int newsize, forkoff, retval; |
57c9fccb | 422 | |
a2ceac1f DC |
423 | trace_xfs_attr_sf_addname(args); |
424 | ||
57c9fccb | 425 | retval = xfs_attr_shortform_lookup(args); |
a392fd5a | 426 | if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE)) |
af43ca9f | 427 | return retval; |
74ed8e80 | 428 | if (retval == -EEXIST) { |
a392fd5a | 429 | if (args->attr_flags & XATTR_CREATE) |
af43ca9f | 430 | return retval; |
57c9fccb | 431 | retval = xfs_attr_shortform_remove(args); |
dee7606b DW |
432 | if (retval) |
433 | return retval; | |
434 | /* | |
435 | * Since we have removed the old attr, clear ATTR_REPLACE so | |
436 | * that the leaf format add routine won't trip over the attr | |
437 | * not being around. | |
438 | */ | |
a392fd5a | 439 | args->attr_flags &= ~XATTR_REPLACE; |
57c9fccb NS |
440 | } |
441 | ||
ca86e759 NS |
442 | if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX || |
443 | args->valuelen >= XFS_ATTR_SF_ENTSIZE_MAX) | |
12b53197 | 444 | return -ENOSPC; |
ca86e759 | 445 | |
57c9fccb NS |
446 | newsize = XFS_ATTR_SF_TOTSIZE(args->dp); |
447 | newsize += XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen); | |
ca86e759 NS |
448 | |
449 | forkoff = xfs_attr_shortform_bytesfit(args->dp, newsize); | |
450 | if (!forkoff) | |
12b53197 | 451 | return -ENOSPC; |
ca86e759 NS |
452 | |
453 | xfs_attr_shortform_add(args, forkoff); | |
af43ca9f | 454 | return 0; |
57c9fccb NS |
455 | } |
456 | ||
457 | ||
458 | /*======================================================================== | |
459 | * External routines when attribute list is one block | |
460 | *========================================================================*/ | |
461 | ||
462 | /* | |
463 | * Add a name to the leaf attribute list structure | |
464 | * | |
465 | * This leaf block cannot have a "remote" value, we only call this routine | |
466 | * if bmap_one_block() says there is only one block (ie: no remote blks). | |
467 | */ | |
5e656dbb | 468 | STATIC int |
a50d2ab0 BF |
469 | xfs_attr_leaf_addname( |
470 | struct xfs_da_args *args) | |
57c9fccb | 471 | { |
a50d2ab0 BF |
472 | struct xfs_inode *dp; |
473 | struct xfs_buf *bp; | |
474 | int retval, error, forkoff; | |
57c9fccb | 475 | |
a2ceac1f DC |
476 | trace_xfs_attr_leaf_addname(args); |
477 | ||
57c9fccb NS |
478 | /* |
479 | * Read the (only) block in the attribute list in. | |
480 | */ | |
481 | dp = args->dp; | |
482 | args->blkno = 0; | |
edf3b3a8 | 483 | error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp); |
57c9fccb | 484 | if (error) |
a2ceac1f | 485 | return error; |
57c9fccb NS |
486 | |
487 | /* | |
488 | * Look up the given attribute in the leaf block. Figure out if | |
489 | * the given flags produce an error or call for an atomic rename. | |
490 | */ | |
a24374f4 | 491 | retval = xfs_attr3_leaf_lookup_int(bp, args); |
a392fd5a | 492 | if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE)) |
74ed8e80 CH |
493 | goto out_brelse; |
494 | if (retval == -EEXIST) { | |
a392fd5a | 495 | if (args->attr_flags & XATTR_CREATE) |
74ed8e80 | 496 | goto out_brelse; |
a2ceac1f DC |
497 | |
498 | trace_xfs_attr_leaf_replace(args); | |
499 | ||
ff105f75 | 500 | /* save the attribute state for later removal*/ |
5e656dbb | 501 | args->op_flags |= XFS_DA_OP_RENAME; /* an atomic rename */ |
57c9fccb NS |
502 | args->blkno2 = args->blkno; /* set 2nd entry info*/ |
503 | args->index2 = args->index; | |
504 | args->rmtblkno2 = args->rmtblkno; | |
505 | args->rmtblkcnt2 = args->rmtblkcnt; | |
ff105f75 DC |
506 | args->rmtvaluelen2 = args->rmtvaluelen; |
507 | ||
508 | /* | |
509 | * clear the remote attr state now that it is saved so that the | |
510 | * values reflect the state of the attribute we are about to | |
511 | * add, not the attribute we just found and will remove later. | |
512 | */ | |
513 | args->rmtblkno = 0; | |
514 | args->rmtblkcnt = 0; | |
515 | args->rmtvaluelen = 0; | |
57c9fccb NS |
516 | } |
517 | ||
518 | /* | |
519 | * Add the attribute to the leaf block, transitioning to a Btree | |
520 | * if required. | |
521 | */ | |
a24374f4 | 522 | retval = xfs_attr3_leaf_add(bp, args); |
12b53197 | 523 | if (retval == -ENOSPC) { |
57c9fccb NS |
524 | /* |
525 | * Promote the attribute list to the Btree format, then | |
526 | * Commit that transaction so that the node_addname() call | |
527 | * can manage its own transactions. | |
528 | */ | |
a24374f4 | 529 | error = xfs_attr3_leaf_to_node(args); |
5c33baee | 530 | if (error) |
50fba283 | 531 | return error; |
ac0a2228 | 532 | error = xfs_defer_finish(&args->trans); |
5c33baee | 533 | if (error) |
9f5a828b | 534 | return error; |
57c9fccb | 535 | |
57c9fccb NS |
536 | /* |
537 | * Commit the current trans (including the inode) and start | |
538 | * a new one. | |
539 | */ | |
d67406c9 | 540 | error = xfs_trans_roll_inode(&args->trans, dp); |
5e656dbb | 541 | if (error) |
af43ca9f | 542 | return error; |
57c9fccb NS |
543 | |
544 | /* | |
545 | * Fob the whole rest of the problem off on the Btree code. | |
546 | */ | |
547 | error = xfs_attr_node_addname(args); | |
af43ca9f | 548 | return error; |
57c9fccb NS |
549 | } |
550 | ||
551 | /* | |
552 | * Commit the transaction that added the attr name so that | |
553 | * later routines can manage their own transactions. | |
554 | */ | |
d67406c9 | 555 | error = xfs_trans_roll_inode(&args->trans, dp); |
5e656dbb | 556 | if (error) |
af43ca9f | 557 | return error; |
57c9fccb NS |
558 | |
559 | /* | |
560 | * If there was an out-of-line value, allocate the blocks we | |
561 | * identified for its storage and copy the value. This is done | |
562 | * after we create the attribute so that we don't overflow the | |
563 | * maximum size of a transaction and/or hit a deadlock. | |
564 | */ | |
565 | if (args->rmtblkno > 0) { | |
566 | error = xfs_attr_rmtval_set(args); | |
567 | if (error) | |
af43ca9f | 568 | return error; |
57c9fccb NS |
569 | } |
570 | ||
571 | /* | |
572 | * If this is an atomic rename operation, we must "flip" the | |
573 | * incomplete flags on the "new" and "old" attribute/value pairs | |
574 | * so that one disappears and one appears atomically. Then we | |
575 | * must remove the "old" attribute/value pair. | |
576 | */ | |
5e656dbb | 577 | if (args->op_flags & XFS_DA_OP_RENAME) { |
57c9fccb NS |
578 | /* |
579 | * In a separate transaction, set the incomplete flag on the | |
580 | * "old" attr and clear the incomplete flag on the "new" attr. | |
581 | */ | |
a24374f4 | 582 | error = xfs_attr3_leaf_flipflags(args); |
57c9fccb | 583 | if (error) |
af43ca9f | 584 | return error; |
57c9fccb NS |
585 | |
586 | /* | |
587 | * Dismantle the "old" attribute/value pair by removing | |
588 | * a "remote" value (if it exists). | |
589 | */ | |
590 | args->index = args->index2; | |
591 | args->blkno = args->blkno2; | |
592 | args->rmtblkno = args->rmtblkno2; | |
593 | args->rmtblkcnt = args->rmtblkcnt2; | |
ff105f75 | 594 | args->rmtvaluelen = args->rmtvaluelen2; |
57c9fccb NS |
595 | if (args->rmtblkno) { |
596 | error = xfs_attr_rmtval_remove(args); | |
597 | if (error) | |
af43ca9f | 598 | return error; |
57c9fccb NS |
599 | } |
600 | ||
601 | /* | |
602 | * Read in the block containing the "old" attr, then | |
603 | * remove the "old" attr from that block (neat, huh!) | |
604 | */ | |
a24374f4 | 605 | error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, |
edf3b3a8 | 606 | &bp); |
57c9fccb | 607 | if (error) |
a2ceac1f DC |
608 | return error; |
609 | ||
a24374f4 | 610 | xfs_attr3_leaf_remove(bp, args); |
57c9fccb NS |
611 | |
612 | /* | |
613 | * If the result is small enough, shrink it all into the inode. | |
614 | */ | |
ca86e759 | 615 | if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) { |
a24374f4 | 616 | error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); |
57c9fccb | 617 | /* bp is gone due to xfs_da_shrink_inode */ |
5c33baee | 618 | if (error) |
50fba283 | 619 | return error; |
ac0a2228 | 620 | error = xfs_defer_finish(&args->trans); |
5c33baee | 621 | if (error) |
9f5a828b | 622 | return error; |
a2ceac1f | 623 | } |
57c9fccb NS |
624 | |
625 | /* | |
626 | * Commit the remove and start the next trans in series. | |
627 | */ | |
d67406c9 | 628 | error = xfs_trans_roll_inode(&args->trans, dp); |
57c9fccb NS |
629 | |
630 | } else if (args->rmtblkno > 0) { | |
631 | /* | |
632 | * Added a "remote" value, just clear the incomplete flag. | |
633 | */ | |
a24374f4 | 634 | error = xfs_attr3_leaf_clearflag(args); |
57c9fccb | 635 | } |
a24374f4 | 636 | return error; |
74ed8e80 CH |
637 | out_brelse: |
638 | xfs_trans_brelse(args->trans, bp); | |
639 | return retval; | |
57c9fccb NS |
640 | } |
641 | ||
642 | /* | |
643 | * Remove a name from the leaf attribute list structure | |
644 | * | |
645 | * This leaf block cannot have a "remote" value, we only call this routine | |
646 | * if bmap_one_block() says there is only one block (ie: no remote blks). | |
647 | */ | |
648 | STATIC int | |
a50d2ab0 BF |
649 | xfs_attr_leaf_removename( |
650 | struct xfs_da_args *args) | |
57c9fccb | 651 | { |
a50d2ab0 BF |
652 | struct xfs_inode *dp; |
653 | struct xfs_buf *bp; | |
654 | int error, forkoff; | |
57c9fccb | 655 | |
a2ceac1f DC |
656 | trace_xfs_attr_leaf_removename(args); |
657 | ||
57c9fccb NS |
658 | /* |
659 | * Remove the attribute. | |
660 | */ | |
661 | dp = args->dp; | |
662 | args->blkno = 0; | |
edf3b3a8 | 663 | error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp); |
a2ceac1f DC |
664 | if (error) |
665 | return error; | |
57c9fccb | 666 | |
a24374f4 | 667 | error = xfs_attr3_leaf_lookup_int(bp, args); |
12b53197 | 668 | if (error == -ENOATTR) { |
a2ceac1f | 669 | xfs_trans_brelse(args->trans, bp); |
78d6585c | 670 | return error; |
57c9fccb NS |
671 | } |
672 | ||
a24374f4 | 673 | xfs_attr3_leaf_remove(bp, args); |
57c9fccb NS |
674 | |
675 | /* | |
676 | * If the result is small enough, shrink it all into the inode. | |
677 | */ | |
ca86e759 | 678 | if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) { |
a24374f4 | 679 | error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); |
57c9fccb | 680 | /* bp is gone due to xfs_da_shrink_inode */ |
5c33baee | 681 | if (error) |
50fba283 | 682 | return error; |
ac0a2228 | 683 | error = xfs_defer_finish(&args->trans); |
5c33baee | 684 | if (error) |
9f5a828b | 685 | return error; |
a2ceac1f | 686 | } |
a24374f4 | 687 | return 0; |
57c9fccb NS |
688 | } |
689 | ||
5e656dbb BN |
690 | /* |
691 | * Look up a name in a leaf attribute list structure. | |
692 | * | |
693 | * This leaf block cannot have a "remote" value, we only call this routine | |
694 | * if bmap_one_block() says there is only one block (ie: no remote blks). | |
42a383ab DC |
695 | * |
696 | * Returns 0 on successful retrieval, otherwise an error. | |
5e656dbb BN |
697 | */ |
698 | STATIC int | |
699 | xfs_attr_leaf_get(xfs_da_args_t *args) | |
700 | { | |
a2ceac1f | 701 | struct xfs_buf *bp; |
5e656dbb BN |
702 | int error; |
703 | ||
a2ceac1f DC |
704 | trace_xfs_attr_leaf_get(args); |
705 | ||
5e656dbb | 706 | args->blkno = 0; |
edf3b3a8 | 707 | error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp); |
5e656dbb | 708 | if (error) |
a2ceac1f | 709 | return error; |
5e656dbb | 710 | |
a24374f4 | 711 | error = xfs_attr3_leaf_lookup_int(bp, args); |
12b53197 | 712 | if (error != -EEXIST) { |
a2ceac1f | 713 | xfs_trans_brelse(args->trans, bp); |
a24374f4 | 714 | return error; |
5e656dbb | 715 | } |
a24374f4 | 716 | error = xfs_attr3_leaf_getvalue(bp, args); |
a2ceac1f | 717 | xfs_trans_brelse(args->trans, bp); |
17e72771 | 718 | return error; |
5e656dbb BN |
719 | } |
720 | ||
57c9fccb | 721 | /*======================================================================== |
ff105f75 | 722 | * External routines when attribute list size > geo->blksize |
57c9fccb NS |
723 | *========================================================================*/ |
724 | ||
725 | /* | |
726 | * Add a name to a Btree-format attribute list. | |
727 | * | |
728 | * This will involve walking down the Btree, and may involve splitting | |
729 | * leaf nodes and even splitting intermediate nodes up to and including | |
730 | * the root node (a special case of an intermediate node). | |
731 | * | |
732 | * "Remote" attribute values confuse the issue and atomic rename operations | |
733 | * add a whole extra layer of confusion on top of that. | |
734 | */ | |
735 | STATIC int | |
a50d2ab0 BF |
736 | xfs_attr_node_addname( |
737 | struct xfs_da_args *args) | |
57c9fccb | 738 | { |
a50d2ab0 BF |
739 | struct xfs_da_state *state; |
740 | struct xfs_da_state_blk *blk; | |
741 | struct xfs_inode *dp; | |
742 | struct xfs_mount *mp; | |
743 | int retval, error; | |
57c9fccb | 744 | |
a2ceac1f DC |
745 | trace_xfs_attr_node_addname(args); |
746 | ||
57c9fccb NS |
747 | /* |
748 | * Fill in bucket of arguments/results/context to carry around. | |
749 | */ | |
750 | dp = args->dp; | |
751 | mp = dp->i_mount; | |
752 | restart: | |
753 | state = xfs_da_state_alloc(); | |
754 | state->args = args; | |
755 | state->mp = mp; | |
57c9fccb NS |
756 | |
757 | /* | |
758 | * Search to see if name already exists, and get back a pointer | |
759 | * to where it should go. | |
760 | */ | |
88b32f06 | 761 | error = xfs_da3_node_lookup_int(state, &retval); |
57c9fccb NS |
762 | if (error) |
763 | goto out; | |
764 | blk = &state->path.blk[ state->path.active-1 ]; | |
765 | ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); | |
a392fd5a | 766 | if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE)) |
57c9fccb | 767 | goto out; |
74ed8e80 | 768 | if (retval == -EEXIST) { |
a392fd5a | 769 | if (args->attr_flags & XATTR_CREATE) |
57c9fccb | 770 | goto out; |
a2ceac1f DC |
771 | |
772 | trace_xfs_attr_node_replace(args); | |
773 | ||
ff105f75 | 774 | /* save the attribute state for later removal*/ |
5e656dbb | 775 | args->op_flags |= XFS_DA_OP_RENAME; /* atomic rename op */ |
57c9fccb NS |
776 | args->blkno2 = args->blkno; /* set 2nd entry info*/ |
777 | args->index2 = args->index; | |
778 | args->rmtblkno2 = args->rmtblkno; | |
779 | args->rmtblkcnt2 = args->rmtblkcnt; | |
ff105f75 DC |
780 | args->rmtvaluelen2 = args->rmtvaluelen; |
781 | ||
782 | /* | |
783 | * clear the remote attr state now that it is saved so that the | |
784 | * values reflect the state of the attribute we are about to | |
785 | * add, not the attribute we just found and will remove later. | |
786 | */ | |
57c9fccb NS |
787 | args->rmtblkno = 0; |
788 | args->rmtblkcnt = 0; | |
ff105f75 | 789 | args->rmtvaluelen = 0; |
57c9fccb NS |
790 | } |
791 | ||
a24374f4 | 792 | retval = xfs_attr3_leaf_add(blk->bp, state->args); |
12b53197 | 793 | if (retval == -ENOSPC) { |
57c9fccb NS |
794 | if (state->path.active == 1) { |
795 | /* | |
796 | * Its really a single leaf node, but it had | |
797 | * out-of-line values so it looked like it *might* | |
798 | * have been a b-tree. | |
799 | */ | |
800 | xfs_da_state_free(state); | |
504dbe46 | 801 | state = NULL; |
a24374f4 | 802 | error = xfs_attr3_leaf_to_node(args); |
5c33baee | 803 | if (error) |
50fba283 | 804 | goto out; |
ac0a2228 | 805 | error = xfs_defer_finish(&args->trans); |
5c33baee | 806 | if (error) |
9f5a828b | 807 | goto out; |
57c9fccb | 808 | |
57c9fccb NS |
809 | /* |
810 | * Commit the node conversion and start the next | |
811 | * trans in the chain. | |
812 | */ | |
d67406c9 | 813 | error = xfs_trans_roll_inode(&args->trans, dp); |
5e656dbb | 814 | if (error) |
57c9fccb NS |
815 | goto out; |
816 | ||
817 | goto restart; | |
818 | } | |
819 | ||
820 | /* | |
821 | * Split as many Btree elements as required. | |
822 | * This code tracks the new and old attr's location | |
823 | * in the index/blkno/rmtblkno/rmtblkcnt fields and | |
824 | * in the index2/blkno2/rmtblkno2/rmtblkcnt2 fields. | |
825 | */ | |
88b32f06 | 826 | error = xfs_da3_split(state); |
5c33baee | 827 | if (error) |
50fba283 | 828 | goto out; |
ac0a2228 | 829 | error = xfs_defer_finish(&args->trans); |
5c33baee | 830 | if (error) |
9f5a828b | 831 | goto out; |
57c9fccb NS |
832 | } else { |
833 | /* | |
834 | * Addition succeeded, update Btree hashvals. | |
835 | */ | |
88b32f06 | 836 | xfs_da3_fixhashpath(state, &state->path); |
57c9fccb NS |
837 | } |
838 | ||
839 | /* | |
840 | * Kill the state structure, we're done with it and need to | |
841 | * allow the buffers to come back later. | |
842 | */ | |
843 | xfs_da_state_free(state); | |
844 | state = NULL; | |
845 | ||
846 | /* | |
847 | * Commit the leaf addition or btree split and start the next | |
848 | * trans in the chain. | |
849 | */ | |
d67406c9 | 850 | error = xfs_trans_roll_inode(&args->trans, dp); |
5e656dbb | 851 | if (error) |
57c9fccb NS |
852 | goto out; |
853 | ||
854 | /* | |
855 | * If there was an out-of-line value, allocate the blocks we | |
856 | * identified for its storage and copy the value. This is done | |
857 | * after we create the attribute so that we don't overflow the | |
858 | * maximum size of a transaction and/or hit a deadlock. | |
859 | */ | |
860 | if (args->rmtblkno > 0) { | |
861 | error = xfs_attr_rmtval_set(args); | |
862 | if (error) | |
af43ca9f | 863 | return error; |
57c9fccb NS |
864 | } |
865 | ||
866 | /* | |
867 | * If this is an atomic rename operation, we must "flip" the | |
868 | * incomplete flags on the "new" and "old" attribute/value pairs | |
869 | * so that one disappears and one appears atomically. Then we | |
870 | * must remove the "old" attribute/value pair. | |
871 | */ | |
5e656dbb | 872 | if (args->op_flags & XFS_DA_OP_RENAME) { |
57c9fccb NS |
873 | /* |
874 | * In a separate transaction, set the incomplete flag on the | |
875 | * "old" attr and clear the incomplete flag on the "new" attr. | |
876 | */ | |
a24374f4 | 877 | error = xfs_attr3_leaf_flipflags(args); |
57c9fccb NS |
878 | if (error) |
879 | goto out; | |
880 | ||
881 | /* | |
882 | * Dismantle the "old" attribute/value pair by removing | |
883 | * a "remote" value (if it exists). | |
884 | */ | |
885 | args->index = args->index2; | |
886 | args->blkno = args->blkno2; | |
887 | args->rmtblkno = args->rmtblkno2; | |
888 | args->rmtblkcnt = args->rmtblkcnt2; | |
ff105f75 | 889 | args->rmtvaluelen = args->rmtvaluelen2; |
57c9fccb NS |
890 | if (args->rmtblkno) { |
891 | error = xfs_attr_rmtval_remove(args); | |
892 | if (error) | |
af43ca9f | 893 | return error; |
57c9fccb NS |
894 | } |
895 | ||
896 | /* | |
897 | * Re-find the "old" attribute entry after any split ops. | |
898 | * The INCOMPLETE flag means that we will find the "old" | |
899 | * attr, not the "new" one. | |
900 | */ | |
09e17ba0 | 901 | args->attr_filter |= XFS_ATTR_INCOMPLETE; |
57c9fccb NS |
902 | state = xfs_da_state_alloc(); |
903 | state->args = args; | |
904 | state->mp = mp; | |
57c9fccb | 905 | state->inleaf = 0; |
88b32f06 | 906 | error = xfs_da3_node_lookup_int(state, &retval); |
57c9fccb NS |
907 | if (error) |
908 | goto out; | |
909 | ||
910 | /* | |
911 | * Remove the name and update the hashvals in the tree. | |
912 | */ | |
913 | blk = &state->path.blk[ state->path.active-1 ]; | |
914 | ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); | |
a24374f4 | 915 | error = xfs_attr3_leaf_remove(blk->bp, args); |
88b32f06 | 916 | xfs_da3_fixhashpath(state, &state->path); |
57c9fccb NS |
917 | |
918 | /* | |
919 | * Check to see if the tree needs to be collapsed. | |
920 | */ | |
921 | if (retval && (state->path.active > 1)) { | |
88b32f06 | 922 | error = xfs_da3_join(state); |
5c33baee | 923 | if (error) |
50fba283 | 924 | goto out; |
ac0a2228 | 925 | error = xfs_defer_finish(&args->trans); |
5c33baee | 926 | if (error) |
9f5a828b | 927 | goto out; |
57c9fccb NS |
928 | } |
929 | ||
930 | /* | |
931 | * Commit and start the next trans in the chain. | |
932 | */ | |
d67406c9 | 933 | error = xfs_trans_roll_inode(&args->trans, dp); |
5e656dbb | 934 | if (error) |
57c9fccb NS |
935 | goto out; |
936 | ||
937 | } else if (args->rmtblkno > 0) { | |
938 | /* | |
939 | * Added a "remote" value, just clear the incomplete flag. | |
940 | */ | |
a24374f4 | 941 | error = xfs_attr3_leaf_clearflag(args); |
57c9fccb NS |
942 | if (error) |
943 | goto out; | |
944 | } | |
945 | retval = error = 0; | |
946 | ||
947 | out: | |
948 | if (state) | |
949 | xfs_da_state_free(state); | |
950 | if (error) | |
af43ca9f DC |
951 | return error; |
952 | return retval; | |
57c9fccb NS |
953 | } |
954 | ||
955 | /* | |
956 | * Remove a name from a B-tree attribute list. | |
957 | * | |
958 | * This will involve walking down the Btree, and may involve joining | |
959 | * leaf nodes and even joining intermediate nodes up to and including | |
960 | * the root node (a special case of an intermediate node). | |
961 | */ | |
962 | STATIC int | |
a50d2ab0 BF |
963 | xfs_attr_node_removename( |
964 | struct xfs_da_args *args) | |
57c9fccb | 965 | { |
a50d2ab0 BF |
966 | struct xfs_da_state *state; |
967 | struct xfs_da_state_blk *blk; | |
968 | struct xfs_inode *dp; | |
969 | struct xfs_buf *bp; | |
970 | int retval, error, forkoff; | |
57c9fccb | 971 | |
a2ceac1f DC |
972 | trace_xfs_attr_node_removename(args); |
973 | ||
57c9fccb NS |
974 | /* |
975 | * Tie a string around our finger to remind us where we are. | |
976 | */ | |
977 | dp = args->dp; | |
978 | state = xfs_da_state_alloc(); | |
979 | state->args = args; | |
980 | state->mp = dp->i_mount; | |
57c9fccb NS |
981 | |
982 | /* | |
983 | * Search to see if name exists, and get back a pointer to it. | |
984 | */ | |
88b32f06 | 985 | error = xfs_da3_node_lookup_int(state, &retval); |
12b53197 | 986 | if (error || (retval != -EEXIST)) { |
57c9fccb NS |
987 | if (error == 0) |
988 | error = retval; | |
989 | goto out; | |
990 | } | |
991 | ||
992 | /* | |
993 | * If there is an out-of-line value, de-allocate the blocks. | |
994 | * This is done before we remove the attribute so that we don't | |
995 | * overflow the maximum size of a transaction and/or hit a deadlock. | |
996 | */ | |
997 | blk = &state->path.blk[ state->path.active-1 ]; | |
998 | ASSERT(blk->bp != NULL); | |
999 | ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); | |
1000 | if (args->rmtblkno > 0) { | |
1001 | /* | |
1002 | * Fill in disk block numbers in the state structure | |
1003 | * so that we can get the buffers back after we commit | |
1004 | * several transactions in the following calls. | |
1005 | */ | |
1006 | error = xfs_attr_fillstate(state); | |
1007 | if (error) | |
1008 | goto out; | |
1009 | ||
1010 | /* | |
1011 | * Mark the attribute as INCOMPLETE, then bunmapi() the | |
1012 | * remote value. | |
1013 | */ | |
a24374f4 | 1014 | error = xfs_attr3_leaf_setflag(args); |
57c9fccb NS |
1015 | if (error) |
1016 | goto out; | |
1017 | error = xfs_attr_rmtval_remove(args); | |
1018 | if (error) | |
1019 | goto out; | |
1020 | ||
1021 | /* | |
1022 | * Refill the state structure with buffers, the prior calls | |
1023 | * released our buffers. | |
1024 | */ | |
1025 | error = xfs_attr_refillstate(state); | |
1026 | if (error) | |
1027 | goto out; | |
1028 | } | |
1029 | ||
1030 | /* | |
1031 | * Remove the name and update the hashvals in the tree. | |
1032 | */ | |
1033 | blk = &state->path.blk[ state->path.active-1 ]; | |
1034 | ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); | |
a24374f4 | 1035 | retval = xfs_attr3_leaf_remove(blk->bp, args); |
88b32f06 | 1036 | xfs_da3_fixhashpath(state, &state->path); |
57c9fccb NS |
1037 | |
1038 | /* | |
1039 | * Check to see if the tree needs to be collapsed. | |
1040 | */ | |
1041 | if (retval && (state->path.active > 1)) { | |
88b32f06 | 1042 | error = xfs_da3_join(state); |
5c33baee | 1043 | if (error) |
50fba283 | 1044 | goto out; |
ac0a2228 | 1045 | error = xfs_defer_finish(&args->trans); |
5c33baee | 1046 | if (error) |
9f5a828b | 1047 | goto out; |
57c9fccb NS |
1048 | /* |
1049 | * Commit the Btree join operation and start a new trans. | |
1050 | */ | |
d67406c9 | 1051 | error = xfs_trans_roll_inode(&args->trans, dp); |
5e656dbb | 1052 | if (error) |
57c9fccb NS |
1053 | goto out; |
1054 | } | |
1055 | ||
1056 | /* | |
1057 | * If the result is small enough, push it all into the inode. | |
1058 | */ | |
1059 | if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { | |
1060 | /* | |
1061 | * Have to get rid of the copy of this dabuf in the state. | |
1062 | */ | |
1063 | ASSERT(state->path.active == 1); | |
1064 | ASSERT(state->path.blk[0].bp); | |
57c9fccb NS |
1065 | state->path.blk[0].bp = NULL; |
1066 | ||
edf3b3a8 | 1067 | error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp); |
57c9fccb NS |
1068 | if (error) |
1069 | goto out; | |
57c9fccb | 1070 | |
ca86e759 | 1071 | if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) { |
a24374f4 | 1072 | error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); |
57c9fccb | 1073 | /* bp is gone due to xfs_da_shrink_inode */ |
5c33baee | 1074 | if (error) |
50fba283 | 1075 | goto out; |
ac0a2228 | 1076 | error = xfs_defer_finish(&args->trans); |
5c33baee | 1077 | if (error) |
9f5a828b | 1078 | goto out; |
57c9fccb | 1079 | } else |
a2ceac1f | 1080 | xfs_trans_brelse(args->trans, bp); |
57c9fccb NS |
1081 | } |
1082 | error = 0; | |
1083 | ||
1084 | out: | |
1085 | xfs_da_state_free(state); | |
af43ca9f | 1086 | return error; |
57c9fccb NS |
1087 | } |
1088 | ||
1089 | /* | |
1090 | * Fill in the disk block numbers in the state structure for the buffers | |
1091 | * that are attached to the state structure. | |
1092 | * This is done so that we can quickly reattach ourselves to those buffers | |
5e656dbb | 1093 | * after some set of transaction commits have released these buffers. |
57c9fccb NS |
1094 | */ |
1095 | STATIC int | |
1096 | xfs_attr_fillstate(xfs_da_state_t *state) | |
1097 | { | |
1098 | xfs_da_state_path_t *path; | |
1099 | xfs_da_state_blk_t *blk; | |
1100 | int level; | |
1101 | ||
a2ceac1f DC |
1102 | trace_xfs_attr_fillstate(state->args); |
1103 | ||
57c9fccb NS |
1104 | /* |
1105 | * Roll down the "path" in the state structure, storing the on-disk | |
1106 | * block number for those buffers in the "path". | |
1107 | */ | |
1108 | path = &state->path; | |
1109 | ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); | |
1110 | for (blk = path->blk, level = 0; level < path->active; blk++, level++) { | |
1111 | if (blk->bp) { | |
a2ceac1f | 1112 | blk->disk_blkno = XFS_BUF_ADDR(blk->bp); |
57c9fccb NS |
1113 | blk->bp = NULL; |
1114 | } else { | |
1115 | blk->disk_blkno = 0; | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | /* | |
1120 | * Roll down the "altpath" in the state structure, storing the on-disk | |
1121 | * block number for those buffers in the "altpath". | |
1122 | */ | |
1123 | path = &state->altpath; | |
1124 | ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); | |
1125 | for (blk = path->blk, level = 0; level < path->active; blk++, level++) { | |
1126 | if (blk->bp) { | |
a2ceac1f | 1127 | blk->disk_blkno = XFS_BUF_ADDR(blk->bp); |
57c9fccb NS |
1128 | blk->bp = NULL; |
1129 | } else { | |
1130 | blk->disk_blkno = 0; | |
1131 | } | |
1132 | } | |
1133 | ||
af43ca9f | 1134 | return 0; |
57c9fccb NS |
1135 | } |
1136 | ||
1137 | /* | |
1138 | * Reattach the buffers to the state structure based on the disk block | |
1139 | * numbers stored in the state structure. | |
5e656dbb | 1140 | * This is done after some set of transaction commits have released those |
57c9fccb NS |
1141 | * buffers from our grip. |
1142 | */ | |
1143 | STATIC int | |
1144 | xfs_attr_refillstate(xfs_da_state_t *state) | |
1145 | { | |
1146 | xfs_da_state_path_t *path; | |
1147 | xfs_da_state_blk_t *blk; | |
1148 | int level, error; | |
1149 | ||
a2ceac1f DC |
1150 | trace_xfs_attr_refillstate(state->args); |
1151 | ||
57c9fccb NS |
1152 | /* |
1153 | * Roll down the "path" in the state structure, storing the on-disk | |
1154 | * block number for those buffers in the "path". | |
1155 | */ | |
1156 | path = &state->path; | |
1157 | ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); | |
1158 | for (blk = path->blk, level = 0; level < path->active; blk++, level++) { | |
1159 | if (blk->disk_blkno) { | |
02cc4995 CH |
1160 | error = xfs_da3_node_read_mapped(state->args->trans, |
1161 | state->args->dp, blk->disk_blkno, | |
1162 | &blk->bp, XFS_ATTR_FORK); | |
57c9fccb | 1163 | if (error) |
af43ca9f | 1164 | return error; |
57c9fccb NS |
1165 | } else { |
1166 | blk->bp = NULL; | |
1167 | } | |
1168 | } | |
1169 | ||
1170 | /* | |
1171 | * Roll down the "altpath" in the state structure, storing the on-disk | |
1172 | * block number for those buffers in the "altpath". | |
1173 | */ | |
1174 | path = &state->altpath; | |
1175 | ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); | |
1176 | for (blk = path->blk, level = 0; level < path->active; blk++, level++) { | |
1177 | if (blk->disk_blkno) { | |
02cc4995 CH |
1178 | error = xfs_da3_node_read_mapped(state->args->trans, |
1179 | state->args->dp, blk->disk_blkno, | |
1180 | &blk->bp, XFS_ATTR_FORK); | |
57c9fccb | 1181 | if (error) |
af43ca9f | 1182 | return error; |
57c9fccb NS |
1183 | } else { |
1184 | blk->bp = NULL; | |
1185 | } | |
1186 | } | |
1187 | ||
af43ca9f | 1188 | return 0; |
57c9fccb NS |
1189 | } |
1190 | ||
5e656dbb | 1191 | /* |
42a383ab | 1192 | * Retrieve the attribute data from a node attribute list. |
5e656dbb BN |
1193 | * |
1194 | * This routine gets called for any attribute fork that has more than one | |
1195 | * block, ie: both true Btree attr lists and for single-leaf-blocks with | |
1196 | * "remote" values taking up more blocks. | |
42a383ab DC |
1197 | * |
1198 | * Returns 0 on successful retrieval, otherwise an error. | |
5e656dbb BN |
1199 | */ |
1200 | STATIC int | |
1201 | xfs_attr_node_get(xfs_da_args_t *args) | |
1202 | { | |
1203 | xfs_da_state_t *state; | |
1204 | xfs_da_state_blk_t *blk; | |
1205 | int error, retval; | |
1206 | int i; | |
1207 | ||
a2ceac1f DC |
1208 | trace_xfs_attr_node_get(args); |
1209 | ||
5e656dbb BN |
1210 | state = xfs_da_state_alloc(); |
1211 | state->args = args; | |
1212 | state->mp = args->dp->i_mount; | |
5e656dbb BN |
1213 | |
1214 | /* | |
1215 | * Search to see if name exists, and get back a pointer to it. | |
1216 | */ | |
88b32f06 | 1217 | error = xfs_da3_node_lookup_int(state, &retval); |
5e656dbb BN |
1218 | if (error) { |
1219 | retval = error; | |
42a383ab | 1220 | goto out_release; |
5e656dbb | 1221 | } |
42a383ab DC |
1222 | if (retval != -EEXIST) |
1223 | goto out_release; | |
1224 | ||
1225 | /* | |
1226 | * Get the value, local or "remote" | |
1227 | */ | |
1228 | blk = &state->path.blk[state->path.active - 1]; | |
1229 | retval = xfs_attr3_leaf_getvalue(blk->bp, args); | |
5e656dbb BN |
1230 | |
1231 | /* | |
1232 | * If not in a transaction, we have to release all the buffers. | |
1233 | */ | |
42a383ab | 1234 | out_release: |
5e656dbb | 1235 | for (i = 0; i < state->path.active; i++) { |
a2ceac1f | 1236 | xfs_trans_brelse(args->trans, state->path.blk[i].bp); |
5e656dbb BN |
1237 | state->path.blk[i].bp = NULL; |
1238 | } | |
1239 | ||
1240 | xfs_da_state_free(state); | |
af43ca9f | 1241 | return retval; |
5e656dbb | 1242 | } |
06926579 DW |
1243 | |
1244 | /* Returns true if the attribute entry name is valid. */ | |
1245 | bool | |
1246 | xfs_attr_namecheck( | |
1247 | const void *name, | |
1248 | size_t length) | |
1249 | { | |
1250 | /* | |
1251 | * MAXNAMELEN includes the trailing null, but (name/length) leave it | |
1252 | * out, so use >= for the length check. | |
1253 | */ | |
1254 | if (length >= MAXNAMELEN) | |
1255 | return false; | |
1256 | ||
1257 | /* There shouldn't be any nulls here */ | |
1258 | return !memchr(name, 0, length); | |
1259 | } |