Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  linux/fs/affs/dir.c
0004  *
0005  *  (c) 1996  Hans-Joachim Widmaier - Rewritten
0006  *
0007  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
0008  *
0009  *  (C) 1992  Eric Youngdale Modified for ISO 9660 filesystem.
0010  *
0011  *  (C) 1991  Linus Torvalds - minix filesystem
0012  *
0013  *  affs directory handling functions
0014  *
0015  */
0016 
0017 #include <linux/iversion.h>
0018 #include "affs.h"
0019 
0020 static int affs_readdir(struct file *, struct dir_context *);
0021 
0022 const struct file_operations affs_dir_operations = {
0023     .read       = generic_read_dir,
0024     .llseek     = generic_file_llseek,
0025     .iterate_shared = affs_readdir,
0026     .fsync      = affs_file_fsync,
0027 };
0028 
0029 /*
0030  * directories can handle most operations...
0031  */
0032 const struct inode_operations affs_dir_inode_operations = {
0033     .create     = affs_create,
0034     .lookup     = affs_lookup,
0035     .link       = affs_link,
0036     .unlink     = affs_unlink,
0037     .symlink    = affs_symlink,
0038     .mkdir      = affs_mkdir,
0039     .rmdir      = affs_rmdir,
0040     .rename     = affs_rename2,
0041     .setattr    = affs_notify_change,
0042 };
0043 
0044 static int
0045 affs_readdir(struct file *file, struct dir_context *ctx)
0046 {
0047     struct inode        *inode = file_inode(file);
0048     struct super_block  *sb = inode->i_sb;
0049     struct buffer_head  *dir_bh = NULL;
0050     struct buffer_head  *fh_bh = NULL;
0051     unsigned char       *name;
0052     int          namelen;
0053     u32          i;
0054     int          hash_pos;
0055     int          chain_pos;
0056     u32          ino;
0057     int          error = 0;
0058 
0059     pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos);
0060 
0061     if (ctx->pos < 2) {
0062         file->private_data = (void *)0;
0063         if (!dir_emit_dots(file, ctx))
0064             return 0;
0065     }
0066 
0067     affs_lock_dir(inode);
0068     chain_pos = (ctx->pos - 2) & 0xffff;
0069     hash_pos  = (ctx->pos - 2) >> 16;
0070     if (chain_pos == 0xffff) {
0071         affs_warning(sb, "readdir", "More than 65535 entries in chain");
0072         chain_pos = 0;
0073         hash_pos++;
0074         ctx->pos = ((hash_pos << 16) | chain_pos) + 2;
0075     }
0076     dir_bh = affs_bread(sb, inode->i_ino);
0077     if (!dir_bh)
0078         goto out_unlock_dir;
0079 
0080     /* If the directory hasn't changed since the last call to readdir(),
0081      * we can jump directly to where we left off.
0082      */
0083     ino = (u32)(long)file->private_data;
0084     if (ino && inode_eq_iversion(inode, file->f_version)) {
0085         pr_debug("readdir() left off=%d\n", ino);
0086         goto inside;
0087     }
0088 
0089     ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
0090     for (i = 0; ino && i < chain_pos; i++) {
0091         fh_bh = affs_bread(sb, ino);
0092         if (!fh_bh) {
0093             affs_error(sb, "readdir","Cannot read block %d", i);
0094             error = -EIO;
0095             goto out_brelse_dir;
0096         }
0097         ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
0098         affs_brelse(fh_bh);
0099         fh_bh = NULL;
0100     }
0101     if (ino)
0102         goto inside;
0103     hash_pos++;
0104 
0105     for (; hash_pos < AFFS_SB(sb)->s_hashsize; hash_pos++) {
0106         ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
0107         if (!ino)
0108             continue;
0109         ctx->pos = (hash_pos << 16) + 2;
0110 inside:
0111         do {
0112             fh_bh = affs_bread(sb, ino);
0113             if (!fh_bh) {
0114                 affs_error(sb, "readdir",
0115                        "Cannot read block %d", ino);
0116                 break;
0117             }
0118 
0119             namelen = min(AFFS_TAIL(sb, fh_bh)->name[0],
0120                       (u8)AFFSNAMEMAX);
0121             name = AFFS_TAIL(sb, fh_bh)->name + 1;
0122             pr_debug("readdir(): dir_emit(\"%.*s\", ino=%u), hash=%d, f_pos=%llx\n",
0123                  namelen, name, ino, hash_pos, ctx->pos);
0124 
0125             if (!dir_emit(ctx, name, namelen, ino, DT_UNKNOWN))
0126                 goto done;
0127             ctx->pos++;
0128             ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
0129             affs_brelse(fh_bh);
0130             fh_bh = NULL;
0131         } while (ino);
0132     }
0133 done:
0134     file->f_version = inode_query_iversion(inode);
0135     file->private_data = (void *)(long)ino;
0136     affs_brelse(fh_bh);
0137 
0138 out_brelse_dir:
0139     affs_brelse(dir_bh);
0140 
0141 out_unlock_dir:
0142     affs_unlock_dir(inode);
0143     return error;
0144 }