ext4: More buffer head reference leaks
authorCurt Wohlgemuth <curtw@google.com>
Fri, 17 Jul 2009 14:54:08 +0000 (10:54 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 17 Jul 2009 14:54:08 +0000 (10:54 -0400)
After the patch I posted last week regarding buffer head ref leaks in
no-journal mode, I looked at all the code that uses buffer heads and
searched for more potential leaks.

The patch below fixes the issues I found; these can occur even when a
journal is present.

The change to inode.c fixes a double release if
ext4_journal_get_create_access() fails.

The changes to namei.c are more complicated.  add_dirent_to_buf() will
release the input buffer head EXCEPT when it returns -ENOSPC.  There are
some callers of this routine that don't always do the brelse() in the event
that -ENOSPC is returned.  Unfortunately, to put this fix into ext4_add_entry()
required capturing the return value of make_indexed_dir() and
add_dirent_to_buf().

Signed-off-by: Curt Wohlgemuth <curtw@google.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
fs/ext4/inode.c
fs/ext4/namei.c

index f9c642b22efabb56f5ec3ae109bf2eaf75beee8a..deb14a728791aac05cdf9e60010268ae36da05a3 100644 (file)
@@ -762,8 +762,9 @@ static int ext4_alloc_branch(handle_t *handle, struct inode *inode,
                BUFFER_TRACE(bh, "call get_create_access");
                err = ext4_journal_get_create_access(handle, bh);
                if (err) {
+                       /* Don't brelse(bh) here; it's done in
+                        * ext4_journal_forget() below */
                        unlock_buffer(bh);
-                       brelse(bh);
                        goto failed;
                }
 
index 114abe5d2c1df437fb173f00abc78baed338ba3d..fea14dbd3c224ff74511df76941ad1522edcf9d0 100644 (file)
@@ -1518,8 +1518,12 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
                        return retval;
 
                if (blocks == 1 && !dx_fallback &&
-                   EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX))
-                       return make_indexed_dir(handle, dentry, inode, bh);
+                   EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
+                       retval = make_indexed_dir(handle, dentry, inode, bh);
+                       if (retval == -ENOSPC)
+                               brelse(bh);
+                       return retval;
+               }
                brelse(bh);
        }
        bh = ext4_append(handle, dir, &block, &retval);
@@ -1528,7 +1532,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        de->inode = 0;
        de->rec_len = ext4_rec_len_to_disk(blocksize, blocksize);
-       return add_dirent_to_buf(handle, dentry, inode, de, bh);
+       retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
+       if (retval == -ENOSPC)
+               brelse(bh);
+       return retval;
 }
 
 /*
@@ -1657,7 +1664,8 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
        if (!de)
                goto cleanup;
        err = add_dirent_to_buf(handle, dentry, inode, de, bh);
-       bh = NULL;
+       if (err != -ENOSPC)
+               bh = NULL;
        goto cleanup;
 
 journal_error: