diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2008-08-01 04:29:18 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-08-01 11:25:25 -0400 |
commit | 77e69dac3cefacee939cb107ae9cd520a62338e0 (patch) | |
tree | 02ddee5ac85ceb632eab2aff994ffbd3233e51eb | |
parent | 1b7e190b4764ea3ca1080404dd593eae5230d2b3 (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.c | 33 | ||||
-rw-r--r-- | fs/ext3/super.c | 3 | ||||
-rw-r--r-- | fs/ext4/super.c | 3 | ||||
-rw-r--r-- | fs/reiserfs/super.c | 16 | ||||
-rw-r--r-- | include/linux/quotaops.h | 2 |
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 | ||
1796 | int 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() */ |
1797 | int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, | 1812 | int 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); | ||
1818 | out_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); | |||
2185 | EXPORT_SYMBOL(dqstats); | 2191 | EXPORT_SYMBOL(dqstats); |
2186 | EXPORT_SYMBOL(dq_data_lock); | 2192 | EXPORT_SYMBOL(dq_data_lock); |
2187 | EXPORT_SYMBOL(vfs_quota_on); | 2193 | EXPORT_SYMBOL(vfs_quota_on); |
2194 | EXPORT_SYMBOL(vfs_quota_on_path); | ||
2188 | EXPORT_SYMBOL(vfs_quota_on_mount); | 2195 | EXPORT_SYMBOL(vfs_quota_on_mount); |
2189 | EXPORT_SYMBOL(vfs_quota_off); | 2196 | EXPORT_SYMBOL(vfs_quota_off); |
2190 | EXPORT_SYMBOL(vfs_quota_sync); | 2197 | EXPORT_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); | ||
2118 | out: | ||
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 | ||
44 | int vfs_quota_on(struct super_block *sb, int type, int format_id, | 44 | int vfs_quota_on(struct super_block *sb, int type, int format_id, |
45 | char *path, int remount); | 45 | char *path, int remount); |
46 | int vfs_quota_on_path(struct super_block *sb, int type, int format_id, | ||
47 | struct path *path); | ||
46 | int vfs_quota_on_mount(struct super_block *sb, char *qf_name, | 48 | int vfs_quota_on_mount(struct super_block *sb, char *qf_name, |
47 | int format_id, int type); | 49 | int format_id, int type); |
48 | int vfs_quota_off(struct super_block *sb, int type, int remount); | 50 | int vfs_quota_off(struct super_block *sb, int type, int remount); |