[PATCH] 2.4.0test1 - VFS return EIO instead of (eg) ENOSPACE

From: Theodore Y. Ts'o (tytso@MIT.EDU)
Date: Tue May 30 2000 - 01:55:42 EST


   From: Andreas Dilger <adilger@turbolabs.com>
   Date: Mon, 29 May 2000 11:29:21 -0600 (MDT)

   Not needed, as ext2_new_inode() always sets &err to some sane value.
   This is easily seen by looking in the ext2_new_inode() code.
   Linux-2.2.14 had it right here - should be simply "return err", but
   I think Ted changed this as part of an unrelated fix. Comments, Ted?

Yeah, that was caused by my beliving the comment (which wasn't removed
when ext2_new_inode was fixed) that said that ext2_new_inode didn't set
err in all cases.

   (*) There was one (probably very rare) error case where block_getblk()
   might return non-NULL: after bforget(result) jumps back to repeat: and
   then we get an error with ext2_alloc_block() - we return a bad result.
   I also fixed this in the attached patch.

Good catch; yup, that's a nasty race condition. There were a few minor
mistakes in your patch, though. There was one place where an error
return that really should have beeen ENOSPC was changed to return
EFBIG. Otherise, I thought your cleanups are fine.

Alan, could you please apply the following patch to 2.4.0test1-ac4?
In addition to fixing these cases, I also fixed a bug pointed out by
bcrl; namely, that in the integration of the LFS pathces into 2.3., the
code which set the LARGE_FILE flag in the superblock fell out of
ext2_file_write(). This adds the check to ext2_update_inode(), since
ext2_file_write() is no more.

                                                - Ted

Patch generated: on Tue May 30 02:48:17 EDT 2000 by tytso@snap.thunk.org
against Linux version 2.4.0test1-ac4
 
===================================================================
RCS file: fs/ext2/RCS/ialloc.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/ialloc.c
--- fs/ext2/ialloc.c 2000/05/29 20:05:45 1.1
+++ fs/ext2/ialloc.c 2000/05/29 20:09:21
@@ -305,7 +305,6 @@
 repeat:
         gdp = NULL; i=0;
         
- *err = -ENOSPC;
         if (S_ISDIR(mode)) {
                 avefreei = le32_to_cpu(es->s_free_inodes_count) /
                         sb->u.ext2_sb.s_groups_count;
@@ -387,6 +386,7 @@
         if (!gdp) {
                 unlock_super (sb);
                 iput(inode);
+ *err = -ENOSPC;
                 return NULL;
         }
         bitmap_nr = load_inode_bitmap (sb, i);
@@ -416,9 +416,8 @@
                         ext2_error (sb, "ext2_new_inode",
                                     "Free inodes count corrupted in group %d",
                                     i);
- unlock_super (sb);
- iput (inode);
- return NULL;
+ /* If we continue recover from this case */
+ gdp->bg_free_inodes_count = 0;
                 }
                 goto repeat;
         }
@@ -429,6 +428,7 @@
                             "block_group = %d,inode=%d", i, j);
                 unlock_super (sb);
                 iput (inode);
+ *err = EIO; /* Should never happen */
                 return NULL;
         }
         gdp->bg_free_inodes_count =
===================================================================
RCS file: fs/ext2/RCS/balloc.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/balloc.c
--- fs/ext2/balloc.c 2000/05/30 05:52:45 1.1
+++ fs/ext2/balloc.c 2000/05/30 05:53:48
@@ -473,11 +473,8 @@
                 if (i >= sb->u.ext2_sb.s_groups_count)
                         i = 0;
                 gdp = ext2_get_group_desc (sb, i, &bh2);
- if (!gdp) {
- *err = -EIO;
- unlock_super (sb);
- return 0;
- }
+ if (!gdp)
+ goto io_error;
                 if (le16_to_cpu(gdp->bg_free_blocks_count) > 0)
                         break;
         }
===================================================================
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/05/29 20:10:23 1.1
+++ fs/ext2/namei.c 2000/05/29 20:10:58
@@ -366,12 +366,9 @@
         struct inode * inode;
         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 -EIO;
+ return err;
 
         inode->i_op = &ext2_file_inode_operations;
         inode->i_fop = &ext2_file_operations;
@@ -397,7 +394,7 @@
 
         inode = ext2_new_inode (dir, mode, &err);
         if (!inode)
- return -EIO;
+ return err;
 
         inode->i_uid = current->fsuid;
         init_special_inode(inode, mode, rdev);
@@ -428,7 +425,7 @@
 
         inode = ext2_new_inode (dir, S_IFDIR, &err);
         if (!inode)
- return -EIO;
+ return err;
 
         inode->i_op = &ext2_dir_inode_operations;
         inode->i_fop = &ext2_dir_operations;
@@ -634,7 +631,7 @@
                 return -ENAMETOOLONG;
 
         if (!(inode = ext2_new_inode (dir, S_IFLNK, &err)))
- return -EIO;
+ return err;
 
         inode->i_mode = S_IFLNK | S_IRWXUGO;
 
===================================================================
RCS file: fs/ext2/RCS/inode.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/inode.c
--- fs/ext2/inode.c 2000/05/30 04:12:03 1.1
+++ fs/ext2/inode.c 2000/05/30 06:47:39
@@ -117,7 +117,7 @@
                 inode->u.ext2_i.i_prealloc_count--;
                 ext2_debug ("preallocation hit (%lu/%lu).\n",
                             ++alloc_hits, ++alloc_attempts);
-
+ *err = 0;
         } else {
                 ext2_discard_prealloc (inode);
                 ext2_debug ("preallocation miss (%lu/%lu).\n",
@@ -200,6 +200,7 @@
         return ret;
 }
 
+/* returns NULL and sets *err on error */
 static struct buffer_head * inode_getblk (struct inode * inode, int nr,
         int new_block, int * err, int metadata, long *phys, int *new)
 {
@@ -223,7 +224,6 @@
                         return NULL;
                 }
         }
- *err = -EFBIG;
 
         /* Check file limits.. */
         {
@@ -311,7 +311,7 @@
  * can fail due to: - not present
  * - out of space
  *
- * NULL return in the data case is mandatory.
+ * NULL return in the data case, or an error, is mandatory.
  */
 static struct buffer_head * block_getblk (struct inode * inode,
           struct buffer_head * bh, int nr,
@@ -341,6 +341,7 @@
                         if (tmp == le32_to_cpu(*p))
                                 goto out;
                         brelse (result);
+ result = NULL;
                         goto repeat;
                 } else {
                         *phys = tmp;
@@ -487,9 +488,9 @@
 #define GET_INODE_PTR(x) \
                 inode_getblk(inode, x, iblock, &err, 1, NULL, NULL)
 #define GET_INDIRECT_DATABLOCK(x) \
- block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new);
+ block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new)
 #define GET_INDIRECT_PTR(x) \
- block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL);
+ block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL)
 
         if (ptr < direct_blocks) {
                 bh = GET_INODE_DATABLOCK(ptr);
@@ -547,13 +548,11 @@
 struct buffer_head * ext2_getblk(struct inode * inode, long block, int create, int * err)
 {
         struct buffer_head dummy;
- int error;
 
         dummy.b_state = 0;
         dummy.b_blocknr = -1000;
- error = ext2_get_block(inode, block, &dummy, create);
- *err = error;
- if (!error && buffer_mapped(&dummy)) {
+ *err = ext2_get_block(inode, block, &dummy, create);
+ if (!*err && buffer_mapped(&dummy)) {
                 struct buffer_head *bh;
                 bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize);
                 if (buffer_new(&dummy)) {
@@ -881,8 +880,23 @@
         raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl);
         if (S_ISDIR(inode->i_mode))
                 raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl);
- else
+ else {
                 raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
+ if (inode->i_size >> 31) {
+ struct super_block *sb = inode->i_sb;
+ struct ext2_super_block *es = sb->u.ext2_sb.s_es;
+ if (!(es->s_feature_ro_compat & cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) {
+ /* If this is the first large file
+ * created, add a flag to the superblock
+ * SMP Note: we're currently protected by the
+ * big kernel lock here, so this will need
+ * to be changed if that's no longer true.
+ */
+ es->s_feature_ro_compat |= cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE);
+ ext2_write_super(sb);
+ }
+ }
+ }
 
         raw_inode->i_generation = cpu_to_le32(inode->i_generation);
         if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))

-
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 : Wed May 31 2000 - 21:00:23 EST