linux-old/fs/ext3/dir.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/ext3/dir.c
   3 *
   4 * Copyright (C) 1992, 1993, 1994, 1995
   5 * Remy Card (card@masi.ibp.fr)
   6 * Laboratoire MASI - Institut Blaise Pascal
   7 * Universite Pierre et Marie Curie (Paris VI)
   8 *
   9 *  from
  10 *
  11 *  linux/fs/minix/dir.c
  12 *
  13 *  Copyright (C) 1991, 1992  Linus Torvalds
  14 *
  15 *  ext3 directory handling functions
  16 *
  17 *  Big-endian to little-endian byte-swapping/bitmaps by
  18 *        David S. Miller (davem@caip.rutgers.edu), 1995
  19 */
  20
  21#include <linux/fs.h>
  22#include <linux/jbd.h>
  23#include <linux/ext3_fs.h>
  24
  25static unsigned char ext3_filetype_table[] = {
  26        DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
  27};
  28
  29static int ext3_readdir(struct file *, void *, filldir_t);
  30
  31struct file_operations ext3_dir_operations = {
  32        read:           generic_read_dir,
  33        readdir:        ext3_readdir,           /* BKL held */
  34        ioctl:          ext3_ioctl,             /* BKL held */
  35        fsync:          ext3_sync_file,         /* BKL held */
  36};
  37
  38int ext3_check_dir_entry (const char * function, struct inode * dir,
  39                          struct ext3_dir_entry_2 * de,
  40                          struct buffer_head * bh,
  41                          unsigned long offset)
  42{
  43        const char * error_msg = NULL;
  44        const int rlen = le16_to_cpu(de->rec_len);
  45
  46        if (rlen < EXT3_DIR_REC_LEN(1))
  47                error_msg = "rec_len is smaller than minimal";
  48        else if (rlen % 4 != 0)
  49                error_msg = "rec_len % 4 != 0";
  50        else if (rlen < EXT3_DIR_REC_LEN(de->name_len))
  51                error_msg = "rec_len is too small for name_len";
  52        else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize)
  53                error_msg = "directory entry across blocks";
  54        else if (le32_to_cpu(de->inode) >
  55                        le32_to_cpu(dir->i_sb->u.ext3_sb.s_es->s_inodes_count))
  56                error_msg = "inode out of bounds";
  57
  58        if (error_msg != NULL)
  59                ext3_error (dir->i_sb, function,
  60                        "bad entry in directory #%lu: %s - "
  61                        "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
  62                        dir->i_ino, error_msg, offset,
  63                        (unsigned long) le32_to_cpu(de->inode),
  64                        rlen, de->name_len);
  65        return error_msg == NULL ? 1 : 0;
  66}
  67
  68static int ext3_readdir(struct file * filp,
  69                         void * dirent, filldir_t filldir)
  70{
  71        int error = 0;
  72        unsigned long offset, blk;
  73        int i, num, stored;
  74        struct buffer_head * bh, * tmp, * bha[16];
  75        struct ext3_dir_entry_2 * de;
  76        struct super_block * sb;
  77        int err;
  78        struct inode *inode = filp->f_dentry->d_inode;
  79
  80        sb = inode->i_sb;
  81
  82        stored = 0;
  83        bh = NULL;
  84        offset = filp->f_pos & (sb->s_blocksize - 1);
  85
  86        while (!error && !stored && filp->f_pos < inode->i_size) {
  87                blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb);
  88                bh = ext3_bread (0, inode, blk, 0, &err);
  89                if (!bh) {
  90                        ext3_error (sb, "ext3_readdir",
  91                                "directory #%lu contains a hole at offset %lu",
  92                                inode->i_ino, (unsigned long)filp->f_pos);
  93                        filp->f_pos += sb->s_blocksize - offset;
  94                        continue;
  95                }
  96
  97                /*
  98                 * Do the readahead
  99                 */
 100                if (!offset) {
 101                        for (i = 16 >> (EXT3_BLOCK_SIZE_BITS(sb) - 9), num = 0;
 102                             i > 0; i--) {
 103                                tmp = ext3_getblk (NULL, inode, ++blk, 0, &err);
 104                                if (tmp && !buffer_uptodate(tmp) &&
 105                                                !buffer_locked(tmp))
 106                                        bha[num++] = tmp;
 107                                else
 108                                        brelse (tmp);
 109                        }
 110                        if (num) {
 111                                ll_rw_block (READA, num, bha);
 112                                for (i = 0; i < num; i++)
 113                                        brelse (bha[i]);
 114                        }
 115                }
 116                
 117revalidate:
 118                /* If the dir block has changed since the last call to
 119                 * readdir(2), then we might be pointing to an invalid
 120                 * dirent right now.  Scan from the start of the block
 121                 * to make sure. */
 122                if (filp->f_version != inode->i_version) {
 123                        for (i = 0; i < sb->s_blocksize && i < offset; ) {
 124                                de = (struct ext3_dir_entry_2 *) 
 125                                        (bh->b_data + i);
 126                                /* It's too expensive to do a full
 127                                 * dirent test each time round this
 128                                 * loop, but we do have to test at
 129                                 * least that it is non-zero.  A
 130                                 * failure will be detected in the
 131                                 * dirent test below. */
 132                                if (le16_to_cpu(de->rec_len) <
 133                                                EXT3_DIR_REC_LEN(1))
 134                                        break;
 135                                i += le16_to_cpu(de->rec_len);
 136                        }
 137                        offset = i;
 138                        filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
 139                                | offset;
 140                        filp->f_version = inode->i_version;
 141                }
 142                
 143                while (!error && filp->f_pos < inode->i_size 
 144                       && offset < sb->s_blocksize) {
 145                        de = (struct ext3_dir_entry_2 *) (bh->b_data + offset);
 146                        if (!ext3_check_dir_entry ("ext3_readdir", inode, de,
 147                                                   bh, offset)) {
 148                                /* On error, skip the f_pos to the
 149                                   next block. */
 150                                filp->f_pos = (filp->f_pos |
 151                                                (sb->s_blocksize - 1)) + 1;
 152                                brelse (bh);
 153                                return stored;
 154                        }
 155                        offset += le16_to_cpu(de->rec_len);
 156                        if (le32_to_cpu(de->inode)) {
 157                                /* We might block in the next section
 158                                 * if the data destination is
 159                                 * currently swapped out.  So, use a
 160                                 * version stamp to detect whether or
 161                                 * not the directory has been modified
 162                                 * during the copy operation.
 163                                 */
 164                                unsigned long version = filp->f_version;
 165                                unsigned char d_type = DT_UNKNOWN;
 166
 167                                if (EXT3_HAS_INCOMPAT_FEATURE(sb,
 168                                                EXT3_FEATURE_INCOMPAT_FILETYPE)
 169                                                && de->file_type < EXT3_FT_MAX)
 170                                        d_type =
 171                                          ext3_filetype_table[de->file_type];
 172                                error = filldir(dirent, de->name,
 173                                                de->name_len,
 174                                                filp->f_pos,
 175                                                le32_to_cpu(de->inode),
 176                                                d_type);
 177                                if (error)
 178                                        break;
 179                                if (version != filp->f_version)
 180                                        goto revalidate;
 181                                stored ++;
 182                        }
 183                        filp->f_pos += le16_to_cpu(de->rec_len);
 184                }
 185                offset = 0;
 186                brelse (bh);
 187        }
 188        UPDATE_ATIME(inode);
 189        return 0;
 190}
 191
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.