2 From: Jan Kara <jack@suse.cz>
4 Fix possible races between umount and quota on/off.
6 Finally I decided to take a reference to vfsmount during vfs_quota_on() and
7 to drop it after the final cleanup in the vfs_quota_off(). This way we
8 should be all the time guarded against umount. This way was protected also
9 the old code which used filp_open() for opening quota files. I was also
10 thinking about other ways of protection but there would be always a window
11 (provided I don't want to play much with namespace locks) where
12 vfs_quota_on() could be called while umount() is in progress resulting in
13 the "Busy inodes after unmount" messages...
15 Get a reference to vfsmount during quotaon() so that we are guarded against
16 umount (as was the old code using filp_open()).
18 Signed-off-by: Jan Kara <jack@suse.cz>
19 Signed-off-by: Andrew Morton <akpm@osdl.org>
22 25-akpm/fs/dquot.c | 45 ++++++++++++++++++++++++++++-----------
23 25-akpm/include/linux/quota.h | 1
24 25-akpm/include/linux/quotaops.h | 2 -
25 3 files changed, 35 insertions(+), 13 deletions(-)
27 diff -puN fs/dquot.c~quota-umount-race-fix fs/dquot.c
28 --- 25/fs/dquot.c~quota-umount-race-fix Tue Nov 23 17:11:34 2004
29 +++ 25-akpm/fs/dquot.c Tue Nov 23 17:11:34 2004
30 @@ -1314,12 +1314,14 @@ int vfs_quota_off(struct super_block *sb
33 struct quota_info *dqopt = sb_dqopt(sb);
34 - struct inode *toput[MAXQUOTAS];
35 + struct inode *toputinode[MAXQUOTAS];
36 + struct vfsmount *toputmnt[MAXQUOTAS];
38 /* We need to serialize quota_off() for device */
39 down(&dqopt->dqonoff_sem);
40 for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
42 + toputinode[cnt] = NULL;
43 + toputmnt[cnt] = NULL;
44 if (type != -1 && cnt != type)
46 if (!sb_has_quota_enabled(sb, cnt))
47 @@ -1339,8 +1341,10 @@ int vfs_quota_off(struct super_block *sb
48 dqopt->ops[cnt]->free_file_info(sb, cnt);
49 put_quota_format(dqopt->info[cnt].dqi_format);
51 - toput[cnt] = dqopt->files[cnt];
52 + toputinode[cnt] = dqopt->files[cnt];
53 + toputmnt[cnt] = dqopt->mnt[cnt];
54 dqopt->files[cnt] = NULL;
55 + dqopt->mnt[cnt] = NULL;
56 dqopt->info[cnt].dqi_flags = 0;
57 dqopt->info[cnt].dqi_igrace = 0;
58 dqopt->info[cnt].dqi_bgrace = 0;
59 @@ -1348,7 +1352,10 @@ int vfs_quota_off(struct super_block *sb
61 up(&dqopt->dqonoff_sem);
62 /* Sync the superblock so that buffers with quota data are written to
63 - * disk (and so userspace sees correct data afterwards) */
64 + * disk (and so userspace sees correct data afterwards).
65 + * The reference to vfsmnt we are still holding protects us from
66 + * umount (we don't have it only when quotas are turned on/off for
67 + * journal replay but in that case we are guarded by the fs anyway). */
68 if (sb->s_op->sync_fs)
69 sb->s_op->sync_fs(sb, 1);
70 sync_blockdev(sb->s_bdev);
71 @@ -1358,13 +1365,24 @@ int vfs_quota_off(struct super_block *sb
72 * must also discard the blockdev buffers so that we see the
73 * changes done by userspace on the next quotaon() */
74 for (cnt = 0; cnt < MAXQUOTAS; cnt++)
76 - down(&toput[cnt]->i_sem);
77 - toput[cnt]->i_flags &= ~(S_IMMUTABLE | S_NOATIME | S_NOQUOTA);
78 - truncate_inode_pages(&toput[cnt]->i_data, 0);
79 - up(&toput[cnt]->i_sem);
80 - mark_inode_dirty(toput[cnt]);
82 + if (toputinode[cnt]) {
83 + down(&dqopt->dqonoff_sem);
84 + /* If quota was reenabled in the meantime, we have
86 + if (!sb_has_quota_enabled(sb, cnt)) {
87 + down(&toputinode[cnt]->i_sem);
88 + toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
89 + S_NOATIME | S_NOQUOTA);
90 + truncate_inode_pages(&toputinode[cnt]->i_data, 0);
91 + up(&toputinode[cnt]->i_sem);
92 + mark_inode_dirty(toputinode[cnt]);
93 + iput(toputinode[cnt]);
95 + up(&dqopt->dqonoff_sem);
96 + /* We don't hold the reference when we turned on quotas
97 + * just for the journal replay... */
99 + mntput(toputmnt[cnt]);
101 invalidate_bdev(sb->s_bdev, 0);
103 @@ -1478,8 +1496,11 @@ int vfs_quota_on(struct super_block *sb,
104 /* Quota file not on the same filesystem? */
105 if (nd.mnt->mnt_sb != sb)
109 error = vfs_quota_on_inode(nd.dentry->d_inode, type, format_id);
111 + sb_dqopt(sb)->mnt[type] = mntget(nd.mnt);
116 diff -puN include/linux/quota.h~quota-umount-race-fix include/linux/quota.h
117 --- 25/include/linux/quota.h~quota-umount-race-fix Tue Nov 23 17:11:34 2004
118 +++ 25-akpm/include/linux/quota.h Tue Nov 23 17:11:34 2004
119 @@ -286,6 +286,7 @@ struct quota_info {
120 struct semaphore dqonoff_sem; /* Serialize quotaon & quotaoff */
121 struct rw_semaphore dqptr_sem; /* serialize ops using quota_info struct, pointers from inode to dquots */
122 struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */
123 + struct vfsmount *mnt[MAXQUOTAS]; /* mountpoint entries of filesystems with quota files */
124 struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */
125 struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */
127 diff -puN include/linux/quotaops.h~quota-umount-race-fix include/linux/quotaops.h
128 --- 25/include/linux/quotaops.h~quota-umount-race-fix Tue Nov 23 17:11:34 2004
129 +++ 25-akpm/include/linux/quotaops.h Tue Nov 23 17:11:34 2004
130 @@ -177,7 +177,7 @@ static __inline__ int DQUOT_OFF(struct s
134 - if (sb->s_qcop && sb->s_qcop->quota_off)
135 + if (sb_any_quota_enabled(sb) && sb->s_qcop && sb->s_qcop->quota_off)
136 ret = sb->s_qcop->quota_off(sb, -1);