aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2008-08-01 04:29:18 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2008-08-01 11:25:25 -0400
commit77e69dac3cefacee939cb107ae9cd520a62338e0 (patch)
tree02ddee5ac85ceb632eab2aff994ffbd3233e51eb
parent1b7e190b4764ea3ca1080404dd593eae5230d2b3 (diff)
[PATCH] fix races and leaks in vfs_quota_on() users
* new helper: vfs_quota_on_path(); equivalent of vfs_quota_on() sans the pathname resolution. * callers of vfs_quota_on() that do their own pathname resolution and checks based on it are switched to vfs_quota_on_path(); that way we avoid the races. * reiserfs leaked dentry/vfsmount references on several failure exits. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/dquot.c33
-rw-r--r--fs/ext3/super.c3
-rw-r--r--fs/ext4/super.c3
-rw-r--r--fs/reiserfs/super.c16
-rw-r--r--include/linux/quotaops.h2
5 files changed, 35 insertions, 22 deletions
diff --git a/fs/dquot.c b/fs/dquot.c
index 1346eebe74ce..8ec4d6cc7633 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -1793,6 +1793,21 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
1793 return ret; 1793 return ret;
1794} 1794}
1795 1795
1796int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
1797 struct path *path)
1798{
1799 int error = security_quota_on(path->dentry);
1800 if (error)
1801 return error;
1802 /* Quota file not on the same filesystem? */
1803 if (path->mnt->mnt_sb != sb)
1804 error = -EXDEV;
1805 else
1806 error = vfs_quota_on_inode(path->dentry->d_inode, type,
1807 format_id);
1808 return error;
1809}
1810
1796/* Actual function called from quotactl() */ 1811/* Actual function called from quotactl() */
1797int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, 1812int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
1798 int remount) 1813 int remount)
@@ -1804,19 +1819,10 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
1804 return vfs_quota_on_remount(sb, type); 1819 return vfs_quota_on_remount(sb, type);
1805 1820
1806 error = path_lookup(path, LOOKUP_FOLLOW, &nd); 1821 error = path_lookup(path, LOOKUP_FOLLOW, &nd);
1807 if (error < 0) 1822 if (!error) {
1808 return error; 1823 error = vfs_quota_on_path(sb, type, format_id, &nd.path);
1809 error = security_quota_on(nd.path.dentry); 1824 path_put(&nd.path);
1810 if (error) 1825 }
1811 goto out_path;
1812 /* Quota file not on the same filesystem? */
1813 if (nd.path.mnt->mnt_sb != sb)
1814 error = -EXDEV;
1815 else
1816 error = vfs_quota_on_inode(nd.path.dentry->d_inode, type,
1817 format_id);
1818out_path:
1819 path_put(&nd.path);
1820 return error; 1826 return error;
1821} 1827}
1822 1828
@@ -2185,6 +2191,7 @@ EXPORT_SYMBOL(unregister_quota_format);
2185EXPORT_SYMBOL(dqstats); 2191EXPORT_SYMBOL(dqstats);
2186EXPORT_SYMBOL(dq_data_lock); 2192EXPORT_SYMBOL(dq_data_lock);
2187EXPORT_SYMBOL(vfs_quota_on); 2193EXPORT_SYMBOL(vfs_quota_on);
2194EXPORT_SYMBOL(vfs_quota_on_path);
2188EXPORT_SYMBOL(vfs_quota_on_mount); 2195EXPORT_SYMBOL(vfs_quota_on_mount);
2189EXPORT_SYMBOL(vfs_quota_off); 2196EXPORT_SYMBOL(vfs_quota_off);
2190EXPORT_SYMBOL(vfs_quota_sync); 2197EXPORT_SYMBOL(vfs_quota_sync);
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 8ddced384674..f38a5afc39a1 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -2810,8 +2810,9 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
2810 journal_unlock_updates(EXT3_SB(sb)->s_journal); 2810 journal_unlock_updates(EXT3_SB(sb)->s_journal);
2811 } 2811 }
2812 2812
2813 err = vfs_quota_on_path(sb, type, format_id, &nd.path);
2813 path_put(&nd.path); 2814 path_put(&nd.path);
2814 return vfs_quota_on(sb, type, format_id, path, remount); 2815 return err;
2815} 2816}
2816 2817
2817/* Read data from quotafile - avoid pagecache and such because we cannot afford 2818/* Read data from quotafile - avoid pagecache and such because we cannot afford
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index b5479b1dff14..1e69f29a8c55 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3352,8 +3352,9 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
3352 jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); 3352 jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
3353 } 3353 }
3354 3354
3355 err = vfs_quota_on_path(sb, type, format_id, &nd.path);
3355 path_put(&nd.path); 3356 path_put(&nd.path);
3356 return vfs_quota_on(sb, type, format_id, path, remount); 3357 return err;
3357} 3358}
3358 3359
3359/* Read data from quotafile - avoid pagecache and such because we cannot afford 3360/* Read data from quotafile - avoid pagecache and such because we cannot afford
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 879e54d35c2d..282a13596c70 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -2076,8 +2076,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
2076 return err; 2076 return err;
2077 /* Quotafile not on the same filesystem? */ 2077 /* Quotafile not on the same filesystem? */
2078 if (nd.path.mnt->mnt_sb != sb) { 2078 if (nd.path.mnt->mnt_sb != sb) {
2079 path_put(&nd.path); 2079 err = -EXDEV;
2080 return -EXDEV; 2080 goto out;
2081 } 2081 }
2082 inode = nd.path.dentry->d_inode; 2082 inode = nd.path.dentry->d_inode;
2083 /* We must not pack tails for quota files on reiserfs for quota IO to work */ 2083 /* We must not pack tails for quota files on reiserfs for quota IO to work */
@@ -2087,8 +2087,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
2087 reiserfs_warning(sb, 2087 reiserfs_warning(sb,
2088 "reiserfs: Unpacking tail of quota file failed" 2088 "reiserfs: Unpacking tail of quota file failed"
2089 " (%d). Cannot turn on quotas.", err); 2089 " (%d). Cannot turn on quotas.", err);
2090 path_put(&nd.path); 2090 err = -EINVAL;
2091 return -EINVAL; 2091 goto out;
2092 } 2092 }
2093 mark_inode_dirty(inode); 2093 mark_inode_dirty(inode);
2094 } 2094 }
@@ -2109,13 +2109,15 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
2109 /* Just start temporary transaction and finish it */ 2109 /* Just start temporary transaction and finish it */
2110 err = journal_begin(&th, sb, 1); 2110 err = journal_begin(&th, sb, 1);
2111 if (err) 2111 if (err)
2112 return err; 2112 goto out;
2113 err = journal_end_sync(&th, sb, 1); 2113 err = journal_end_sync(&th, sb, 1);
2114 if (err) 2114 if (err)
2115 return err; 2115 goto out;
2116 } 2116 }
2117 err = vfs_quota_on_path(sb, type, format_id, &nd.path);
2118out:
2117 path_put(&nd.path); 2119 path_put(&nd.path);
2118 return vfs_quota_on(sb, type, format_id, path, 0); 2120 return err;
2119} 2121}
2120 2122
2121/* Read data from quotafile - avoid pagecache and such because we cannot afford 2123/* Read data from quotafile - avoid pagecache and such because we cannot afford
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 742187f7a05c..ca6b9b5c8d52 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -43,6 +43,8 @@ int dquot_mark_dquot_dirty(struct dquot *dquot);
43 43
44int vfs_quota_on(struct super_block *sb, int type, int format_id, 44int vfs_quota_on(struct super_block *sb, int type, int format_id,
45 char *path, int remount); 45 char *path, int remount);
46int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
47 struct path *path);
46int vfs_quota_on_mount(struct super_block *sb, char *qf_name, 48int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
47 int format_id, int type); 49 int format_id, int type);
48int vfs_quota_off(struct super_block *sb, int type, int remount); 50int vfs_quota_off(struct super_block *sb, int type, int remount);