]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libxfs/xfs_dir.c
Merge whitespace changes over
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_dir.c
CommitLineData
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
43xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot;
44
45/*
46 * One-time startup routine called from xfs_init().
47 */
48void
49xfs_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 */
58STATIC void
59xfs_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 */
83STATIC int
84xfs_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 */
104STATIC int /* error */
105xfs_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 */
170STATIC int /* error */
171xfs_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
214STATIC int /* error */
215xfs_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
256STATIC int /* error */
257xfs_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 */
307int
308xfs_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 */
330STATIC int
331xfs_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 */
359STATIC int
360xfs_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 */
379STATIC int
380xfs_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 */
423STATIC int
424xfs_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 }
467error:
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 */
480STATIC int
481xfs_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 */
530STATIC int
531xfs_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 */
567STATIC int
568xfs_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}