home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / fs / ext2 / dir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-26  |  6.1 KB  |  228 lines

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