]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libxfs/xfs_dir2.c
libxfs: refactor manage_zones()
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_dir2.c
CommitLineData
37b3b4d6 1// SPDX-License-Identifier: GPL-2.0
2bd0ea18 2/*
da23017d
NS
3 * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
2bd0ea18 5 */
9c799827 6#include "libxfs_priv.h"
b626fb59
DC
7#include "xfs_fs.h"
8#include "xfs_format.h"
9#include "xfs_log_format.h"
10#include "xfs_trans_resv.h"
11#include "xfs_mount.h"
f944d3d0 12#include "xfs_defer.h"
b626fb59
DC
13#include "xfs_da_format.h"
14#include "xfs_da_btree.h"
15#include "xfs_inode.h"
16#include "xfs_trans.h"
17#include "xfs_bmap.h"
18#include "xfs_dir2.h"
19#include "xfs_dir2_priv.h"
be3bf25c 20#include "xfs_ialloc.h"
56d3fc2b 21#include "xfs_errortag.h"
b626fb59 22#include "xfs_trace.h"
2bd0ea18 23
494434d7
DC
24struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR };
25
aaca101b 26/*
3297e0ca 27 * Convert inode mode to directory entry filetype
aaca101b 28 */
16c101bb
DW
29unsigned char
30xfs_mode_to_ftype(
31 int mode)
3297e0ca
AG
32{
33 switch (mode & S_IFMT) {
34 case S_IFREG:
35 return XFS_DIR3_FT_REG_FILE;
36 case S_IFDIR:
37 return XFS_DIR3_FT_DIR;
38 case S_IFCHR:
39 return XFS_DIR3_FT_CHRDEV;
40 case S_IFBLK:
41 return XFS_DIR3_FT_BLKDEV;
42 case S_IFIFO:
43 return XFS_DIR3_FT_FIFO;
44 case S_IFSOCK:
45 return XFS_DIR3_FT_SOCK;
46 case S_IFLNK:
47 return XFS_DIR3_FT_SYMLINK;
48 default:
49 return XFS_DIR3_FT_UNKNOWN;
50 }
51}
51ca7008
BN
52
53/*
5e656dbb
BN
54 * ASCII case-insensitive (ie. A-Z) support for directories that was
55 * used in IRIX.
51ca7008 56 */
5e656dbb 57STATIC xfs_dahash_t
51ca7008 58xfs_ascii_ci_hashname(
5e656dbb 59 struct xfs_name *name)
51ca7008
BN
60{
61 xfs_dahash_t hash;
62 int i;
63
5e656dbb
BN
64 for (i = 0, hash = 0; i < name->len; i++)
65 hash = tolower(name->name[i]) ^ rol32(hash, 7);
51ca7008
BN
66
67 return hash;
68}
69
5e656dbb 70STATIC enum xfs_dacmp
51ca7008 71xfs_ascii_ci_compname(
5e656dbb 72 struct xfs_da_args *args,
56b2de80
DC
73 const unsigned char *name,
74 int len)
51ca7008
BN
75{
76 enum xfs_dacmp result;
77 int i;
78
5e656dbb 79 if (args->namelen != len)
51ca7008
BN
80 return XFS_CMP_DIFFERENT;
81
82 result = XFS_CMP_EXACT;
5e656dbb
BN
83 for (i = 0; i < len; i++) {
84 if (args->name[i] == name[i])
51ca7008 85 continue;
5e656dbb 86 if (tolower(args->name[i]) != tolower(name[i]))
51ca7008
BN
87 return XFS_CMP_DIFFERENT;
88 result = XFS_CMP_CASE;
89 }
90
91 return result;
92}
93
b6b86724 94static const struct xfs_nameops xfs_ascii_ci_nameops = {
51ca7008
BN
95 .hashname = xfs_ascii_ci_hashname,
96 .compname = xfs_ascii_ci_compname,
97};
98
ff105f75
DC
99int
100xfs_da_mount(
101 struct xfs_mount *mp)
2bd0ea18 102{
ff105f75
DC
103 struct xfs_da_geometry *dageo;
104 int nodehdr_size;
ed59338e
DC
105
106
ff105f75 107 ASSERT(mp->m_sb.sb_versionnum & XFS_SB_VERSION_DIRV2BIT);
fdef0e8b 108 ASSERT(xfs_dir2_dirblock_bytes(&mp->m_sb) <= XFS_MAX_BLOCKSIZE);
ff105f75
DC
109
110 mp->m_dir_inode_ops = xfs_dir_get_ops(mp, NULL);
111 mp->m_nondir_inode_ops = xfs_nondir_get_ops(mp, NULL);
112
113 nodehdr_size = mp->m_dir_inode_ops->node_hdr_size;
114 mp->m_dir_geo = kmem_zalloc(sizeof(struct xfs_da_geometry),
115 KM_SLEEP | KM_MAYFAIL);
116 mp->m_attr_geo = kmem_zalloc(sizeof(struct xfs_da_geometry),
117 KM_SLEEP | KM_MAYFAIL);
118 if (!mp->m_dir_geo || !mp->m_attr_geo) {
119 kmem_free(mp->m_dir_geo);
120 kmem_free(mp->m_attr_geo);
12b53197 121 return -ENOMEM;
ff105f75
DC
122 }
123
124 /* set up directory geometry */
125 dageo = mp->m_dir_geo;
126 dageo->blklog = mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog;
127 dageo->fsblog = mp->m_sb.sb_blocklog;
fdef0e8b 128 dageo->blksize = xfs_dir2_dirblock_bytes(&mp->m_sb);
ff105f75
DC
129 dageo->fsbcount = 1 << mp->m_sb.sb_dirblklog;
130
131 /*
132 * Now we've set up the block conversion variables, we can calculate the
133 * segment block constants using the geometry structure.
134 */
135 dageo->datablk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_DATA_OFFSET);
136 dageo->leafblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_LEAF_OFFSET);
137 dageo->freeblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_FREE_OFFSET);
138 dageo->node_ents = (dageo->blksize - nodehdr_size) /
ed59338e 139 (uint)sizeof(xfs_da_node_entry_t);
ff105f75
DC
140 dageo->magicpct = (dageo->blksize * 37) / 100;
141
142 /* set up attribute geometry - single fsb only */
143 dageo = mp->m_attr_geo;
144 dageo->blklog = mp->m_sb.sb_blocklog;
145 dageo->fsblog = mp->m_sb.sb_blocklog;
146 dageo->blksize = 1 << dageo->blklog;
147 dageo->fsbcount = 1;
148 dageo->node_ents = (dageo->blksize - nodehdr_size) /
ed59338e 149 (uint)sizeof(xfs_da_node_entry_t);
ff105f75 150 dageo->magicpct = (dageo->blksize * 37) / 100;
ed59338e 151
51ca7008
BN
152 if (xfs_sb_version_hasasciici(&mp->m_sb))
153 mp->m_dirnameops = &xfs_ascii_ci_nameops;
154 else
155 mp->m_dirnameops = &xfs_default_nameops;
ff105f75
DC
156
157 return 0;
158}
159
160void
161xfs_da_unmount(
162 struct xfs_mount *mp)
163{
164 kmem_free(mp->m_dir_geo);
165 kmem_free(mp->m_attr_geo);
2bd0ea18
NS
166}
167
5e656dbb
BN
168/*
169 * Return 1 if directory contains only "." and "..".
170 */
171int
172xfs_dir_isempty(
173 xfs_inode_t *dp)
174{
a2ceac1f 175 xfs_dir2_sf_hdr_t *sfp;
5e656dbb 176
e37bf53c 177 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
5e656dbb
BN
178 if (dp->i_d.di_size == 0) /* might happen during shutdown. */
179 return 1;
180 if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
181 return 0;
a2ceac1f
DC
182 sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
183 return !sfp->count;
5e656dbb
BN
184}
185
186/*
187 * Validate a given inode number.
188 */
189int
190xfs_dir_ino_validate(
191 xfs_mount_t *mp,
192 xfs_ino_t ino)
193{
be3bf25c
DW
194 bool ino_ok = xfs_verify_dir_ino(mp, ino);
195
e2a190dd 196 if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE))) {
a2ceac1f 197 xfs_warn(mp, "Invalid inode number 0x%Lx",
5e656dbb
BN
198 (unsigned long long) ino);
199 XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp);
12b53197 200 return -EFSCORRUPTED;
5e656dbb
BN
201 }
202 return 0;
203}
204
2bd0ea18
NS
205/*
206 * Initialize a directory with its "." and ".." entries.
207 */
5e656dbb
BN
208int
209xfs_dir_init(
210 xfs_trans_t *tp,
211 xfs_inode_t *dp,
212 xfs_inode_t *pdp)
2bd0ea18 213{
ff105f75 214 struct xfs_da_args *args;
5e656dbb 215 int error;
2bd0ea18 216
e37bf53c 217 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
ff105f75
DC
218 error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino);
219 if (error)
2bd0ea18 220 return error;
ff105f75
DC
221
222 args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
223 if (!args)
12b53197 224 return -ENOMEM;
ff105f75
DC
225
226 args->geo = dp->i_mount->m_dir_geo;
227 args->dp = dp;
228 args->trans = tp;
229 error = xfs_dir2_sf_create(args, pdp->i_ino);
230 kmem_free(args);
231 return error;
2bd0ea18
NS
232}
233
234/*
5a35bf2c
DC
235 * Enter a name in a directory, or check for available space.
236 * If inum is 0, only the available space test is performed.
2bd0ea18 237 */
5e656dbb
BN
238int
239xfs_dir_createname(
a50d2ab0
BF
240 struct xfs_trans *tp,
241 struct xfs_inode *dp,
5e656dbb 242 struct xfs_name *name,
2bd0ea18 243 xfs_ino_t inum, /* new entry inode number */
2bd0ea18
NS
244 xfs_extlen_t total) /* bmap's total block count */
245{
ff105f75 246 struct xfs_da_args *args;
5e656dbb 247 int rval;
2bd0ea18
NS
248 int v; /* type-checking value */
249
e37bf53c 250 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
44be3822 251
5a35bf2c
DC
252 if (inum) {
253 rval = xfs_dir_ino_validate(tp->t_mountp, inum);
254 if (rval)
255 return rval;
79896434 256 XFS_STATS_INC(dp->i_mount, xs_dir_create);
5a35bf2c 257 }
5e656dbb 258
ff105f75
DC
259 args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
260 if (!args)
12b53197 261 return -ENOMEM;
ff105f75
DC
262
263 args->geo = dp->i_mount->m_dir_geo;
264 args->name = name->name;
265 args->namelen = name->len;
266 args->filetype = name->type;
267 args->hashval = dp->i_mount->m_dirnameops->hashname(name);
268 args->inumber = inum;
269 args->dp = dp;
ff105f75
DC
270 args->total = total;
271 args->whichfork = XFS_DATA_FORK;
272 args->trans = tp;
273 args->op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
5a35bf2c
DC
274 if (!inum)
275 args->op_flags |= XFS_DA_OP_JUSTCHECK;
ff105f75
DC
276
277 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
278 rval = xfs_dir2_sf_addname(args);
279 goto out_free;
280 }
281
282 rval = xfs_dir2_isblock(args, &v);
283 if (rval)
284 goto out_free;
285 if (v) {
286 rval = xfs_dir2_block_addname(args);
287 goto out_free;
288 }
289
290 rval = xfs_dir2_isleaf(args, &v);
291 if (rval)
292 goto out_free;
293 if (v)
294 rval = xfs_dir2_leaf_addname(args);
2bd0ea18 295 else
ff105f75
DC
296 rval = xfs_dir2_node_addname(args);
297
298out_free:
299 kmem_free(args);
2bd0ea18
NS
300 return rval;
301}
302
5e656dbb
BN
303/*
304 * If doing a CI lookup and case-insensitive match, dup actual name into
305 * args.value. Return EEXIST for success (ie. name found) or an error.
306 */
307int
308xfs_dir_cilookup_result(
309 struct xfs_da_args *args,
56b2de80 310 const unsigned char *name,
5e656dbb
BN
311 int len)
312{
313 if (args->cmpresult == XFS_CMP_DIFFERENT)
12b53197 314 return -ENOENT;
5e656dbb
BN
315 if (args->cmpresult != XFS_CMP_CASE ||
316 !(args->op_flags & XFS_DA_OP_CILOOKUP))
12b53197 317 return -EEXIST;
5e656dbb 318
56b2de80 319 args->value = kmem_alloc(len, KM_NOFS | KM_MAYFAIL);
5e656dbb 320 if (!args->value)
12b53197 321 return -ENOMEM;
5e656dbb
BN
322
323 memcpy(args->value, name, len);
324 args->valuelen = len;
12b53197 325 return -EEXIST;
5e656dbb
BN
326}
327
2bd0ea18
NS
328/*
329 * Lookup a name in a directory, give back the inode number.
5e656dbb
BN
330 * If ci_name is not NULL, returns the actual name in ci_name if it differs
331 * to name, or ci_name->name is set to NULL for an exact match.
2bd0ea18 332 */
5e656dbb
BN
333
334int
335xfs_dir_lookup(
336 xfs_trans_t *tp,
337 xfs_inode_t *dp,
338 struct xfs_name *name,
339 xfs_ino_t *inum, /* out: inode number */
340 struct xfs_name *ci_name) /* out: actual name if CI match */
2bd0ea18 341{
ff105f75 342 struct xfs_da_args *args;
5e656dbb 343 int rval;
2bd0ea18 344 int v; /* type-checking value */
7ebb7646 345 int lock_mode;
2bd0ea18 346
e37bf53c 347 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
79896434 348 XFS_STATS_INC(dp->i_mount, xs_dir_lookup);
a95cf252 349
ff105f75
DC
350 /*
351 * We need to use KM_NOFS here so that lockdep will not throw false
352 * positive deadlock warnings on a non-transactional lookup path. It is
353 * safe to recurse into inode recalim in that case, but lockdep can't
354 * easily be taught about it. Hence KM_NOFS avoids having to add more
355 * lockdep Doing this avoids having to add a bunch of lockdep class
356 * annotations into the reclaim path for the ilock.
357 */
358 args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
359 args->geo = dp->i_mount->m_dir_geo;
360 args->name = name->name;
361 args->namelen = name->len;
362 args->filetype = name->type;
363 args->hashval = dp->i_mount->m_dirnameops->hashname(name);
364 args->dp = dp;
365 args->whichfork = XFS_DATA_FORK;
366 args->trans = tp;
367 args->op_flags = XFS_DA_OP_OKNOENT;
5e656dbb 368 if (ci_name)
ff105f75 369 args->op_flags |= XFS_DA_OP_CILOOKUP;
5e656dbb 370
7ebb7646 371 lock_mode = xfs_ilock_data_map_shared(dp);
ff105f75
DC
372 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
373 rval = xfs_dir2_sf_lookup(args);
374 goto out_check_rval;
375 }
376
377 rval = xfs_dir2_isblock(args, &v);
378 if (rval)
379 goto out_free;
380 if (v) {
381 rval = xfs_dir2_block_lookup(args);
382 goto out_check_rval;
383 }
384
385 rval = xfs_dir2_isleaf(args, &v);
386 if (rval)
387 goto out_free;
388 if (v)
389 rval = xfs_dir2_leaf_lookup(args);
2bd0ea18 390 else
ff105f75
DC
391 rval = xfs_dir2_node_lookup(args);
392
393out_check_rval:
12b53197 394 if (rval == -EEXIST)
2bd0ea18 395 rval = 0;
5e656dbb 396 if (!rval) {
ff105f75 397 *inum = args->inumber;
5e656dbb 398 if (ci_name) {
ff105f75
DC
399 ci_name->name = args->value;
400 ci_name->len = args->valuelen;
5e656dbb
BN
401 }
402 }
ff105f75 403out_free:
7ebb7646 404 xfs_iunlock(dp, lock_mode);
ff105f75 405 kmem_free(args);
2bd0ea18
NS
406 return rval;
407}
408
409/*
410 * Remove an entry from a directory.
411 */
5e656dbb
BN
412int
413xfs_dir_removename(
a50d2ab0
BF
414 struct xfs_trans *tp,
415 struct xfs_inode *dp,
416 struct xfs_name *name,
417 xfs_ino_t ino,
a50d2ab0 418 xfs_extlen_t total) /* bmap's total block count */
2bd0ea18 419{
a50d2ab0
BF
420 struct xfs_da_args *args;
421 int rval;
422 int v; /* type-checking value */
2bd0ea18 423
e37bf53c 424 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
79896434 425 XFS_STATS_INC(dp->i_mount, xs_dir_remove);
5e656dbb 426
ff105f75
DC
427 args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
428 if (!args)
12b53197 429 return -ENOMEM;
ff105f75
DC
430
431 args->geo = dp->i_mount->m_dir_geo;
432 args->name = name->name;
433 args->namelen = name->len;
434 args->filetype = name->type;
435 args->hashval = dp->i_mount->m_dirnameops->hashname(name);
436 args->inumber = ino;
437 args->dp = dp;
ff105f75
DC
438 args->total = total;
439 args->whichfork = XFS_DATA_FORK;
440 args->trans = tp;
441
442 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
443 rval = xfs_dir2_sf_removename(args);
444 goto out_free;
445 }
446
447 rval = xfs_dir2_isblock(args, &v);
448 if (rval)
449 goto out_free;
450 if (v) {
451 rval = xfs_dir2_block_removename(args);
452 goto out_free;
453 }
454
455 rval = xfs_dir2_isleaf(args, &v);
456 if (rval)
457 goto out_free;
458 if (v)
459 rval = xfs_dir2_leaf_removename(args);
2bd0ea18 460 else
ff105f75
DC
461 rval = xfs_dir2_node_removename(args);
462out_free:
463 kmem_free(args);
2bd0ea18
NS
464 return rval;
465}
466
467/*
468 * Replace the inode number of a directory entry.
469 */
5e656dbb
BN
470int
471xfs_dir_replace(
a50d2ab0
BF
472 struct xfs_trans *tp,
473 struct xfs_inode *dp,
474 struct xfs_name *name, /* name of entry to replace */
475 xfs_ino_t inum, /* new inode number */
a50d2ab0 476 xfs_extlen_t total) /* bmap's total block count */
2bd0ea18 477{
a50d2ab0
BF
478 struct xfs_da_args *args;
479 int rval;
480 int v; /* type-checking value */
2bd0ea18 481
e37bf53c 482 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
a95cf252 483
ff105f75
DC
484 rval = xfs_dir_ino_validate(tp->t_mountp, inum);
485 if (rval)
2bd0ea18 486 return rval;
5e656dbb 487
ff105f75
DC
488 args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
489 if (!args)
12b53197 490 return -ENOMEM;
ff105f75
DC
491
492 args->geo = dp->i_mount->m_dir_geo;
493 args->name = name->name;
494 args->namelen = name->len;
495 args->filetype = name->type;
496 args->hashval = dp->i_mount->m_dirnameops->hashname(name);
497 args->inumber = inum;
498 args->dp = dp;
ff105f75
DC
499 args->total = total;
500 args->whichfork = XFS_DATA_FORK;
501 args->trans = tp;
502
503 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
504 rval = xfs_dir2_sf_replace(args);
505 goto out_free;
506 }
507
508 rval = xfs_dir2_isblock(args, &v);
509 if (rval)
510 goto out_free;
511 if (v) {
512 rval = xfs_dir2_block_replace(args);
513 goto out_free;
514 }
515
516 rval = xfs_dir2_isleaf(args, &v);
517 if (rval)
518 goto out_free;
519 if (v)
520 rval = xfs_dir2_leaf_replace(args);
2bd0ea18 521 else
ff105f75
DC
522 rval = xfs_dir2_node_replace(args);
523out_free:
524 kmem_free(args);
2bd0ea18
NS
525 return rval;
526}
527
4a34b33d
DC
528/*
529 * See if this entry can be added to the directory without allocating space.
4a34b33d
DC
530 */
531int
532xfs_dir_canenter(
533 xfs_trans_t *tp,
534 xfs_inode_t *dp,
5a35bf2c 535 struct xfs_name *name) /* name of entry to add */
4a34b33d 536{
015bc82e 537 return xfs_dir_createname(tp, dp, name, 0, 0);
4a34b33d
DC
538}
539
2bd0ea18
NS
540/*
541 * Utility routines.
542 */
543
544/*
545 * Add a block to the directory.
a2ceac1f
DC
546 *
547 * This routine is for data and free blocks, not leaf/node blocks which are
548 * handled by xfs_da_grow_inode.
2bd0ea18 549 */
5e656dbb 550int
2bd0ea18 551xfs_dir2_grow_inode(
a2ceac1f
DC
552 struct xfs_da_args *args,
553 int space, /* v2 dir's space XFS_DIR2_xxx_SPACE */
554 xfs_dir2_db_t *dbp) /* out: block number added */
2bd0ea18 555{
a2ceac1f
DC
556 struct xfs_inode *dp = args->dp;
557 struct xfs_mount *mp = dp->i_mount;
558 xfs_fileoff_t bno; /* directory offset of new block */
559 int count; /* count of filesystem blocks */
560 int error;
56b2de80
DC
561
562 trace_xfs_dir2_grow_inode(args, space);
2bd0ea18 563
2bd0ea18
NS
564 /*
565 * Set lowest possible block in the space requested.
566 */
567 bno = XFS_B_TO_FSBT(mp, space * XFS_DIR2_SPACE_SIZE);
ff105f75 568 count = args->geo->fsbcount;
2bd0ea18 569
a2ceac1f
DC
570 error = xfs_da_grow_inode_int(args, &bno, count);
571 if (error)
572 return error;
56b2de80 573
ff105f75 574 *dbp = xfs_dir2_da_to_db(args->geo, (xfs_dablk_t)bno);
56b2de80 575
2bd0ea18
NS
576 /*
577 * Update file's size if this is the data space and it grew.
578 */
579 if (space == XFS_DIR2_DATA_SPACE) {
580 xfs_fsize_t size; /* directory file (data) size */
581
582 size = XFS_FSB_TO_B(mp, bno + count);
583 if (size > dp->i_d.di_size) {
584 dp->i_d.di_size = size;
a2ceac1f 585 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
2bd0ea18
NS
586 }
587 }
588 return 0;
589}
590
591/*
592 * See if the directory is a single-block form directory.
593 */
5e656dbb 594int
2bd0ea18 595xfs_dir2_isblock(
ff105f75
DC
596 struct xfs_da_args *args,
597 int *vp) /* out: 1 is block, 0 is not block */
2bd0ea18 598{
ff105f75
DC
599 xfs_fileoff_t last; /* last file offset */
600 int rval;
2bd0ea18 601
ff105f75 602 if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
2bd0ea18 603 return rval;
ff105f75 604 rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize;
3cfabff6
AG
605 if (rval != 0 && args->dp->i_d.di_size != args->geo->blksize)
606 return -EFSCORRUPTED;
2bd0ea18
NS
607 *vp = rval;
608 return 0;
609}
610
611/*
612 * See if the directory is a single-leaf form directory.
613 */
5e656dbb 614int
2bd0ea18 615xfs_dir2_isleaf(
ff105f75
DC
616 struct xfs_da_args *args,
617 int *vp) /* out: 1 is block, 0 is not block */
2bd0ea18 618{
ff105f75
DC
619 xfs_fileoff_t last; /* last file offset */
620 int rval;
2bd0ea18 621
ff105f75 622 if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
2bd0ea18 623 return rval;
ff105f75 624 *vp = last == args->geo->leafblk + args->geo->fsbcount;
2bd0ea18
NS
625 return 0;
626}
627
628/*
629 * Remove the given block from the directory.
630 * This routine is used for data and free blocks, leaf/node are done
631 * by xfs_da_shrink_inode.
632 */
633int
634xfs_dir2_shrink_inode(
a50d2ab0
BF
635 struct xfs_da_args *args,
636 xfs_dir2_db_t db,
637 struct xfs_buf *bp)
2bd0ea18 638{
a50d2ab0
BF
639 xfs_fileoff_t bno; /* directory file offset */
640 xfs_dablk_t da; /* directory file offset */
641 int done; /* bunmap is finished */
642 struct xfs_inode *dp;
643 int error;
644 struct xfs_mount *mp;
645 struct xfs_trans *tp;
2bd0ea18 646
56b2de80
DC
647 trace_xfs_dir2_shrink_inode(args, db);
648
2bd0ea18
NS
649 dp = args->dp;
650 mp = dp->i_mount;
651 tp = args->trans;
ff105f75 652 da = xfs_dir2_db_to_da(args->geo, db);
db9cba4e
DC
653
654 /* Unmap the fsblock(s). */
d3c5f3dd 655 error = xfs_bunmapi(tp, dp, da, args->geo->fsbcount, 0, 0, &done);
db9cba4e 656 if (error) {
2bd0ea18 657 /*
db9cba4e
DC
658 * ENOSPC actually can happen if we're in a removename with no
659 * space reservation, and the resulting block removal would
660 * cause a bmap btree split or conversion from extents to btree.
661 * This can only happen for un-fragmented directory blocks,
662 * since you need to be punching out the middle of an extent.
663 * In this case we need to leave the block in the file, and not
664 * binval it. So the block has to be in a consistent empty
665 * state and appropriately logged. We don't free up the buffer,
666 * the caller can tell it hasn't happened since it got an error
667 * back.
2bd0ea18
NS
668 */
669 return error;
670 }
671 ASSERT(done);
672 /*
673 * Invalidate the buffer from the transaction.
674 */
a2ceac1f 675 xfs_trans_binval(tp, bp);
2bd0ea18
NS
676 /*
677 * If it's not a data block, we're done.
678 */
ff105f75 679 if (db >= xfs_dir2_byte_to_db(args->geo, XFS_DIR2_LEAF_OFFSET))
2bd0ea18
NS
680 return 0;
681 /*
682 * If the block isn't the last one in the directory, we're done.
683 */
ff105f75 684 if (dp->i_d.di_size > xfs_dir2_db_off_to_byte(args->geo, db + 1, 0))
2bd0ea18
NS
685 return 0;
686 bno = da;
0e266570 687 if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) {
2bd0ea18
NS
688 /*
689 * This can't really happen unless there's kernel corruption.
690 */
691 return error;
692 }
ff105f75 693 if (db == args->geo->datablk)
2bd0ea18
NS
694 ASSERT(bno == 0);
695 else
696 ASSERT(bno > 0);
697 /*
698 * Set the size to the new last block.
699 */
700 dp->i_d.di_size = XFS_FSB_TO_B(mp, bno);
701 xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
702 return 0;
703}