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