]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libxfs/xfs_dir.c
Update copyright dates (again)
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_dir.c
CommitLineData
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
42xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot;
43
44/*
45 * One-time startup routine called from xfs_init().
46 */
47void
48xfs_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 */
57STATIC void
58xfs_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 */
82STATIC int
83xfs_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 */
103STATIC int /* error */
104xfs_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 */
169STATIC int /* error */
170xfs_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
213STATIC int /* error */
214xfs_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
258STATIC int /* error */
259xfs_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 */
312int
313xfs_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 */
335STATIC int
336xfs_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 */
364STATIC int
365xfs_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 */
384STATIC int
385xfs_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 */
428STATIC int
429xfs_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 }
471error:
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 */
484STATIC int
485xfs_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 */
533STATIC int
534xfs_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 */
569STATIC int
570xfs_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}