Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2000-2005 Silicon Graphics, Inc.
0004  * Copyright (c) 2013 Red Hat, Inc.
0005  * All Rights Reserved.
0006  */
0007 #include "xfs.h"
0008 #include "xfs_fs.h"
0009 #include "xfs_shared.h"
0010 #include "xfs_format.h"
0011 #include "xfs_log_format.h"
0012 #include "xfs_trans_resv.h"
0013 #include "xfs_mount.h"
0014 #include "xfs_inode.h"
0015 #include "xfs_dir2.h"
0016 #include "xfs_dir2_priv.h"
0017 #include "xfs_trace.h"
0018 #include "xfs_bmap.h"
0019 #include "xfs_trans.h"
0020 #include "xfs_error.h"
0021 
0022 /*
0023  * Directory file type support functions
0024  */
0025 static unsigned char xfs_dir3_filetype_table[] = {
0026     DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK,
0027     DT_FIFO, DT_SOCK, DT_LNK, DT_WHT,
0028 };
0029 
0030 unsigned char
0031 xfs_dir3_get_dtype(
0032     struct xfs_mount    *mp,
0033     uint8_t         filetype)
0034 {
0035     if (!xfs_has_ftype(mp))
0036         return DT_UNKNOWN;
0037 
0038     if (filetype >= XFS_DIR3_FT_MAX)
0039         return DT_UNKNOWN;
0040 
0041     return xfs_dir3_filetype_table[filetype];
0042 }
0043 
0044 STATIC int
0045 xfs_dir2_sf_getdents(
0046     struct xfs_da_args  *args,
0047     struct dir_context  *ctx)
0048 {
0049     int         i;      /* shortform entry number */
0050     struct xfs_inode    *dp = args->dp; /* incore directory inode */
0051     struct xfs_mount    *mp = dp->i_mount;
0052     xfs_dir2_dataptr_t  off;        /* current entry's offset */
0053     xfs_dir2_sf_entry_t *sfep;      /* shortform directory entry */
0054     xfs_dir2_sf_hdr_t   *sfp;       /* shortform structure */
0055     xfs_dir2_dataptr_t  dot_offset;
0056     xfs_dir2_dataptr_t  dotdot_offset;
0057     xfs_ino_t       ino;
0058     struct xfs_da_geometry  *geo = args->geo;
0059 
0060     ASSERT(dp->i_df.if_format == XFS_DINODE_FMT_LOCAL);
0061     ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
0062     ASSERT(dp->i_df.if_u1.if_data != NULL);
0063 
0064     sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
0065 
0066     /*
0067      * If the block number in the offset is out of range, we're done.
0068      */
0069     if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)
0070         return 0;
0071 
0072     /*
0073      * Precalculate offsets for "." and ".." as we will always need them.
0074      * This relies on the fact that directories always start with the
0075      * entries for "." and "..".
0076      */
0077     dot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
0078             geo->data_entry_offset);
0079     dotdot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
0080             geo->data_entry_offset +
0081             xfs_dir2_data_entsize(mp, sizeof(".") - 1));
0082 
0083     /*
0084      * Put . entry unless we're starting past it.
0085      */
0086     if (ctx->pos <= dot_offset) {
0087         ctx->pos = dot_offset & 0x7fffffff;
0088         if (!dir_emit(ctx, ".", 1, dp->i_ino, DT_DIR))
0089             return 0;
0090     }
0091 
0092     /*
0093      * Put .. entry unless we're starting past it.
0094      */
0095     if (ctx->pos <= dotdot_offset) {
0096         ino = xfs_dir2_sf_get_parent_ino(sfp);
0097         ctx->pos = dotdot_offset & 0x7fffffff;
0098         if (!dir_emit(ctx, "..", 2, ino, DT_DIR))
0099             return 0;
0100     }
0101 
0102     /*
0103      * Loop while there are more entries and put'ing works.
0104      */
0105     sfep = xfs_dir2_sf_firstentry(sfp);
0106     for (i = 0; i < sfp->count; i++) {
0107         uint8_t filetype;
0108 
0109         off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
0110                 xfs_dir2_sf_get_offset(sfep));
0111 
0112         if (ctx->pos > off) {
0113             sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
0114             continue;
0115         }
0116 
0117         ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
0118         filetype = xfs_dir2_sf_get_ftype(mp, sfep);
0119         ctx->pos = off & 0x7fffffff;
0120         if (XFS_IS_CORRUPT(dp->i_mount,
0121                    !xfs_dir2_namecheck(sfep->name,
0122                                sfep->namelen)))
0123             return -EFSCORRUPTED;
0124         if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen, ino,
0125                 xfs_dir3_get_dtype(mp, filetype)))
0126             return 0;
0127         sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
0128     }
0129 
0130     ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &
0131                                 0x7fffffff;
0132     return 0;
0133 }
0134 
0135 /*
0136  * Readdir for block directories.
0137  */
0138 STATIC int
0139 xfs_dir2_block_getdents(
0140     struct xfs_da_args  *args,
0141     struct dir_context  *ctx,
0142     unsigned int        *lock_mode)
0143 {
0144     struct xfs_inode    *dp = args->dp; /* incore directory inode */
0145     struct xfs_buf      *bp;        /* buffer for block */
0146     int         error;      /* error return value */
0147     int         wantoff;    /* starting block offset */
0148     xfs_off_t       cook;
0149     struct xfs_da_geometry  *geo = args->geo;
0150     unsigned int        offset, next_offset;
0151     unsigned int        end;
0152 
0153     /*
0154      * If the block number in the offset is out of range, we're done.
0155      */
0156     if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)
0157         return 0;
0158 
0159     error = xfs_dir3_block_read(args->trans, dp, &bp);
0160     if (error)
0161         return error;
0162 
0163     xfs_iunlock(dp, *lock_mode);
0164     *lock_mode = 0;
0165 
0166     /*
0167      * Extract the byte offset we start at from the seek pointer.
0168      * We'll skip entries before this.
0169      */
0170     wantoff = xfs_dir2_dataptr_to_off(geo, ctx->pos);
0171     xfs_dir3_data_check(dp, bp);
0172 
0173     /*
0174      * Loop over the data portion of the block.
0175      * Each object is a real entry (dep) or an unused one (dup).
0176      */
0177     end = xfs_dir3_data_end_offset(geo, bp->b_addr);
0178     for (offset = geo->data_entry_offset;
0179          offset < end;
0180          offset = next_offset) {
0181         struct xfs_dir2_data_unused *dup = bp->b_addr + offset;
0182         struct xfs_dir2_data_entry  *dep = bp->b_addr + offset;
0183         uint8_t filetype;
0184 
0185         /*
0186          * Unused, skip it.
0187          */
0188         if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
0189             next_offset = offset + be16_to_cpu(dup->length);
0190             continue;
0191         }
0192 
0193         /*
0194          * Bump pointer for the next iteration.
0195          */
0196         next_offset = offset +
0197             xfs_dir2_data_entsize(dp->i_mount, dep->namelen);
0198 
0199         /*
0200          * The entry is before the desired starting point, skip it.
0201          */
0202         if (offset < wantoff)
0203             continue;
0204 
0205         cook = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, offset);
0206 
0207         ctx->pos = cook & 0x7fffffff;
0208         filetype = xfs_dir2_data_get_ftype(dp->i_mount, dep);
0209         /*
0210          * If it didn't fit, set the final offset to here & return.
0211          */
0212         if (XFS_IS_CORRUPT(dp->i_mount,
0213                    !xfs_dir2_namecheck(dep->name,
0214                                dep->namelen))) {
0215             error = -EFSCORRUPTED;
0216             goto out_rele;
0217         }
0218         if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
0219                 be64_to_cpu(dep->inumber),
0220                 xfs_dir3_get_dtype(dp->i_mount, filetype)))
0221             goto out_rele;
0222     }
0223 
0224     /*
0225      * Reached the end of the block.
0226      * Set the offset to a non-existent block 1 and return.
0227      */
0228     ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &
0229                                 0x7fffffff;
0230 out_rele:
0231     xfs_trans_brelse(args->trans, bp);
0232     return error;
0233 }
0234 
0235 /*
0236  * Read a directory block and initiate readahead for blocks beyond that.
0237  * We maintain a sliding readahead window of the remaining space in the
0238  * buffer rounded up to the nearest block.
0239  */
0240 STATIC int
0241 xfs_dir2_leaf_readbuf(
0242     struct xfs_da_args  *args,
0243     size_t          bufsize,
0244     xfs_dir2_off_t      *cur_off,
0245     xfs_dablk_t     *ra_blk,
0246     struct xfs_buf      **bpp)
0247 {
0248     struct xfs_inode    *dp = args->dp;
0249     struct xfs_buf      *bp = NULL;
0250     struct xfs_da_geometry  *geo = args->geo;
0251     struct xfs_ifork    *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
0252     struct xfs_bmbt_irec    map;
0253     struct blk_plug     plug;
0254     xfs_dir2_off_t      new_off;
0255     xfs_dablk_t     next_ra;
0256     xfs_dablk_t     map_off;
0257     xfs_dablk_t     last_da;
0258     struct xfs_iext_cursor  icur;
0259     int         ra_want;
0260     int         error = 0;
0261 
0262     error = xfs_iread_extents(args->trans, dp, XFS_DATA_FORK);
0263     if (error)
0264         goto out;
0265 
0266     /*
0267      * Look for mapped directory blocks at or above the current offset.
0268      * Truncate down to the nearest directory block to start the scanning
0269      * operation.
0270      */
0271     last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
0272     map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *cur_off));
0273     if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
0274         goto out;
0275     if (map.br_startoff >= last_da)
0276         goto out;
0277     xfs_trim_extent(&map, map_off, last_da - map_off);
0278 
0279     /* Read the directory block of that first mapping. */
0280     new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
0281     if (new_off > *cur_off)
0282         *cur_off = new_off;
0283     error = xfs_dir3_data_read(args->trans, dp, map.br_startoff, 0, &bp);
0284     if (error)
0285         goto out;
0286 
0287     /*
0288      * Start readahead for the next bufsize's worth of dir data blocks.
0289      * We may have already issued readahead for some of that range;
0290      * ra_blk tracks the last block we tried to read(ahead).
0291      */
0292     ra_want = howmany(bufsize + geo->blksize, (1 << geo->fsblog));
0293     if (*ra_blk >= last_da)
0294         goto out;
0295     else if (*ra_blk == 0)
0296         *ra_blk = map.br_startoff;
0297     next_ra = map.br_startoff + geo->fsbcount;
0298     if (next_ra >= last_da)
0299         goto out_no_ra;
0300     if (map.br_blockcount < geo->fsbcount &&
0301         !xfs_iext_next_extent(ifp, &icur, &map))
0302         goto out_no_ra;
0303     if (map.br_startoff >= last_da)
0304         goto out_no_ra;
0305     xfs_trim_extent(&map, next_ra, last_da - next_ra);
0306 
0307     /* Start ra for each dir (not fs) block that has a mapping. */
0308     blk_start_plug(&plug);
0309     while (ra_want > 0) {
0310         next_ra = roundup((xfs_dablk_t)map.br_startoff, geo->fsbcount);
0311         while (ra_want > 0 &&
0312                next_ra < map.br_startoff + map.br_blockcount) {
0313             if (next_ra >= last_da) {
0314                 *ra_blk = last_da;
0315                 break;
0316             }
0317             if (next_ra > *ra_blk) {
0318                 xfs_dir3_data_readahead(dp, next_ra,
0319                             XFS_DABUF_MAP_HOLE_OK);
0320                 *ra_blk = next_ra;
0321             }
0322             ra_want -= geo->fsbcount;
0323             next_ra += geo->fsbcount;
0324         }
0325         if (!xfs_iext_next_extent(ifp, &icur, &map)) {
0326             *ra_blk = last_da;
0327             break;
0328         }
0329     }
0330     blk_finish_plug(&plug);
0331 
0332 out:
0333     *bpp = bp;
0334     return error;
0335 out_no_ra:
0336     *ra_blk = last_da;
0337     goto out;
0338 }
0339 
0340 /*
0341  * Getdents (readdir) for leaf and node directories.
0342  * This reads the data blocks only, so is the same for both forms.
0343  */
0344 STATIC int
0345 xfs_dir2_leaf_getdents(
0346     struct xfs_da_args  *args,
0347     struct dir_context  *ctx,
0348     size_t          bufsize,
0349     unsigned int        *lock_mode)
0350 {
0351     struct xfs_inode    *dp = args->dp;
0352     struct xfs_mount    *mp = dp->i_mount;
0353     struct xfs_buf      *bp = NULL; /* data block buffer */
0354     xfs_dir2_data_entry_t   *dep;       /* data entry */
0355     xfs_dir2_data_unused_t  *dup;       /* unused entry */
0356     struct xfs_da_geometry  *geo = args->geo;
0357     xfs_dablk_t     rablk = 0;  /* current readahead block */
0358     xfs_dir2_off_t      curoff;     /* current overall offset */
0359     int         length;     /* temporary length value */
0360     int         byteoff;    /* offset in current block */
0361     unsigned int        offset = 0;
0362     int         error = 0;  /* error return value */
0363 
0364     /*
0365      * If the offset is at or past the largest allowed value,
0366      * give up right away.
0367      */
0368     if (ctx->pos >= XFS_DIR2_MAX_DATAPTR)
0369         return 0;
0370 
0371     /*
0372      * Inside the loop we keep the main offset value as a byte offset
0373      * in the directory file.
0374      */
0375     curoff = xfs_dir2_dataptr_to_byte(ctx->pos);
0376 
0377     /*
0378      * Loop over directory entries until we reach the end offset.
0379      * Get more blocks and readahead as necessary.
0380      */
0381     while (curoff < XFS_DIR2_LEAF_OFFSET) {
0382         uint8_t filetype;
0383 
0384         /*
0385          * If we have no buffer, or we're off the end of the
0386          * current buffer, need to get another one.
0387          */
0388         if (!bp || offset >= geo->blksize) {
0389             if (bp) {
0390                 xfs_trans_brelse(args->trans, bp);
0391                 bp = NULL;
0392             }
0393 
0394             if (*lock_mode == 0)
0395                 *lock_mode = xfs_ilock_data_map_shared(dp);
0396             error = xfs_dir2_leaf_readbuf(args, bufsize, &curoff,
0397                     &rablk, &bp);
0398             if (error || !bp)
0399                 break;
0400 
0401             xfs_iunlock(dp, *lock_mode);
0402             *lock_mode = 0;
0403 
0404             xfs_dir3_data_check(dp, bp);
0405             /*
0406              * Find our position in the block.
0407              */
0408             offset = geo->data_entry_offset;
0409             byteoff = xfs_dir2_byte_to_off(geo, curoff);
0410             /*
0411              * Skip past the header.
0412              */
0413             if (byteoff == 0)
0414                 curoff += geo->data_entry_offset;
0415             /*
0416              * Skip past entries until we reach our offset.
0417              */
0418             else {
0419                 while (offset < byteoff) {
0420                     dup = bp->b_addr + offset;
0421 
0422                     if (be16_to_cpu(dup->freetag)
0423                           == XFS_DIR2_DATA_FREE_TAG) {
0424 
0425                         length = be16_to_cpu(dup->length);
0426                         offset += length;
0427                         continue;
0428                     }
0429                     dep = bp->b_addr + offset;
0430                     length = xfs_dir2_data_entsize(mp,
0431                             dep->namelen);
0432                     offset += length;
0433                 }
0434                 /*
0435                  * Now set our real offset.
0436                  */
0437                 curoff =
0438                     xfs_dir2_db_off_to_byte(geo,
0439                         xfs_dir2_byte_to_db(geo, curoff),
0440                         offset);
0441                 if (offset >= geo->blksize)
0442                     continue;
0443             }
0444         }
0445 
0446         /*
0447          * We have a pointer to an entry.  Is it a live one?
0448          */
0449         dup = bp->b_addr + offset;
0450 
0451         /*
0452          * No, it's unused, skip over it.
0453          */
0454         if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
0455             length = be16_to_cpu(dup->length);
0456             offset += length;
0457             curoff += length;
0458             continue;
0459         }
0460 
0461         dep = bp->b_addr + offset;
0462         length = xfs_dir2_data_entsize(mp, dep->namelen);
0463         filetype = xfs_dir2_data_get_ftype(mp, dep);
0464 
0465         ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
0466         if (XFS_IS_CORRUPT(dp->i_mount,
0467                    !xfs_dir2_namecheck(dep->name,
0468                                dep->namelen))) {
0469             error = -EFSCORRUPTED;
0470             break;
0471         }
0472         if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
0473                 be64_to_cpu(dep->inumber),
0474                 xfs_dir3_get_dtype(dp->i_mount, filetype)))
0475             break;
0476 
0477         /*
0478          * Advance to next entry in the block.
0479          */
0480         offset += length;
0481         curoff += length;
0482         /* bufsize may have just been a guess; don't go negative */
0483         bufsize = bufsize > length ? bufsize - length : 0;
0484     }
0485 
0486     /*
0487      * All done.  Set output offset value to current offset.
0488      */
0489     if (curoff > xfs_dir2_dataptr_to_byte(XFS_DIR2_MAX_DATAPTR))
0490         ctx->pos = XFS_DIR2_MAX_DATAPTR & 0x7fffffff;
0491     else
0492         ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
0493     if (bp)
0494         xfs_trans_brelse(args->trans, bp);
0495     return error;
0496 }
0497 
0498 /*
0499  * Read a directory.
0500  *
0501  * If supplied, the transaction collects locked dir buffers to avoid
0502  * nested buffer deadlocks.  This function does not dirty the
0503  * transaction.  The caller must hold the IOLOCK (shared or exclusive)
0504  * before calling this function.
0505  */
0506 int
0507 xfs_readdir(
0508     struct xfs_trans    *tp,
0509     struct xfs_inode    *dp,
0510     struct dir_context  *ctx,
0511     size_t          bufsize)
0512 {
0513     struct xfs_da_args  args = { NULL };
0514     unsigned int        lock_mode;
0515     int         isblock;
0516     int         error;
0517 
0518     trace_xfs_readdir(dp);
0519 
0520     if (xfs_is_shutdown(dp->i_mount))
0521         return -EIO;
0522 
0523     ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
0524     ASSERT(xfs_isilocked(dp, XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL));
0525     XFS_STATS_INC(dp->i_mount, xs_dir_getdents);
0526 
0527     args.dp = dp;
0528     args.geo = dp->i_mount->m_dir_geo;
0529     args.trans = tp;
0530 
0531     if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
0532         return xfs_dir2_sf_getdents(&args, ctx);
0533 
0534     lock_mode = xfs_ilock_data_map_shared(dp);
0535     error = xfs_dir2_isblock(&args, &isblock);
0536     if (error)
0537         goto out_unlock;
0538 
0539     if (isblock) {
0540         error = xfs_dir2_block_getdents(&args, ctx, &lock_mode);
0541         goto out_unlock;
0542     }
0543 
0544     error = xfs_dir2_leaf_getdents(&args, ctx, bufsize, &lock_mode);
0545 
0546 out_unlock:
0547     if (lock_mode)
0548         xfs_iunlock(dp, lock_mode);
0549     return error;
0550 }