]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - libxfs/xfs_dir2.c
2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
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.
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.
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.
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.
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
28 * For further information regarding this notice, see:
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
34 * XFS v2 directory implmentation.
35 * Top-level and utility routines.
42 * Initialize directory-related fields in the mount structure.
46 xfs_mount_t
*mp
) /* filesystem mount point */
49 ASSERT((1 << (mp
->m_sb
.sb_blocklog
+ mp
->m_sb
.sb_dirblklog
)) <=
51 mp
->m_dirblksize
= 1 << (mp
->m_sb
.sb_blocklog
+ mp
->m_sb
.sb_dirblklog
);
52 mp
->m_dirblkfsbs
= 1 << mp
->m_sb
.sb_dirblklog
;
53 mp
->m_dirdatablk
= XFS_DIR2_DB_TO_DA(mp
, XFS_DIR2_DATA_FIRSTDB(mp
));
54 mp
->m_dirleafblk
= XFS_DIR2_DB_TO_DA(mp
, XFS_DIR2_LEAF_FIRSTDB(mp
));
55 mp
->m_dirfreeblk
= XFS_DIR2_DB_TO_DA(mp
, XFS_DIR2_FREE_FIRSTDB(mp
));
57 (mp
->m_dirblksize
- (uint
)sizeof(xfs_da_node_hdr_t
)) /
58 (uint
)sizeof(xfs_da_node_entry_t
);
59 mp
->m_dir_magicpct
= (mp
->m_dirblksize
* 37) / 100;
63 * Initialize a directory with its "." and ".." entries.
67 xfs_trans_t
*tp
, /* transaction pointer */
68 xfs_inode_t
*dp
, /* incore directory inode */
69 xfs_inode_t
*pdp
) /* incore parent directory inode */
71 xfs_da_args_t args
; /* operation arguments */
72 int error
; /* error return value */
74 bzero((char *)&args
, sizeof(args
));
77 ASSERT((dp
->i_d
.di_mode
& IFMT
) == IFDIR
);
78 if ((error
= xfs_dir_ino_validate(tp
->t_mountp
, pdp
->i_ino
))) {
81 return xfs_dir2_sf_create(&args
, pdp
->i_ino
);
85 Enter a name in a directory.
87 STATIC
int /* error */
89 xfs_trans_t
*tp
, /* transaction pointer */
90 xfs_inode_t
*dp
, /* incore directory inode */
91 char *name
, /* new entry name */
92 int namelen
, /* new entry name length */
93 xfs_ino_t inum
, /* new entry inode number */
94 xfs_fsblock_t
*first
, /* bmap's firstblock */
95 xfs_bmap_free_t
*flist
, /* bmap's freeblock list */
96 xfs_extlen_t total
) /* bmap's total block count */
98 xfs_da_args_t args
; /* operation arguments */
99 int rval
; /* return value */
100 int v
; /* type-checking value */
102 ASSERT((dp
->i_d
.di_mode
& IFMT
) == IFDIR
);
103 if ((rval
= xfs_dir_ino_validate(tp
->t_mountp
, inum
))) {
106 XFS_STATS_INC(xs_dir_create
);
108 * Fill in the arg structure for this request.
111 args
.namelen
= namelen
;
112 args
.hashval
= xfs_da_hashname(name
, namelen
);
115 args
.firstblock
= first
;
118 args
.whichfork
= XFS_DATA_FORK
;
121 args
.addname
= args
.oknoent
= 1;
123 * Decide on what work routines to call based on the inode size.
125 if (dp
->i_d
.di_format
== XFS_DINODE_FMT_LOCAL
)
126 rval
= xfs_dir2_sf_addname(&args
);
127 else if ((rval
= xfs_dir2_isblock(tp
, dp
, &v
))) {
130 rval
= xfs_dir2_block_addname(&args
);
131 else if ((rval
= xfs_dir2_isleaf(tp
, dp
, &v
))) {
134 rval
= xfs_dir2_leaf_addname(&args
);
136 rval
= xfs_dir2_node_addname(&args
);
141 * Lookup a name in a directory, give back the inode number.
143 STATIC
int /* error */
145 xfs_trans_t
*tp
, /* transaction pointer */
146 xfs_inode_t
*dp
, /* incore directory inode */
147 char *name
, /* lookup name */
148 int namelen
, /* lookup name length */
149 xfs_ino_t
*inum
) /* out: inode number */
151 xfs_da_args_t args
; /* operation arguments */
152 int rval
; /* return value */
153 int v
; /* type-checking value */
155 ASSERT((dp
->i_d
.di_mode
& IFMT
) == IFDIR
);
156 if (namelen
>= MAXNAMELEN
) {
157 return XFS_ERROR(EINVAL
);
159 XFS_STATS_INC(xs_dir_lookup
);
161 * Fill in the arg structure for this request.
164 args
.namelen
= namelen
;
165 args
.hashval
= xfs_da_hashname(name
, namelen
);
168 args
.firstblock
= NULL
;
171 args
.whichfork
= XFS_DATA_FORK
;
173 args
.justcheck
= args
.addname
= 0;
176 * Decide on what work routines to call based on the inode size.
178 if (dp
->i_d
.di_format
== XFS_DINODE_FMT_LOCAL
)
179 rval
= xfs_dir2_sf_lookup(&args
);
180 else if ((rval
= xfs_dir2_isblock(tp
, dp
, &v
))) {
183 rval
= xfs_dir2_block_lookup(&args
);
184 else if ((rval
= xfs_dir2_isleaf(tp
, dp
, &v
))) {
187 rval
= xfs_dir2_leaf_lookup(&args
);
189 rval
= xfs_dir2_node_lookup(&args
);
193 *inum
= args
.inumber
;
198 * Remove an entry from a directory.
200 STATIC
int /* error */
202 xfs_trans_t
*tp
, /* transaction pointer */
203 xfs_inode_t
*dp
, /* incore directory inode */
204 char *name
, /* name of entry to remove */
205 int namelen
, /* name length of entry to remove */
206 xfs_ino_t ino
, /* inode number of entry to remove */
207 xfs_fsblock_t
*first
, /* bmap's firstblock */
208 xfs_bmap_free_t
*flist
, /* bmap's freeblock list */
209 xfs_extlen_t total
) /* bmap's total block count */
211 xfs_da_args_t args
; /* operation arguments */
212 int rval
; /* return value */
213 int v
; /* type-checking value */
215 ASSERT((dp
->i_d
.di_mode
& IFMT
) == IFDIR
);
216 XFS_STATS_INC(xs_dir_remove
);
218 * Fill in the arg structure for this request.
221 args
.namelen
= namelen
;
222 args
.hashval
= xfs_da_hashname(name
, namelen
);
225 args
.firstblock
= first
;
228 args
.whichfork
= XFS_DATA_FORK
;
230 args
.justcheck
= args
.addname
= args
.oknoent
= 0;
232 * Decide on what work routines to call based on the inode size.
234 if (dp
->i_d
.di_format
== XFS_DINODE_FMT_LOCAL
)
235 rval
= xfs_dir2_sf_removename(&args
);
236 else if ((rval
= xfs_dir2_isblock(tp
, dp
, &v
))) {
239 rval
= xfs_dir2_block_removename(&args
);
240 else if ((rval
= xfs_dir2_isleaf(tp
, dp
, &v
))) {
243 rval
= xfs_dir2_leaf_removename(&args
);
245 rval
= xfs_dir2_node_removename(&args
);
250 * Replace the inode number of a directory entry.
252 STATIC
int /* error */
254 xfs_trans_t
*tp
, /* transaction pointer */
255 xfs_inode_t
*dp
, /* incore directory inode */
256 char *name
, /* name of entry to replace */
257 int namelen
, /* name length of entry to replace */
258 xfs_ino_t inum
, /* new inode number */
259 xfs_fsblock_t
*first
, /* bmap's firstblock */
260 xfs_bmap_free_t
*flist
, /* bmap's freeblock list */
261 xfs_extlen_t total
) /* bmap's total block count */
263 xfs_da_args_t args
; /* operation arguments */
264 int rval
; /* return value */
265 int v
; /* type-checking value */
267 ASSERT((dp
->i_d
.di_mode
& IFMT
) == IFDIR
);
268 if (namelen
>= MAXNAMELEN
) {
269 return XFS_ERROR(EINVAL
);
271 if ((rval
= xfs_dir_ino_validate(tp
->t_mountp
, inum
))) {
275 * Fill in the arg structure for this request.
278 args
.namelen
= namelen
;
279 args
.hashval
= xfs_da_hashname(name
, namelen
);
282 args
.firstblock
= first
;
285 args
.whichfork
= XFS_DATA_FORK
;
287 args
.justcheck
= args
.addname
= args
.oknoent
= 0;
289 * Decide on what work routines to call based on the inode size.
291 if (dp
->i_d
.di_format
== XFS_DINODE_FMT_LOCAL
)
292 rval
= xfs_dir2_sf_replace(&args
);
293 else if ((rval
= xfs_dir2_isblock(tp
, dp
, &v
))) {
296 rval
= xfs_dir2_block_replace(&args
);
297 else if ((rval
= xfs_dir2_isleaf(tp
, dp
, &v
))) {
300 rval
= xfs_dir2_leaf_replace(&args
);
302 rval
= xfs_dir2_node_replace(&args
);
311 * Add a block to the directory.
312 * This routine is for data and free blocks, not leaf/node blocks
313 * which are handled by xfs_da_grow_inode.
317 xfs_da_args_t
*args
, /* operation arguments */
318 int space
, /* v2 dir's space XFS_DIR2_xxx_SPACE */
319 xfs_dir2_db_t
*dbp
) /* out: block number added */
321 xfs_fileoff_t bno
; /* directory offset of new block */
322 int count
; /* count of filesystem blocks */
323 xfs_inode_t
*dp
; /* incore directory inode */
324 int error
; /* error return value */
325 int got
; /* blocks actually mapped */
326 int i
; /* temp mapping index */
327 xfs_bmbt_irec_t map
; /* single structure for bmap */
328 int mapi
; /* mapping index */
329 xfs_bmbt_irec_t
*mapp
; /* bmap mapping structure(s) */
330 xfs_mount_t
*mp
; /* filesystem mount point */
331 int nmap
; /* number of bmap entries */
332 xfs_trans_t
*tp
; /* transaction pointer */
334 xfs_dir2_trace_args_s("grow_inode", args
, space
);
339 * Set lowest possible block in the space requested.
341 bno
= XFS_B_TO_FSBT(mp
, space
* XFS_DIR2_SPACE_SIZE
);
342 count
= mp
->m_dirblkfsbs
;
344 * Find the first hole for our block.
346 if ((error
= xfs_bmap_first_unused(tp
, dp
, count
, &bno
, XFS_DATA_FORK
))) {
350 ASSERT(args
->firstblock
!= NULL
);
352 * Try mapping the new block contiguously (one extent).
354 if ((error
= xfs_bmapi(tp
, dp
, bno
, count
,
355 XFS_BMAPI_WRITE
|XFS_BMAPI_METADATA
|XFS_BMAPI_CONTIG
,
356 args
->firstblock
, args
->total
, &map
, &nmap
,
369 * Didn't work and this is a multiple-fsb directory block.
370 * Try again with contiguous flag turned on.
372 else if (nmap
== 0 && count
> 1) {
373 xfs_fileoff_t b
; /* current file offset */
376 * Space for maximum number of mappings.
378 mapp
= kmem_alloc(sizeof(*mapp
) * count
, KM_SLEEP
);
380 * Iterate until we get to the end of our block.
382 for (b
= bno
, mapi
= 0; b
< bno
+ count
; ) {
383 int c
; /* current fsb count */
386 * Can't map more than MAX_NMAP at once.
388 nmap
= MIN(XFS_BMAP_MAX_NMAP
, count
);
389 c
= (int)(bno
+ count
- b
);
390 if ((error
= xfs_bmapi(tp
, dp
, b
, c
,
391 XFS_BMAPI_WRITE
|XFS_BMAPI_METADATA
,
392 args
->firstblock
, args
->total
,
393 &mapp
[mapi
], &nmap
, args
->flist
))) {
394 kmem_free(mapp
, sizeof(*mapp
) * count
);
400 * Add this bunch into our table, go to the next offset.
403 b
= mapp
[mapi
- 1].br_startoff
+
404 mapp
[mapi
- 1].br_blockcount
;
415 * See how many fsb's we got.
417 for (i
= 0, got
= 0; i
< mapi
; i
++)
418 got
+= mapp
[i
].br_blockcount
;
420 * Didn't get enough fsb's, or the first/last block's are wrong.
422 if (got
!= count
|| mapp
[0].br_startoff
!= bno
||
423 mapp
[mapi
- 1].br_startoff
+ mapp
[mapi
- 1].br_blockcount
!=
426 kmem_free(mapp
, sizeof(*mapp
) * count
);
427 return XFS_ERROR(ENOSPC
);
430 * Done with the temporary mapping table.
433 kmem_free(mapp
, sizeof(*mapp
) * count
);
434 *dbp
= XFS_DIR2_DA_TO_DB(mp
, (xfs_dablk_t
)bno
);
436 * Update file's size if this is the data space and it grew.
438 if (space
== XFS_DIR2_DATA_SPACE
) {
439 xfs_fsize_t size
; /* directory file (data) size */
441 size
= XFS_FSB_TO_B(mp
, bno
+ count
);
442 if (size
> dp
->i_d
.di_size
) {
443 dp
->i_d
.di_size
= size
;
444 xfs_trans_log_inode(tp
, dp
, XFS_ILOG_CORE
);
451 * See if the directory is a single-block form directory.
455 xfs_trans_t
*tp
, /* transaction pointer */
456 xfs_inode_t
*dp
, /* incore directory inode */
457 int *vp
) /* out: 1 is block, 0 is not block */
459 xfs_fileoff_t last
; /* last file offset */
460 xfs_mount_t
*mp
; /* filesystem mount point */
461 int rval
; /* return value */
464 if ((rval
= xfs_bmap_last_offset(tp
, dp
, &last
, XFS_DATA_FORK
))) {
467 rval
= XFS_FSB_TO_B(mp
, last
) == mp
->m_dirblksize
;
468 ASSERT(rval
== 0 || dp
->i_d
.di_size
== mp
->m_dirblksize
);
474 * See if the directory is a single-leaf form directory.
478 xfs_trans_t
*tp
, /* transaction pointer */
479 xfs_inode_t
*dp
, /* incore directory inode */
480 int *vp
) /* out: 1 is leaf, 0 is not leaf */
482 xfs_fileoff_t last
; /* last file offset */
483 xfs_mount_t
*mp
; /* filesystem mount point */
484 int rval
; /* return value */
487 if ((rval
= xfs_bmap_last_offset(tp
, dp
, &last
, XFS_DATA_FORK
))) {
490 *vp
= last
== mp
->m_dirleafblk
+ (1 << mp
->m_sb
.sb_dirblklog
);
495 * Remove the given block from the directory.
496 * This routine is used for data and free blocks, leaf/node are done
497 * by xfs_da_shrink_inode.
500 xfs_dir2_shrink_inode(
501 xfs_da_args_t
*args
, /* operation arguments */
502 xfs_dir2_db_t db
, /* directory block number */
503 xfs_dabuf_t
*bp
) /* block's buffer */
505 xfs_fileoff_t bno
; /* directory file offset */
506 xfs_dablk_t da
; /* directory file offset */
507 int done
; /* bunmap is finished */
508 xfs_inode_t
*dp
; /* incore directory inode */
509 int error
; /* error return value */
510 xfs_mount_t
*mp
; /* filesystem mount point */
511 xfs_trans_t
*tp
; /* transaction pointer */
513 xfs_dir2_trace_args_db("shrink_inode", args
, db
, bp
);
517 da
= XFS_DIR2_DB_TO_DA(mp
, db
);
519 * Unmap the fsblock(s).
521 if ((error
= xfs_bunmapi(tp
, dp
, da
, mp
->m_dirblkfsbs
,
522 XFS_BMAPI_METADATA
, 0, args
->firstblock
, args
->flist
,
525 * ENOSPC actually can happen if we're in a removename with
526 * no space reservation, and the resulting block removal
527 * would cause a bmap btree split or conversion from extents
528 * to btree. This can only happen for un-fragmented
529 * directory blocks, since you need to be punching out
530 * the middle of an extent.
531 * In this case we need to leave the block in the file,
533 * So the block has to be in a consistent empty state
534 * and appropriately logged.
535 * We don't free up the buffer, the caller can tell it
536 * hasn't happened since it got an error back.
542 * Invalidate the buffer from the transaction.
544 xfs_da_binval(tp
, bp
);
546 * If it's not a data block, we're done.
548 if (db
>= XFS_DIR2_LEAF_FIRSTDB(mp
))
551 * If the block isn't the last one in the directory, we're done.
553 if (dp
->i_d
.di_size
> XFS_DIR2_DB_OFF_TO_BYTE(mp
, db
+ 1, 0))
556 if ((error
= xfs_bmap_last_before(tp
, dp
, &bno
, XFS_DATA_FORK
))) {
558 * This can't really happen unless there's kernel corruption.
562 if (db
== mp
->m_dirdatablk
)
567 * Set the size to the new last block.
569 dp
->i_d
.di_size
= XFS_FSB_TO_B(mp
, bno
);
570 xfs_trans_log_inode(tp
, dp
, XFS_ILOG_CORE
);