557 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2000-2005 Silicon Graphics, Inc.4 * Copyright (c) 2013 Red Hat, Inc.5 * All Rights Reserved.6 */7#include "xfs.h"8#include "xfs_fs.h"9#include "xfs_shared.h"10#include "xfs_format.h"11#include "xfs_log_format.h"12#include "xfs_trans_resv.h"13#include "xfs_mount.h"14#include "xfs_inode.h"15#include "xfs_dir2.h"16#include "xfs_dir2_priv.h"17#include "xfs_trace.h"18#include "xfs_bmap.h"19#include "xfs_trans.h"20#include "xfs_error.h"21#include "xfs_health.h"22 23/*24 * Directory file type support functions25 */26static unsigned char xfs_dir3_filetype_table[] = {27 DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK,28 DT_FIFO, DT_SOCK, DT_LNK, DT_WHT,29};30 31unsigned char32xfs_dir3_get_dtype(33 struct xfs_mount *mp,34 uint8_t filetype)35{36 if (!xfs_has_ftype(mp))37 return DT_UNKNOWN;38 39 if (filetype >= XFS_DIR3_FT_MAX)40 return DT_UNKNOWN;41 42 return xfs_dir3_filetype_table[filetype];43}44 45STATIC int46xfs_dir2_sf_getdents(47 struct xfs_da_args *args,48 struct dir_context *ctx)49{50 int i; /* shortform entry number */51 struct xfs_inode *dp = args->dp; /* incore directory inode */52 struct xfs_mount *mp = dp->i_mount;53 xfs_dir2_dataptr_t off; /* current entry's offset */54 xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */55 struct xfs_dir2_sf_hdr *sfp = dp->i_df.if_data;56 xfs_dir2_dataptr_t dot_offset;57 xfs_dir2_dataptr_t dotdot_offset;58 xfs_ino_t ino;59 struct xfs_da_geometry *geo = args->geo;60 61 ASSERT(dp->i_df.if_format == XFS_DINODE_FMT_LOCAL);62 ASSERT(dp->i_df.if_bytes == dp->i_disk_size);63 ASSERT(sfp != NULL);64 65 /*66 * If the block number in the offset is out of range, we're done.67 */68 if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)69 return 0;70 71 /*72 * Precalculate offsets for "." and ".." as we will always need them.73 * This relies on the fact that directories always start with the74 * entries for "." and "..".75 */76 dot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,77 geo->data_entry_offset);78 dotdot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,79 geo->data_entry_offset +80 xfs_dir2_data_entsize(mp, sizeof(".") - 1));81 82 /*83 * Put . entry unless we're starting past it.84 */85 if (ctx->pos <= dot_offset) {86 ctx->pos = dot_offset & 0x7fffffff;87 if (!dir_emit(ctx, ".", 1, dp->i_ino, DT_DIR))88 return 0;89 }90 91 /*92 * Put .. entry unless we're starting past it.93 */94 if (ctx->pos <= dotdot_offset) {95 ino = xfs_dir2_sf_get_parent_ino(sfp);96 ctx->pos = dotdot_offset & 0x7fffffff;97 if (!dir_emit(ctx, "..", 2, ino, DT_DIR))98 return 0;99 }100 101 /*102 * Loop while there are more entries and put'ing works.103 */104 sfep = xfs_dir2_sf_firstentry(sfp);105 for (i = 0; i < sfp->count; i++) {106 uint8_t filetype;107 108 off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,109 xfs_dir2_sf_get_offset(sfep));110 111 if (ctx->pos > off) {112 sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);113 continue;114 }115 116 ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);117 filetype = xfs_dir2_sf_get_ftype(mp, sfep);118 ctx->pos = off & 0x7fffffff;119 if (XFS_IS_CORRUPT(dp->i_mount,120 !xfs_dir2_namecheck(sfep->name,121 sfep->namelen))) {122 xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);123 return -EFSCORRUPTED;124 }125 if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen, ino,126 xfs_dir3_get_dtype(mp, filetype)))127 return 0;128 sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);129 }130 131 ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &132 0x7fffffff;133 return 0;134}135 136/*137 * Readdir for block directories.138 */139STATIC int140xfs_dir2_block_getdents(141 struct xfs_da_args *args,142 struct dir_context *ctx,143 unsigned int *lock_mode)144{145 struct xfs_inode *dp = args->dp; /* incore directory inode */146 struct xfs_buf *bp; /* buffer for block */147 int error; /* error return value */148 int wantoff; /* starting block offset */149 xfs_off_t cook;150 struct xfs_da_geometry *geo = args->geo;151 unsigned int offset, next_offset;152 unsigned int end;153 154 /*155 * If the block number in the offset is out of range, we're done.156 */157 if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)158 return 0;159 160 error = xfs_dir3_block_read(args->trans, dp, args->owner, &bp);161 if (error)162 return error;163 164 xfs_iunlock(dp, *lock_mode);165 *lock_mode = 0;166 167 /*168 * Extract the byte offset we start at from the seek pointer.169 * We'll skip entries before this.170 */171 wantoff = xfs_dir2_dataptr_to_off(geo, ctx->pos);172 xfs_dir3_data_check(dp, bp);173 174 /*175 * Loop over the data portion of the block.176 * Each object is a real entry (dep) or an unused one (dup).177 */178 end = xfs_dir3_data_end_offset(geo, bp->b_addr);179 for (offset = geo->data_entry_offset;180 offset < end;181 offset = next_offset) {182 struct xfs_dir2_data_unused *dup = bp->b_addr + offset;183 struct xfs_dir2_data_entry *dep = bp->b_addr + offset;184 uint8_t filetype;185 186 /*187 * Unused, skip it.188 */189 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {190 next_offset = offset + be16_to_cpu(dup->length);191 continue;192 }193 194 /*195 * Bump pointer for the next iteration.196 */197 next_offset = offset +198 xfs_dir2_data_entsize(dp->i_mount, dep->namelen);199 200 /*201 * The entry is before the desired starting point, skip it.202 */203 if (offset < wantoff)204 continue;205 206 cook = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, offset);207 208 ctx->pos = cook & 0x7fffffff;209 filetype = xfs_dir2_data_get_ftype(dp->i_mount, dep);210 /*211 * If it didn't fit, set the final offset to here & return.212 */213 if (XFS_IS_CORRUPT(dp->i_mount,214 !xfs_dir2_namecheck(dep->name,215 dep->namelen))) {216 xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);217 error = -EFSCORRUPTED;218 goto out_rele;219 }220 if (!dir_emit(ctx, (char *)dep->name, dep->namelen,221 be64_to_cpu(dep->inumber),222 xfs_dir3_get_dtype(dp->i_mount, filetype)))223 goto out_rele;224 }225 226 /*227 * Reached the end of the block.228 * Set the offset to a non-existent block 1 and return.229 */230 ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &231 0x7fffffff;232out_rele:233 xfs_trans_brelse(args->trans, bp);234 return error;235}236 237/*238 * Read a directory block and initiate readahead for blocks beyond that.239 * We maintain a sliding readahead window of the remaining space in the240 * buffer rounded up to the nearest block.241 */242STATIC int243xfs_dir2_leaf_readbuf(244 struct xfs_da_args *args,245 size_t bufsize,246 xfs_dir2_off_t *cur_off,247 xfs_dablk_t *ra_blk,248 struct xfs_buf **bpp)249{250 struct xfs_inode *dp = args->dp;251 struct xfs_buf *bp = NULL;252 struct xfs_da_geometry *geo = args->geo;253 struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);254 struct xfs_bmbt_irec map;255 struct blk_plug plug;256 xfs_dir2_off_t new_off;257 xfs_dablk_t next_ra;258 xfs_dablk_t map_off;259 xfs_dablk_t last_da;260 struct xfs_iext_cursor icur;261 int ra_want;262 int error = 0;263 264 error = xfs_iread_extents(args->trans, dp, XFS_DATA_FORK);265 if (error)266 goto out;267 268 /*269 * Look for mapped directory blocks at or above the current offset.270 * Truncate down to the nearest directory block to start the scanning271 * operation.272 */273 last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);274 map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *cur_off));275 if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))276 goto out;277 if (map.br_startoff >= last_da)278 goto out;279 xfs_trim_extent(&map, map_off, last_da - map_off);280 281 /* Read the directory block of that first mapping. */282 new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);283 if (new_off > *cur_off)284 *cur_off = new_off;285 error = xfs_dir3_data_read(args->trans, dp, args->owner,286 map.br_startoff, 0, &bp);287 if (error)288 goto out;289 290 /*291 * Start readahead for the next bufsize's worth of dir data blocks.292 * We may have already issued readahead for some of that range;293 * ra_blk tracks the last block we tried to read(ahead).294 */295 ra_want = howmany(bufsize + geo->blksize, (1 << geo->fsblog));296 if (*ra_blk >= last_da)297 goto out;298 else if (*ra_blk == 0)299 *ra_blk = map.br_startoff;300 next_ra = map.br_startoff + geo->fsbcount;301 if (next_ra >= last_da)302 goto out_no_ra;303 if (map.br_blockcount < geo->fsbcount &&304 !xfs_iext_next_extent(ifp, &icur, &map))305 goto out_no_ra;306 if (map.br_startoff >= last_da)307 goto out_no_ra;308 xfs_trim_extent(&map, next_ra, last_da - next_ra);309 310 /* Start ra for each dir (not fs) block that has a mapping. */311 blk_start_plug(&plug);312 while (ra_want > 0) {313 next_ra = roundup((xfs_dablk_t)map.br_startoff, geo->fsbcount);314 while (ra_want > 0 &&315 next_ra < map.br_startoff + map.br_blockcount) {316 if (next_ra >= last_da) {317 *ra_blk = last_da;318 break;319 }320 if (next_ra > *ra_blk) {321 xfs_dir3_data_readahead(dp, next_ra,322 XFS_DABUF_MAP_HOLE_OK);323 *ra_blk = next_ra;324 }325 ra_want -= geo->fsbcount;326 next_ra += geo->fsbcount;327 }328 if (!xfs_iext_next_extent(ifp, &icur, &map)) {329 *ra_blk = last_da;330 break;331 }332 }333 blk_finish_plug(&plug);334 335out:336 *bpp = bp;337 return error;338out_no_ra:339 *ra_blk = last_da;340 goto out;341}342 343/*344 * Getdents (readdir) for leaf and node directories.345 * This reads the data blocks only, so is the same for both forms.346 */347STATIC int348xfs_dir2_leaf_getdents(349 struct xfs_da_args *args,350 struct dir_context *ctx,351 size_t bufsize,352 unsigned int *lock_mode)353{354 struct xfs_inode *dp = args->dp;355 struct xfs_mount *mp = dp->i_mount;356 struct xfs_buf *bp = NULL; /* data block buffer */357 xfs_dir2_data_entry_t *dep; /* data entry */358 xfs_dir2_data_unused_t *dup; /* unused entry */359 struct xfs_da_geometry *geo = args->geo;360 xfs_dablk_t rablk = 0; /* current readahead block */361 xfs_dir2_off_t curoff; /* current overall offset */362 int length; /* temporary length value */363 int byteoff; /* offset in current block */364 unsigned int offset = 0;365 int error = 0; /* error return value */366 367 /*368 * If the offset is at or past the largest allowed value,369 * give up right away.370 */371 if (ctx->pos >= XFS_DIR2_MAX_DATAPTR)372 return 0;373 374 /*375 * Inside the loop we keep the main offset value as a byte offset376 * in the directory file.377 */378 curoff = xfs_dir2_dataptr_to_byte(ctx->pos);379 380 /*381 * Loop over directory entries until we reach the end offset.382 * Get more blocks and readahead as necessary.383 */384 while (curoff < XFS_DIR2_LEAF_OFFSET) {385 uint8_t filetype;386 387 /*388 * If we have no buffer, or we're off the end of the389 * current buffer, need to get another one.390 */391 if (!bp || offset >= geo->blksize) {392 if (bp) {393 xfs_trans_brelse(args->trans, bp);394 bp = NULL;395 }396 397 if (*lock_mode == 0)398 *lock_mode = xfs_ilock_data_map_shared(dp);399 error = xfs_dir2_leaf_readbuf(args, bufsize, &curoff,400 &rablk, &bp);401 if (error || !bp)402 break;403 404 xfs_iunlock(dp, *lock_mode);405 *lock_mode = 0;406 407 xfs_dir3_data_check(dp, bp);408 /*409 * Find our position in the block.410 */411 offset = geo->data_entry_offset;412 byteoff = xfs_dir2_byte_to_off(geo, curoff);413 /*414 * Skip past the header.415 */416 if (byteoff == 0)417 curoff += geo->data_entry_offset;418 /*419 * Skip past entries until we reach our offset.420 */421 else {422 while (offset < byteoff) {423 dup = bp->b_addr + offset;424 425 if (be16_to_cpu(dup->freetag)426 == XFS_DIR2_DATA_FREE_TAG) {427 428 length = be16_to_cpu(dup->length);429 offset += length;430 continue;431 }432 dep = bp->b_addr + offset;433 length = xfs_dir2_data_entsize(mp,434 dep->namelen);435 offset += length;436 }437 /*438 * Now set our real offset.439 */440 curoff =441 xfs_dir2_db_off_to_byte(geo,442 xfs_dir2_byte_to_db(geo, curoff),443 offset);444 if (offset >= geo->blksize)445 continue;446 }447 }448 449 /*450 * We have a pointer to an entry. Is it a live one?451 */452 dup = bp->b_addr + offset;453 454 /*455 * No, it's unused, skip over it.456 */457 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {458 length = be16_to_cpu(dup->length);459 offset += length;460 curoff += length;461 continue;462 }463 464 dep = bp->b_addr + offset;465 length = xfs_dir2_data_entsize(mp, dep->namelen);466 filetype = xfs_dir2_data_get_ftype(mp, dep);467 468 ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;469 if (XFS_IS_CORRUPT(dp->i_mount,470 !xfs_dir2_namecheck(dep->name,471 dep->namelen))) {472 xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);473 error = -EFSCORRUPTED;474 break;475 }476 if (!dir_emit(ctx, (char *)dep->name, dep->namelen,477 be64_to_cpu(dep->inumber),478 xfs_dir3_get_dtype(dp->i_mount, filetype)))479 break;480 481 /*482 * Advance to next entry in the block.483 */484 offset += length;485 curoff += length;486 /* bufsize may have just been a guess; don't go negative */487 bufsize = bufsize > length ? bufsize - length : 0;488 }489 490 /*491 * All done. Set output offset value to current offset.492 */493 if (curoff > xfs_dir2_dataptr_to_byte(XFS_DIR2_MAX_DATAPTR))494 ctx->pos = XFS_DIR2_MAX_DATAPTR & 0x7fffffff;495 else496 ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;497 if (bp)498 xfs_trans_brelse(args->trans, bp);499 return error;500}501 502/*503 * Read a directory.504 *505 * If supplied, the transaction collects locked dir buffers to avoid506 * nested buffer deadlocks. This function does not dirty the507 * transaction. The caller must hold the IOLOCK (shared or exclusive)508 * before calling this function.509 */510int511xfs_readdir(512 struct xfs_trans *tp,513 struct xfs_inode *dp,514 struct dir_context *ctx,515 size_t bufsize)516{517 struct xfs_da_args args = { NULL };518 unsigned int lock_mode;519 int error;520 521 trace_xfs_readdir(dp);522 523 if (xfs_is_shutdown(dp->i_mount))524 return -EIO;525 if (xfs_ifork_zapped(dp, XFS_DATA_FORK))526 return -EIO;527 528 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));529 xfs_assert_ilocked(dp, XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL);530 XFS_STATS_INC(dp->i_mount, xs_dir_getdents);531 532 args.dp = dp;533 args.geo = dp->i_mount->m_dir_geo;534 args.trans = tp;535 args.owner = dp->i_ino;536 537 if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)538 return xfs_dir2_sf_getdents(&args, ctx);539 540 lock_mode = xfs_ilock_data_map_shared(dp);541 switch (xfs_dir2_format(&args, &error)) {542 case XFS_DIR2_FMT_BLOCK:543 error = xfs_dir2_block_getdents(&args, ctx, &lock_mode);544 break;545 case XFS_DIR2_FMT_LEAF:546 case XFS_DIR2_FMT_NODE:547 error = xfs_dir2_leaf_getdents(&args, ctx, bufsize, &lock_mode);548 break;549 default:550 break;551 }552 553 if (lock_mode)554 xfs_iunlock(dp, lock_mode);555 return error;556}557