]>
Commit | Line | Data |
---|---|---|
2bd0ea18 | 1 | /* |
0d3e0b37 | 2 | * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. |
5000d01d | 3 | * |
2bd0ea18 NS |
4 | * This program is free software; you can redistribute it and/or modify it |
5 | * under the terms of version 2 of the GNU General Public License as | |
6 | * published by the Free Software Foundation. | |
5000d01d | 7 | * |
2bd0ea18 NS |
8 | * This program is distributed in the hope that it would be useful, but |
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
5000d01d | 11 | * |
2bd0ea18 NS |
12 | * Further, this software is distributed without any warranty that it is |
13 | * free of the rightful claim of any third person regarding infringement | |
dfc130f3 | 14 | * or the like. Any license provided herein, whether implied or |
2bd0ea18 NS |
15 | * otherwise, applies only to this software file. Patent licenses, if |
16 | * any, provided herein do not apply to combinations of this program with | |
17 | * other software, or any other product whatsoever. | |
5000d01d | 18 | * |
2bd0ea18 NS |
19 | * You should have received a copy of the GNU General Public License along |
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | |
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | |
5000d01d | 22 | * |
2bd0ea18 NS |
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, |
24 | * Mountain View, CA 94043, or: | |
5000d01d SL |
25 | * |
26 | * http://www.sgi.com | |
27 | * | |
28 | * For further information regarding this notice, see: | |
29 | * | |
2bd0ea18 NS |
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ |
31 | */ | |
32 | ||
33 | #include <xfs.h> | |
34 | ||
dfc130f3 | 35 | |
2bd0ea18 NS |
36 | /* |
37 | * xfs_dir.c | |
38 | * | |
39 | * Provide the external interfaces to manage directories. | |
40 | */ | |
41 | ||
42 | ||
43 | xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot; | |
44 | ||
45 | /* | |
46 | * One-time startup routine called from xfs_init(). | |
47 | */ | |
48 | void | |
49 | xfs_dir_startup(void) | |
50 | { | |
51 | xfs_dir_hash_dot = xfs_da_hashname(".", 1); | |
52 | xfs_dir_hash_dotdot = xfs_da_hashname("..", 2); | |
53 | } | |
54 | ||
55 | /* | |
56 | * Initialize directory-related fields in the mount structure. | |
57 | */ | |
58 | STATIC void | |
59 | xfs_dir_mount(xfs_mount_t *mp) | |
60 | { | |
61 | uint shortcount, leafcount, count; | |
62 | ||
63 | mp->m_dirversion = 1; | |
64 | shortcount = (mp->m_attroffset - (uint)sizeof(xfs_dir_sf_hdr_t)) / | |
65 | (uint)sizeof(xfs_dir_sf_entry_t); | |
66 | leafcount = (XFS_LBSIZE(mp) - (uint)sizeof(xfs_dir_leaf_hdr_t)) / | |
67 | ((uint)sizeof(xfs_dir_leaf_entry_t) + | |
68 | (uint)sizeof(xfs_dir_leaf_name_t)); | |
69 | count = shortcount > leafcount ? shortcount : leafcount; | |
70 | mp->m_dircook_elog = xfs_da_log2_roundup(count + 1); | |
71 | ASSERT(mp->m_dircook_elog <= mp->m_sb.sb_blocklog); | |
6bef826c | 72 | mp->m_dir_node_ents = mp->m_attr_node_ents = |
2bd0ea18 NS |
73 | (XFS_LBSIZE(mp) - (uint)sizeof(xfs_da_node_hdr_t)) / |
74 | (uint)sizeof(xfs_da_node_entry_t); | |
75 | mp->m_dir_magicpct = (XFS_LBSIZE(mp) * 37) / 100; | |
76 | mp->m_dirblksize = mp->m_sb.sb_blocksize; | |
77 | mp->m_dirblkfsbs = 1; | |
78 | } | |
79 | ||
80 | /* | |
81 | * Initialize a directory with its "." and ".." entries. | |
82 | */ | |
83 | STATIC int | |
84 | xfs_dir_init(xfs_trans_t *trans, xfs_inode_t *dir, xfs_inode_t *parent_dir) | |
85 | { | |
86 | xfs_da_args_t args; | |
87 | int error; | |
88 | ||
32181a02 | 89 | memset((char *)&args, 0, sizeof(args)); |
2bd0ea18 NS |
90 | args.dp = dir; |
91 | args.trans = trans; | |
92 | ||
93 | ASSERT((dir->i_d.di_mode & IFMT) == IFDIR); | |
0e266570 | 94 | if ((error = xfs_dir_ino_validate(trans->t_mountp, parent_dir->i_ino))) |
2bd0ea18 NS |
95 | return error; |
96 | ||
97 | return(xfs_dir_shortform_create(&args, parent_dir->i_ino)); | |
98 | } | |
99 | ||
100 | /* | |
101 | * Generic handler routine to add a name to a directory. | |
102 | * Transitions directory from shortform to Btree as necessary. | |
103 | */ | |
104 | STATIC int /* error */ | |
105 | xfs_dir_createname(xfs_trans_t *trans, xfs_inode_t *dp, char *name, | |
106 | int namelen, xfs_ino_t inum, xfs_fsblock_t *firstblock, | |
107 | xfs_bmap_free_t *flist, xfs_extlen_t total) | |
108 | { | |
109 | xfs_da_args_t args; | |
110 | int retval, newsize, done; | |
111 | ||
112 | ASSERT((dp->i_d.di_mode & IFMT) == IFDIR); | |
113 | ||
0e266570 | 114 | if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum))) |
2bd0ea18 NS |
115 | return (retval); |
116 | ||
7a3bffe4 | 117 | XFS_STATS_INC(xfsstats.xs_dir_create); |
2bd0ea18 NS |
118 | /* |
119 | * Fill in the arg structure for this request. | |
120 | */ | |
121 | args.name = name; | |
122 | args.namelen = namelen; | |
123 | args.hashval = xfs_da_hashname(name, namelen); | |
124 | args.inumber = inum; | |
125 | args.dp = dp; | |
126 | args.firstblock = firstblock; | |
127 | args.flist = flist; | |
128 | args.total = total; | |
129 | args.whichfork = XFS_DATA_FORK; | |
130 | args.trans = trans; | |
131 | args.justcheck = 0; | |
132 | args.addname = args.oknoent = 1; | |
133 | ||
134 | /* | |
135 | * Decide on what work routines to call based on the inode size. | |
136 | */ | |
137 | done = 0; | |
138 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | |
139 | newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen); | |
140 | if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp)) { | |
141 | retval = xfs_dir_shortform_addname(&args); | |
142 | done = 1; | |
143 | } else { | |
144 | if (total == 0) | |
145 | return XFS_ERROR(ENOSPC); | |
146 | retval = xfs_dir_shortform_to_leaf(&args); | |
147 | done = retval != 0; | |
148 | } | |
149 | } | |
150 | if (!done && xfs_bmap_one_block(dp, XFS_DATA_FORK)) { | |
151 | retval = xfs_dir_leaf_addname(&args); | |
152 | done = retval != ENOSPC; | |
153 | if (!done) { | |
154 | if (total == 0) | |
155 | return XFS_ERROR(ENOSPC); | |
156 | retval = xfs_dir_leaf_to_node(&args); | |
157 | done = retval != 0; | |
158 | } | |
159 | } | |
160 | if (!done) { | |
161 | retval = xfs_dir_node_addname(&args); | |
162 | } | |
163 | return(retval); | |
164 | } | |
165 | ||
166 | /* | |
167 | * Generic handler routine to remove a name from a directory. | |
168 | * Transitions directory from Btree to shortform as necessary. | |
169 | */ | |
170 | STATIC int /* error */ | |
171 | xfs_dir_removename(xfs_trans_t *trans, xfs_inode_t *dp, char *name, | |
172 | int namelen, xfs_ino_t ino, xfs_fsblock_t *firstblock, | |
173 | xfs_bmap_free_t *flist, xfs_extlen_t total) | |
174 | { | |
175 | xfs_da_args_t args; | |
176 | int count, totallen, newsize, retval; | |
177 | ||
178 | ASSERT((dp->i_d.di_mode & IFMT) == IFDIR); | |
7a3bffe4 | 179 | XFS_STATS_INC(xfsstats.xs_dir_remove); |
2bd0ea18 NS |
180 | /* |
181 | * Fill in the arg structure for this request. | |
182 | */ | |
183 | args.name = name; | |
184 | args.namelen = namelen; | |
185 | args.hashval = xfs_da_hashname(name, namelen); | |
186 | args.inumber = ino; | |
187 | args.dp = dp; | |
188 | args.firstblock = firstblock; | |
189 | args.flist = flist; | |
190 | args.total = total; | |
191 | args.whichfork = XFS_DATA_FORK; | |
192 | args.trans = trans; | |
193 | args.justcheck = args.addname = args.oknoent = 0; | |
194 | ||
195 | /* | |
196 | * Decide on what work routines to call based on the inode size. | |
197 | */ | |
198 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | |
199 | retval = xfs_dir_shortform_removename(&args); | |
200 | } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { | |
201 | retval = xfs_dir_leaf_removename(&args, &count, &totallen); | |
202 | if (retval == 0) { | |
203 | newsize = XFS_DIR_SF_ALLFIT(count, totallen); | |
204 | if (newsize <= XFS_IFORK_DSIZE(dp)) { | |
205 | retval = xfs_dir_leaf_to_shortform(&args); | |
206 | } | |
207 | } | |
208 | } else { | |
209 | retval = xfs_dir_node_removename(&args); | |
210 | } | |
211 | return(retval); | |
212 | } | |
213 | ||
214 | STATIC int /* error */ | |
215 | xfs_dir_lookup(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen, | |
216 | xfs_ino_t *inum) | |
217 | { | |
218 | xfs_da_args_t args; | |
219 | int retval; | |
220 | ||
221 | ASSERT((dp->i_d.di_mode & IFMT) == IFDIR); | |
2bd0ea18 | 222 | |
7a3bffe4 | 223 | XFS_STATS_INC(xfsstats.xs_dir_lookup); |
2bd0ea18 NS |
224 | /* |
225 | * Fill in the arg structure for this request. | |
226 | */ | |
227 | args.name = name; | |
228 | args.namelen = namelen; | |
229 | args.hashval = xfs_da_hashname(name, namelen); | |
230 | args.inumber = 0; | |
231 | args.dp = dp; | |
232 | args.firstblock = NULL; | |
233 | args.flist = NULL; | |
234 | args.total = 0; | |
235 | args.whichfork = XFS_DATA_FORK; | |
236 | args.trans = trans; | |
237 | args.justcheck = args.addname = 0; | |
238 | args.oknoent = 1; | |
239 | ||
240 | /* | |
241 | * Decide on what work routines to call based on the inode size. | |
242 | */ | |
243 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | |
244 | retval = xfs_dir_shortform_lookup(&args); | |
245 | } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { | |
246 | retval = xfs_dir_leaf_lookup(&args); | |
247 | } else { | |
248 | retval = xfs_dir_node_lookup(&args); | |
249 | } | |
250 | if (retval == EEXIST) | |
251 | retval = 0; | |
252 | *inum = args.inumber; | |
253 | return(retval); | |
254 | } | |
255 | ||
256 | STATIC int /* error */ | |
257 | xfs_dir_replace(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen, | |
258 | xfs_ino_t inum, xfs_fsblock_t *firstblock, | |
259 | xfs_bmap_free_t *flist, xfs_extlen_t total) | |
260 | { | |
261 | xfs_da_args_t args; | |
262 | int retval; | |
263 | ||
264 | ASSERT((dp->i_d.di_mode & IFMT) == IFDIR); | |
2bd0ea18 | 265 | |
0e266570 | 266 | if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum))) |
2bd0ea18 NS |
267 | return retval; |
268 | ||
269 | /* | |
270 | * Fill in the arg structure for this request. | |
271 | */ | |
272 | args.name = name; | |
273 | args.namelen = namelen; | |
274 | args.hashval = xfs_da_hashname(name, namelen); | |
275 | args.inumber = inum; | |
276 | args.dp = dp; | |
277 | args.firstblock = firstblock; | |
278 | args.flist = flist; | |
279 | args.total = total; | |
280 | args.whichfork = XFS_DATA_FORK; | |
281 | args.trans = trans; | |
282 | args.justcheck = args.addname = args.oknoent = 0; | |
283 | ||
284 | /* | |
285 | * Decide on what work routines to call based on the inode size. | |
286 | */ | |
287 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | |
288 | retval = xfs_dir_shortform_replace(&args); | |
289 | } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { | |
290 | retval = xfs_dir_leaf_replace(&args); | |
291 | } else { | |
292 | retval = xfs_dir_node_replace(&args); | |
293 | } | |
294 | ||
295 | return(retval); | |
296 | } | |
297 | ||
298 | ||
299 | /*======================================================================== | |
300 | * External routines when dirsize == XFS_LBSIZE(dp->i_mount). | |
301 | *========================================================================*/ | |
302 | ||
303 | /* | |
304 | * Add a name to the leaf directory structure | |
305 | * This is the external routine. | |
306 | */ | |
307 | int | |
308 | xfs_dir_leaf_addname(xfs_da_args_t *args) | |
309 | { | |
310 | int index, retval; | |
311 | xfs_dabuf_t *bp; | |
312 | ||
313 | retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, | |
314 | XFS_DATA_FORK); | |
315 | if (retval) | |
316 | return(retval); | |
317 | ASSERT(bp != NULL); | |
318 | ||
319 | retval = xfs_dir_leaf_lookup_int(bp, args, &index); | |
320 | if (retval == ENOENT) | |
321 | retval = xfs_dir_leaf_add(bp, args, index); | |
322 | xfs_da_buf_done(bp); | |
323 | return(retval); | |
324 | } | |
325 | ||
326 | /* | |
327 | * Remove a name from the leaf directory structure | |
328 | * This is the external routine. | |
329 | */ | |
330 | STATIC int | |
331 | xfs_dir_leaf_removename(xfs_da_args_t *args, int *count, int *totallen) | |
332 | { | |
333 | xfs_dir_leafblock_t *leaf; | |
334 | int index, retval; | |
335 | xfs_dabuf_t *bp; | |
336 | ||
337 | retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, | |
338 | XFS_DATA_FORK); | |
339 | if (retval) | |
340 | return(retval); | |
341 | ASSERT(bp != NULL); | |
342 | leaf = bp->data; | |
343 | ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC); | |
344 | retval = xfs_dir_leaf_lookup_int(bp, args, &index); | |
345 | if (retval == EEXIST) { | |
346 | (void)xfs_dir_leaf_remove(args->trans, bp, index); | |
347 | *count = INT_GET(leaf->hdr.count, ARCH_CONVERT); | |
348 | *totallen = INT_GET(leaf->hdr.namebytes, ARCH_CONVERT); | |
349 | retval = 0; | |
350 | } | |
351 | xfs_da_buf_done(bp); | |
352 | return(retval); | |
353 | } | |
354 | ||
355 | /* | |
356 | * Look up a name in a leaf directory structure. | |
357 | * This is the external routine. | |
358 | */ | |
359 | STATIC int | |
360 | xfs_dir_leaf_lookup(xfs_da_args_t *args) | |
361 | { | |
362 | int index, retval; | |
363 | xfs_dabuf_t *bp; | |
364 | ||
365 | retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, | |
366 | XFS_DATA_FORK); | |
367 | if (retval) | |
368 | return(retval); | |
369 | ASSERT(bp != NULL); | |
370 | retval = xfs_dir_leaf_lookup_int(bp, args, &index); | |
371 | xfs_da_brelse(args->trans, bp); | |
372 | return(retval); | |
373 | } | |
374 | ||
375 | /* | |
376 | * Look up a name in a leaf directory structure, replace the inode number. | |
377 | * This is the external routine. | |
378 | */ | |
379 | STATIC int | |
380 | xfs_dir_leaf_replace(xfs_da_args_t *args) | |
381 | { | |
382 | int index, retval; | |
383 | xfs_dabuf_t *bp; | |
384 | xfs_ino_t inum; | |
385 | xfs_dir_leafblock_t *leaf; | |
386 | xfs_dir_leaf_entry_t *entry; | |
387 | xfs_dir_leaf_name_t *namest; | |
388 | ||
389 | inum = args->inumber; | |
390 | retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, | |
391 | XFS_DATA_FORK); | |
392 | if (retval) | |
393 | return(retval); | |
394 | ASSERT(bp != NULL); | |
395 | retval = xfs_dir_leaf_lookup_int(bp, args, &index); | |
396 | if (retval == EEXIST) { | |
397 | leaf = bp->data; | |
398 | entry = &leaf->entries[index]; | |
399 | namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); | |
5000d01d | 400 | /* XXX - replace assert? */ |
2bd0ea18 | 401 | XFS_DIR_SF_PUT_DIRINO_ARCH(&inum, &namest->inumber, ARCH_CONVERT); |
5000d01d | 402 | xfs_da_log_buf(args->trans, bp, |
2bd0ea18 NS |
403 | XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber))); |
404 | xfs_da_buf_done(bp); | |
405 | retval = 0; | |
406 | } else | |
407 | xfs_da_brelse(args->trans, bp); | |
408 | return(retval); | |
409 | } | |
410 | ||
411 | ||
412 | /*======================================================================== | |
413 | * External routines when dirsize > XFS_LBSIZE(mp). | |
414 | *========================================================================*/ | |
415 | ||
416 | /* | |
417 | * Add a name to a Btree-format directory. | |
418 | * | |
419 | * This will involve walking down the Btree, and may involve splitting | |
420 | * leaf nodes and even splitting intermediate nodes up to and including | |
421 | * the root node (a special case of an intermediate node). | |
422 | */ | |
423 | STATIC int | |
424 | xfs_dir_node_addname(xfs_da_args_t *args) | |
425 | { | |
426 | xfs_da_state_t *state; | |
427 | xfs_da_state_blk_t *blk; | |
428 | int retval, error; | |
429 | ||
430 | /* | |
431 | * Fill in bucket of arguments/results/context to carry around. | |
432 | */ | |
433 | state = xfs_da_state_alloc(); | |
434 | state->args = args; | |
435 | state->mp = args->dp->i_mount; | |
436 | state->blocksize = state->mp->m_sb.sb_blocksize; | |
6bef826c | 437 | state->node_ents = state->mp->m_dir_node_ents; |
2bd0ea18 NS |
438 | |
439 | /* | |
440 | * Search to see if name already exists, and get back a pointer | |
441 | * to where it should go. | |
442 | */ | |
443 | error = xfs_da_node_lookup_int(state, &retval); | |
444 | if (error) | |
445 | retval = error; | |
446 | if (retval != ENOENT) | |
447 | goto error; | |
448 | blk = &state->path.blk[ state->path.active-1 ]; | |
449 | ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); | |
450 | retval = xfs_dir_leaf_add(blk->bp, args, blk->index); | |
451 | if (retval == 0) { | |
452 | /* | |
453 | * Addition succeeded, update Btree hashvals. | |
454 | */ | |
455 | if (!args->justcheck) | |
456 | xfs_da_fixhashpath(state, &state->path); | |
457 | } else { | |
458 | /* | |
459 | * Addition failed, split as many Btree elements as required. | |
460 | */ | |
461 | if (args->total == 0) { | |
462 | ASSERT(retval == ENOSPC); | |
463 | goto error; | |
464 | } | |
465 | retval = xfs_da_split(state); | |
466 | } | |
467 | error: | |
468 | xfs_da_state_free(state); | |
469 | ||
470 | return(retval); | |
471 | } | |
472 | ||
473 | /* | |
474 | * Remove a name from a B-tree directory. | |
475 | * | |
476 | * This will involve walking down the Btree, and may involve joining | |
477 | * leaf nodes and even joining intermediate nodes up to and including | |
478 | * the root node (a special case of an intermediate node). | |
479 | */ | |
480 | STATIC int | |
481 | xfs_dir_node_removename(xfs_da_args_t *args) | |
482 | { | |
483 | xfs_da_state_t *state; | |
484 | xfs_da_state_blk_t *blk; | |
485 | int retval, error; | |
486 | ||
487 | state = xfs_da_state_alloc(); | |
488 | state->args = args; | |
489 | state->mp = args->dp->i_mount; | |
490 | state->blocksize = state->mp->m_sb.sb_blocksize; | |
6bef826c | 491 | state->node_ents = state->mp->m_dir_node_ents; |
2bd0ea18 NS |
492 | |
493 | /* | |
494 | * Search to see if name exists, and get back a pointer to it. | |
495 | */ | |
496 | error = xfs_da_node_lookup_int(state, &retval); | |
497 | if (error) | |
498 | retval = error; | |
499 | if (retval != EEXIST) { | |
500 | xfs_da_state_free(state); | |
501 | return(retval); | |
502 | } | |
503 | ||
504 | /* | |
505 | * Remove the name and update the hashvals in the tree. | |
506 | */ | |
507 | blk = &state->path.blk[ state->path.active-1 ]; | |
508 | ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); | |
509 | retval = xfs_dir_leaf_remove(args->trans, blk->bp, blk->index); | |
510 | xfs_da_fixhashpath(state, &state->path); | |
511 | ||
512 | /* | |
513 | * Check to see if the tree needs to be collapsed. | |
514 | */ | |
515 | error = 0; | |
516 | if (retval) { | |
517 | error = xfs_da_join(state); | |
518 | } | |
519 | ||
520 | xfs_da_state_free(state); | |
521 | if (error) | |
522 | return(error); | |
523 | return(0); | |
524 | } | |
525 | ||
526 | /* | |
527 | * Look up a filename in a int directory. | |
528 | * Use an internal routine to actually do all the work. | |
529 | */ | |
530 | STATIC int | |
531 | xfs_dir_node_lookup(xfs_da_args_t *args) | |
532 | { | |
533 | xfs_da_state_t *state; | |
534 | int retval, error, i; | |
535 | ||
536 | state = xfs_da_state_alloc(); | |
537 | state->args = args; | |
538 | state->mp = args->dp->i_mount; | |
539 | state->blocksize = state->mp->m_sb.sb_blocksize; | |
6bef826c | 540 | state->node_ents = state->mp->m_dir_node_ents; |
2bd0ea18 NS |
541 | |
542 | /* | |
543 | * Search to see if name exists, | |
544 | * and get back a pointer to it. | |
545 | */ | |
546 | error = xfs_da_node_lookup_int(state, &retval); | |
547 | if (error) { | |
548 | retval = error; | |
549 | } | |
550 | ||
5000d01d | 551 | /* |
2bd0ea18 NS |
552 | * If not in a transaction, we have to release all the buffers. |
553 | */ | |
554 | for (i = 0; i < state->path.active; i++) { | |
555 | xfs_da_brelse(args->trans, state->path.blk[i].bp); | |
556 | state->path.blk[i].bp = NULL; | |
557 | } | |
558 | ||
559 | xfs_da_state_free(state); | |
560 | return(retval); | |
561 | } | |
562 | ||
563 | /* | |
564 | * Look up a filename in an int directory, replace the inode number. | |
565 | * Use an internal routine to actually do the lookup. | |
566 | */ | |
567 | STATIC int | |
568 | xfs_dir_node_replace(xfs_da_args_t *args) | |
569 | { | |
570 | xfs_da_state_t *state; | |
571 | xfs_da_state_blk_t *blk; | |
572 | xfs_dir_leafblock_t *leaf; | |
573 | xfs_dir_leaf_entry_t *entry; | |
574 | xfs_dir_leaf_name_t *namest; | |
575 | xfs_ino_t inum; | |
576 | int retval, error, i; | |
577 | xfs_dabuf_t *bp; | |
578 | ||
579 | state = xfs_da_state_alloc(); | |
580 | state->args = args; | |
581 | state->mp = args->dp->i_mount; | |
582 | state->blocksize = state->mp->m_sb.sb_blocksize; | |
6bef826c | 583 | state->node_ents = state->mp->m_dir_node_ents; |
2bd0ea18 NS |
584 | inum = args->inumber; |
585 | ||
586 | /* | |
587 | * Search to see if name exists, | |
588 | * and get back a pointer to it. | |
589 | */ | |
590 | error = xfs_da_node_lookup_int(state, &retval); | |
591 | if (error) { | |
592 | retval = error; | |
593 | } | |
594 | ||
595 | if (retval == EEXIST) { | |
596 | blk = &state->path.blk[state->path.active - 1]; | |
597 | ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); | |
598 | bp = blk->bp; | |
599 | leaf = bp->data; | |
600 | entry = &leaf->entries[blk->index]; | |
601 | namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); | |
5000d01d | 602 | /* XXX - replace assert ? */ |
2bd0ea18 NS |
603 | XFS_DIR_SF_PUT_DIRINO_ARCH(&inum, &namest->inumber, ARCH_CONVERT); |
604 | xfs_da_log_buf(args->trans, bp, | |
605 | XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber))); | |
606 | xfs_da_buf_done(bp); | |
607 | blk->bp = NULL; | |
608 | retval = 0; | |
609 | } else { | |
610 | i = state->path.active - 1; | |
611 | xfs_da_brelse(args->trans, state->path.blk[i].bp); | |
612 | state->path.blk[i].bp = NULL; | |
613 | } | |
614 | for (i = 0; i < state->path.active - 1; i++) { | |
615 | xfs_da_brelse(args->trans, state->path.blk[i].bp); | |
616 | state->path.blk[i].bp = NULL; | |
617 | } | |
618 | ||
619 | xfs_da_state_free(state); | |
620 | return(retval); | |
621 | } |