Patch for Linux 2.3.99-pre3

From: tytso@mit.edu
Date: Sun Mar 26 2000 - 07:50:55 EST


Linus,

        I'd appreciate it if you would apply the following patch to the
mainline kernel sources. It does the following:

        * Fixes a race condition reported by Soohoon Lee;
          mark_dirty_buffer() can block, leading to ugly cicumstances.
          (This is most of the patch, as I moved functionality into
          ext2_add_entry in an effort to simplify codepaths).

        * Re-introduces "nocheck" option to mounting ext2 filesystems
          without checking. A lot of people used this option, so
          removing in 2.3.99 caused the mount to fail.

                                                - Ted

Patch generated: on Sun Mar 26 07:11:16 EST 2000 by tytso@trampoline.thunk.org
against Linux version 2.3.99
 
===================================================================
RCS file: fs/ext2/RCS/super.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/super.c
--- fs/ext2/super.c 2000/03/25 00:37:16 1.1
+++ fs/ext2/super.c 2000/03/25 00:38:05
@@ -197,6 +197,8 @@
                         set_opt (*mount_options, GRPID);
                 else if (!strcmp (this_char, "minixdf"))
                         set_opt (*mount_options, MINIX_DF);
+ else if (!strcmp (this_char, "nocheck"))
+ clear_opt (*mount_options, CHECK);
                 else if (!strcmp (this_char, "nogrpid") ||
                          !strcmp (this_char, "sysvgroups"))
                         clear_opt (*mount_options, GRPID);
@@ -300,13 +302,6 @@
                 }
 #endif
         }
-#if 0 /* ibasket's still have unresolved bugs... -DaveM */
-
- /* [T. Schoebel-Theuer] This limit should be maintained on disk.
- * This is just provisionary.
- */
- sb->s_ibasket_max = 100;
-#endif
 }
 
 static int ext2_check_descriptors (struct super_block * sb)
===================================================================
RCS file: fs/ext2/RCS/namei.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/namei.c
--- fs/ext2/namei.c 2000/03/25 00:37:16 1.1
+++ fs/ext2/namei.c 2000/03/26 07:42:58
@@ -182,61 +182,71 @@
         return NULL;
 }
 
+static inline void ext2_set_de_type(struct super_block *sb,
+ struct ext2_dir_entry_2 *de,
+ umode_t mode) {
+ if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
+ return;
+ if (S_ISREG(mode))
+ de->file_type = EXT2_FT_REG_FILE;
+ else if (S_ISDIR(mode))
+ de->file_type = EXT2_FT_DIR;
+ else if (S_ISLNK(mode))
+ de->file_type = EXT2_FT_SYMLINK;
+ else if (S_ISSOCK(mode))
+ de->file_type = EXT2_FT_SOCK;
+ else if (S_ISFIFO(mode))
+ de->file_type = EXT2_FT_FIFO;
+ else if (S_ISCHR(mode))
+ de->file_type = EXT2_FT_CHRDEV;
+ else if (S_ISBLK(mode))
+ de->file_type = EXT2_FT_BLKDEV;
+}
+
 /*
  * ext2_add_entry()
  *
- * adds a file entry to the specified directory, using the same
- * semantics as ext2_find_entry(). It returns NULL if it failed.
- *
- * NOTE!! The inode part of 'de' is left at 0 - which means you
- * may not sleep between calling this and putting something into
- * the entry, as someone else might have used it while you slept.
+ * adds a file entry to the specified directory.
  */
-static struct buffer_head * ext2_add_entry (struct inode * dir,
- const char * name, int namelen,
- struct ext2_dir_entry_2 ** res_dir,
- int *err)
+int ext2_add_entry (struct inode * dir, const char * name, int namelen,
+ struct inode *inode)
 {
         unsigned long offset;
         unsigned short rec_len;
         struct buffer_head * bh;
         struct ext2_dir_entry_2 * de, * de1;
         struct super_block * sb;
+ int retval;
 
- *err = -EINVAL;
- *res_dir = NULL;
         if (!dir || !dir->i_nlink)
- return NULL;
+ return -EINVAL;
         sb = dir->i_sb;
 
         if (!namelen)
- return NULL;
+ return -EINVAL;
         /*
          * Is this a busy deleted directory? Can't create new files if so
          */
         if (dir->i_size == 0)
         {
- *err = -ENOENT;
- return NULL;
+ return -ENOENT;
         }
- bh = ext2_bread (dir, 0, 0, err);
+ bh = ext2_bread (dir, 0, 0, &retval);
         if (!bh)
- return NULL;
+ return retval;
         rec_len = EXT2_DIR_REC_LEN(namelen);
         offset = 0;
         de = (struct ext2_dir_entry_2 *) bh->b_data;
- *err = -ENOSPC;
         while (1) {
                 if ((char *)de >= sb->s_blocksize + bh->b_data) {
                         brelse (bh);
                         bh = NULL;
- bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, err);
+ bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &retval);
                         if (!bh)
- return NULL;
+ return retval;
                         if (dir->i_size <= offset) {
                                 if (dir->i_size == 0) {
- *err = -ENOENT;
- return NULL;
+ return -ENOENT;
                                 }
 
                                 ext2_debug ("creating next block\n");
@@ -256,14 +266,12 @@
                 }
                 if (!ext2_check_dir_entry ("ext2_add_entry", dir, de, bh,
                                            offset)) {
- *err = -ENOENT;
                         brelse (bh);
- return NULL;
+ return -ENOENT;
                 }
                 if (ext2_match (namelen, name, de)) {
- *err = -EEXIST;
                                 brelse (bh);
- return NULL;
+ return -EEXIST;
                 }
                 if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) ||
                     (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
@@ -276,7 +284,11 @@
                                 de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len));
                                 de = de1;
                         }
- de->inode = 0;
+ if (inode) {
+ de->inode = cpu_to_le32(inode->i_ino);
+ ext2_set_de_type(dir->i_sb, de, inode->i_mode);
+ } else
+ de->inode = 0;
                         de->name_len = namelen;
                         de->file_type = 0;
                         memcpy (de->name, name, namelen);
@@ -296,22 +308,26 @@
                         mark_inode_dirty(dir);
                         dir->i_version = ++event;
                         mark_buffer_dirty(bh, 1);
- *res_dir = de;
- *err = 0;
- return bh;
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ brelse(bh);
+ return 0;
                 }
                 offset += le16_to_cpu(de->rec_len);
                 de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
         }
         brelse (bh);
- return NULL;
+ return -ENOSPC;
 }
 
 /*
  * ext2_delete_entry deletes a directory entry by merging it with the
  * previous entry
  */
-static int ext2_delete_entry (struct ext2_dir_entry_2 * dir,
+static int ext2_delete_entry (struct inode * dir,
+ struct ext2_dir_entry_2 * de_del,
                               struct buffer_head * bh)
 {
         struct ext2_dir_entry_2 * de, * pde;
@@ -324,13 +340,19 @@
                 if (!ext2_check_dir_entry ("ext2_delete_entry", NULL,
                                            de, bh, i))
                         return -EIO;
- if (de == dir) {
+ if (de == de_del) {
                         if (pde)
                                 pde->rec_len =
                                         cpu_to_le16(le16_to_cpu(pde->rec_len) +
- le16_to_cpu(dir->rec_len));
+ le16_to_cpu(de->rec_len));
                         else
- dir->inode = 0;
+ de->inode = 0;
+ dir->i_version = ++event;
+ mark_buffer_dirty(bh, 1);
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
                         return 0;
                 }
                 i += le16_to_cpu(de->rec_len);
@@ -340,27 +362,6 @@
         return -ENOENT;
 }
 
-static inline void ext2_set_de_type(struct super_block *sb,
- struct ext2_dir_entry_2 *de,
- umode_t mode) {
- if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
- return;
- if (S_ISREG(mode))
- de->file_type = EXT2_FT_REG_FILE;
- else if (S_ISDIR(mode))
- de->file_type = EXT2_FT_DIR;
- else if (S_ISLNK(mode))
- de->file_type = EXT2_FT_SYMLINK;
- else if (S_ISSOCK(mode))
- de->file_type = EXT2_FT_SOCK;
- else if (S_ISFIFO(mode))
- de->file_type = EXT2_FT_FIFO;
- else if (S_ISCHR(mode))
- de->file_type = EXT2_FT_CHRDEV;
- else if (S_ISBLK(mode))
- de->file_type = EXT2_FT_BLKDEV;
-}
-
 /*
  * By the time this is called, we already have created
  * the directory cache entry for the new file, but it
@@ -372,38 +373,28 @@
 static int ext2_create (struct inode * dir, struct dentry * dentry, int mode)
 {
         struct inode * inode;
- struct buffer_head * bh;
- struct ext2_dir_entry_2 * de;
- int err = -EIO;
+ int err;
 
         /*
          * N.B. Several error exits in ext2_new_inode don't set err.
          */
         inode = ext2_new_inode (dir, mode, &err);
         if (!inode)
- return err;
+ return -EIO;
 
         inode->i_op = &ext2_file_inode_operations;
         inode->i_fop = &ext2_file_operations;
         inode->i_mapping->a_ops = &ext2_aops;
         inode->i_mode = mode;
         mark_inode_dirty(inode);
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh) {
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err) {
                 inode->i_nlink--;
                 mark_inode_dirty(inode);
                 iput (inode);
                 return err;
         }
- de->inode = cpu_to_le32(inode->i_ino);
- ext2_set_de_type(dir->i_sb, de, S_IFREG);
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
         d_instantiate(dentry, inode);
         return 0;
 }
@@ -411,56 +402,42 @@
 static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
 {
         struct inode * inode;
- struct buffer_head * bh;
- struct ext2_dir_entry_2 * de;
- int err = -EIO;
+ int err;
 
         inode = ext2_new_inode (dir, mode, &err);
         if (!inode)
- goto out;
+ return -EIO;
 
         inode->i_uid = current->fsuid;
         init_special_inode(inode, mode, rdev);
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err)
                 goto out_no_entry;
- de->inode = cpu_to_le32(inode->i_ino);
- dir->i_version = ++event;
- ext2_set_de_type(dir->i_sb, de, inode->i_mode);
         mark_inode_dirty(inode);
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
         d_instantiate(dentry, inode);
- brelse(bh);
- err = 0;
-out:
- return err;
+ return 0;
 
 out_no_entry:
         inode->i_nlink--;
         mark_inode_dirty(inode);
         iput(inode);
- goto out;
+ return err;
 }
 
 static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 {
         struct inode * inode;
- struct buffer_head * bh, * dir_block;
+ struct buffer_head * dir_block;
         struct ext2_dir_entry_2 * de;
         int err;
 
- err = -EMLINK;
         if (dir->i_nlink >= EXT2_LINK_MAX)
- goto out;
+ return -EMLINK;
 
- err = -EIO;
         inode = ext2_new_inode (dir, S_IFDIR, &err);
         if (!inode)
- goto out;
+ return -EIO;
 
         inode->i_op = &ext2_dir_inode_operations;
         inode->i_fop = &ext2_dir_operations;
@@ -471,7 +448,7 @@
                 inode->i_nlink--; /* is this nlink == 0? */
                 mark_inode_dirty(inode);
                 iput (inode);
- return err;
+ return -EIO;
         }
         de = (struct ext2_dir_entry_2 *) dir_block->b_data;
         de->inode = cpu_to_le32(inode->i_ino);
@@ -492,31 +469,21 @@
         if (dir->i_mode & S_ISGID)
                 inode->i_mode |= S_ISGID;
         mark_inode_dirty(inode);
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err)
                 goto out_no_entry;
- de->inode = cpu_to_le32(inode->i_ino);
- ext2_set_de_type(dir->i_sb, de, S_IFDIR);
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
         dir->i_nlink++;
         dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
         mark_inode_dirty(dir);
         d_instantiate(dentry, inode);
- brelse (bh);
- err = 0;
-out:
- return err;
+ return 0;
 
 out_no_entry:
         inode->i_nlink = 0;
         mark_inode_dirty(inode);
         iput (inode);
- goto out;
+ return err;
 }
 
 /*
@@ -604,15 +571,9 @@
         if (!empty_dir (inode))
                 goto end_rmdir;
 
- retval = ext2_delete_entry (de, bh);
- dir->i_version = ++event;
+ retval = ext2_delete_entry(dir, de, bh);
         if (retval)
                 goto end_rmdir;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
         if (inode->i_nlink != 2)
                 ext2_warning (inode->i_sb, "ext2_rmdir",
                               "empty directory has nlink!=2 (%d)",
@@ -657,15 +618,9 @@
                               inode->i_ino, inode->i_nlink);
                 inode->i_nlink = 1;
         }
- retval = ext2_delete_entry (de, bh);
+ retval = ext2_delete_entry(dir, de, bh);
         if (retval)
                 goto end_unlink;
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
         dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
         mark_inode_dirty(dir);
@@ -683,18 +638,14 @@
 static int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname)
 {
         struct inode * inode;
- struct ext2_dir_entry_2 * de;
- struct buffer_head * bh = NULL;
         int l, err;
 
- err = -ENAMETOOLONG;
         l = strlen(symname)+1;
         if (l > dir->i_sb->s_blocksize)
- goto out;
+ return -ENAMETOOLONG;
 
- err = -EIO;
         if (!(inode = ext2_new_inode (dir, S_IFLNK, &err)))
- goto out;
+ return -EIO;
 
         inode->i_mode = S_IFLNK | S_IRWXUGO;
 
@@ -711,36 +662,24 @@
         }
         mark_inode_dirty(inode);
 
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err)
                 goto out_no_entry;
- de->inode = cpu_to_le32(inode->i_ino);
- ext2_set_de_type(dir->i_sb, de, S_IFLNK);
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
         d_instantiate(dentry, inode);
- err = 0;
-out:
- return err;
+ return 0;
 
 out_no_entry:
         inode->i_nlink--;
         mark_inode_dirty(inode);
         iput (inode);
- goto out;
+ return err;
 }
 
 static int ext2_link (struct dentry * old_dentry,
                 struct inode * dir, struct dentry *dentry)
 {
         struct inode *inode = old_dentry->d_inode;
- struct ext2_dir_entry_2 * de;
- struct buffer_head * bh;
         int err;
 
         if (S_ISDIR(inode->i_mode))
@@ -748,20 +687,12 @@
 
         if (inode->i_nlink >= EXT2_LINK_MAX)
                 return -EMLINK;
-
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
+
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err)
                 return err;
 
- de->inode = cpu_to_le32(inode->i_ino);
- ext2_set_de_type(dir->i_sb, de, inode->i_mode);
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
         inode->i_nlink++;
         inode->i_ctime = CURRENT_TIME;
         mark_inode_dirty(inode);
@@ -829,14 +760,26 @@
                         goto end_rename;
         }
         if (!new_bh) {
- new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name,
- new_dentry->d_name.len, &new_de,
- &retval);
- if (!new_bh)
+ retval = ext2_add_entry (new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len,
+ old_inode);
+ if (retval)
                         goto end_rename;
+ } else {
+ new_de->inode = le32_to_cpu(old_inode->i_ino);
+ if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ new_de->file_type = old_de->file_type;
+ new_dir->i_version = ++event;
+ mark_buffer_dirty(new_bh, 1);
+ if (IS_SYNC(new_dir)) {
+ ll_rw_block (WRITE, 1, &new_bh);
+ wait_on_buffer (new_bh);
+ }
+ brelse(new_bh);
+ new_bh = NULL;
         }
- new_dir->i_version = ++event;
-
+
         /*
          * Like most other Unix systems, set the ctime for inodes on a
          * rename.
@@ -847,14 +790,8 @@
         /*
          * ok, that's it
          */
- new_de->inode = le32_to_cpu(old_inode->i_ino);
- if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- new_de->file_type = old_de->file_type;
-
- ext2_delete_entry (old_de, old_bh);
+ ext2_delete_entry(old_dir, old_de, old_bh);
 
- old_dir->i_version = ++event;
         if (new_inode) {
                 new_inode->i_nlink--;
                 new_inode->i_ctime = CURRENT_TIME;
@@ -876,16 +813,6 @@
                         new_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
                         mark_inode_dirty(new_dir);
                 }
- }
- mark_buffer_dirty(old_bh, 1);
- if (IS_SYNC(old_dir)) {
- ll_rw_block (WRITE, 1, &old_bh);
- wait_on_buffer (old_bh);
- }
- mark_buffer_dirty(new_bh, 1);
- if (IS_SYNC(new_dir)) {
- ll_rw_block (WRITE, 1, &new_bh);
- wait_on_buffer (new_bh);
         }
 
         retval = 0;
===================================================================
RCS file: include/linux/RCS/fs.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/fs.h
--- include/linux/fs.h 2000/03/25 00:37:16 1.1
+++ include/linux/fs.h 2000/03/25 09:19:37
@@ -79,7 +79,6 @@
 #define FS_NO_PRELIM 4 /* prevent preloading of dentries, even if
                            * FS_NO_DCACHE is not set.
                            */
-#define FS_IBASKET 8 /* FS does callback to free_ibasket() if space gets low. */
 
 /*
  * These are the fs-independent mount-flags: up to 16 flags are supported
@@ -582,9 +581,6 @@
         struct dentry *s_root;
         wait_queue_head_t s_wait;
 
- struct inode *s_ibasket;
- short int s_ibasket_count;
- short int s_ibasket_max;
         struct list_head s_dirty; /* dirty inodes */
         struct list_head s_files;
 

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Mar 31 2000 - 21:00:17 EST