root/fs/ext2/dir.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ext2_dir_read
  2. ext2_check_dir_entry
  3. ext2_readdir

   1 /*
   2  *  linux/fs/ext2/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  *  ext2 directory handling functions
  16  */
  17 
  18 #include <asm/segment.h>
  19 
  20 #include <linux/errno.h>
  21 #include <linux/fs.h>
  22 #include <linux/ext2_fs.h>
  23 #include <linux/sched.h>
  24 #include <linux/stat.h>
  25 
  26 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
  27 #define ROUND_UP(x) (((x)+3) & ~3)
  28 
  29 static int ext2_dir_read (struct inode * inode, struct file * filp,
     /* [previous][next][first][last][top][bottom][index][help] */
  30                             char * buf, int count)
  31 {
  32         return -EISDIR;
  33 }
  34 
  35 static int ext2_readdir (struct inode *, struct file *, struct dirent *, int);
  36 
  37 static struct file_operations ext2_dir_operations = {
  38         NULL,                   /* lseek - default */
  39         ext2_dir_read,          /* read */
  40         NULL,                   /* write - bad */
  41         ext2_readdir,           /* readdir */
  42         NULL,                   /* select - default */
  43         ext2_ioctl,             /* ioctl */
  44         NULL,                   /* mmap */
  45         NULL,                   /* no special open code */
  46         NULL,                   /* no special release code */
  47         file_fsync,             /* fsync */
  48         NULL,                   /* fasync */
  49         NULL,                   /* check_media_change */
  50         NULL                    /* revalidate */
  51 };
  52 
  53 /*
  54  * directories can handle most operations...
  55  */
  56 struct inode_operations ext2_dir_inode_operations = {
  57         &ext2_dir_operations,   /* default directory file-ops */
  58         ext2_create,            /* create */
  59         ext2_lookup,            /* lookup */
  60         ext2_link,              /* link */
  61         ext2_unlink,            /* unlink */
  62         ext2_symlink,           /* symlink */
  63         ext2_mkdir,             /* mkdir */
  64         ext2_rmdir,             /* rmdir */
  65         ext2_mknod,             /* mknod */
  66         ext2_rename,            /* rename */
  67         NULL,                   /* readlink */
  68         NULL,                   /* follow_link */
  69         NULL,                   /* bmap */
  70         ext2_truncate,          /* truncate */
  71         ext2_permission,        /* permission */
  72         NULL                    /* smap */
  73 };
  74 
  75 int ext2_check_dir_entry (char * function, struct inode * dir,
     /* [previous][next][first][last][top][bottom][index][help] */
  76                           struct ext2_dir_entry * de, struct buffer_head * bh,
  77                           unsigned long offset)
  78 {
  79         char * error_msg = NULL;
  80 
  81         if (de->rec_len < EXT2_DIR_REC_LEN(1))
  82                 error_msg = "rec_len is smaller than minimal";
  83         else if (de->rec_len % 4 != 0)
  84                 error_msg = "rec_len % 4 != 0";
  85         else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
  86                 error_msg = "rec_len is too small for name_len";
  87         else if (dir && ((char *) de - bh->b_data) + de->rec_len >
  88                  dir->i_sb->s_blocksize)
  89                 error_msg = "directory entry across blocks";
  90         else if (dir && de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
  91                 error_msg = "inode out of bounds";
  92 
  93         if (error_msg != NULL)
  94                 ext2_error (dir->i_sb, function, "bad directory entry: %s\n"
  95                             "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
  96                             error_msg, offset, (unsigned long) de->inode, de->rec_len,
  97                             de->name_len);
  98         return error_msg == NULL ? 1 : 0;
  99 }
 100 
 101 static int ext2_readdir (struct inode * inode, struct file * filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 102                          struct dirent * dirent, int count)
 103 {
 104         unsigned long offset, blk;
 105         int i, num, stored, dlen;
 106         struct buffer_head * bh, * tmp, * bha[16];
 107         struct ext2_dir_entry * de;
 108         struct super_block * sb;
 109         int err, version;
 110 
 111         if (!inode || !S_ISDIR(inode->i_mode))
 112                 return -EBADF;
 113         sb = inode->i_sb;
 114 
 115         stored = 0;
 116         bh = NULL;
 117         offset = filp->f_pos & (sb->s_blocksize - 1);
 118 
 119         while (count > 0 && !stored && filp->f_pos < inode->i_size) {
 120                 blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
 121                 bh = ext2_bread (inode, blk, 0, &err);
 122                 if (!bh) {
 123                         filp->f_pos += sb->s_blocksize - offset;
 124                         continue;
 125                 }
 126 
 127                 /*
 128                  * Do the readahead
 129                  */
 130                 if (!offset) {
 131                         for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
 132                              i > 0; i--) {
 133                                 tmp = ext2_getblk (inode, ++blk, 0, &err);
 134                                 if (tmp && !tmp->b_uptodate && !tmp->b_lock)
 135                                         bha[num++] = tmp;
 136                                 else
 137                                         brelse (tmp);
 138                         }
 139                         if (num) {
 140                                 ll_rw_block (READA, num, bha);
 141                                 for (i = 0; i < num; i++)
 142                                         brelse (bha[i]);
 143                         }
 144                 }
 145                 
 146 revalidate:
 147                 /* If the dir block has changed since the last call to
 148                  * readdir(2), then we might be pointing to an invalid
 149                  * dirent right now.  Scan from the start of the block
 150                  * to make sure. */
 151                 if (filp->f_version != inode->i_version) {
 152                         for (i = 0; i < sb->s_blocksize && i < offset; ) {
 153                                 de = (struct ext2_dir_entry *) 
 154                                         (bh->b_data + i);
 155                                 /* It's too expensive to do a full
 156                                  * dirent test each time round this
 157                                  * loop, but we do have to test at
 158                                  * least that it is non-zero.  A
 159                                  * failure will be detected in the
 160                                  * dirent test below. */
 161                                 if (de->rec_len < EXT2_DIR_REC_LEN(1))
 162                                         break;
 163                                 i += de->rec_len;
 164                         }
 165                         offset = i;
 166                         filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
 167                                 | offset;
 168                         filp->f_version = inode->i_version;
 169                 }
 170                 
 171                 while (count > 0 && filp->f_pos < inode->i_size 
 172                        && offset < sb->s_blocksize) {
 173                         de = (struct ext2_dir_entry *) (bh->b_data + offset);
 174                         if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
 175                                                    bh, offset)) {
 176                                 /* On error, skip the f_pos to the
 177                                    next block. */
 178                                 filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
 179                                               + sb->s_blocksize;
 180                                 brelse (bh);
 181                                 return stored;
 182                         }
 183                         if (de->inode) {
 184                                 dlen = ROUND_UP(NAME_OFFSET(dirent) 
 185                                                 + de->name_len + 1);
 186                                 /* Old libc libraries always use a
 187                                    count of 1. */
 188                                 if (count == 1 && !stored)
 189                                         count = dlen;
 190                                 if (count < dlen) {
 191                                         count = 0;
 192                                         break;
 193                                 }
 194 
 195                                 /* We might block in the next section
 196                                  * if the data destination is
 197                                  * currently swapped out.  So, use a
 198                                  * version stamp to detect whether or
 199                                  * not the directory has been modified
 200                                  * during the copy operation. */
 201                                 version = inode->i_version;
 202                                 i = de->name_len;
 203                                 memcpy_tofs (dirent->d_name, de->name, i);
 204                                 put_fs_long (de->inode, &dirent->d_ino);
 205                                 put_fs_byte (0, dirent->d_name + i);
 206                                 put_fs_word (i, &dirent->d_reclen);
 207                                 put_fs_long (dlen, &dirent->d_off);
 208                                 if (version != inode->i_version)
 209                                         goto revalidate;
 210                                 dcache_add(inode, de->name, de->name_len,
 211                                                  de->inode);
 212 
 213                                 stored += dlen;
 214                                 count -= dlen;
 215                                 ((char *) dirent) += dlen;
 216                         }
 217                         offset += de->rec_len;
 218                         filp->f_pos += de->rec_len;
 219                 }
 220                 offset = 0;
 221                 brelse (bh);
 222         }
 223         if (!IS_RDONLY(inode)) {
 224                 inode->i_atime = CURRENT_TIME;
 225                 inode->i_dirt = 1;
 226         }
 227         return stored;
 228 }

/* [previous][next][first][last][top][bottom][index][help] */