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