This file is indexed.

/usr/src/kernel-patches/lustre/patches/quota-umount-race-fix.patch is in linux-patch-lustre 1.8.5+dfsg-3ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
From: Jan Kara <jack@suse.cz>

Fix possible races between umount and quota on/off.

Finally I decided to take a reference to vfsmount during vfs_quota_on() and
to drop it after the final cleanup in the vfs_quota_off().  This way we
should be all the time guarded against umount.  This way was protected also
the old code which used filp_open() for opening quota files.  I was also
thinking about other ways of protection but there would be always a window
(provided I don't want to play much with namespace locks) where
vfs_quota_on() could be called while umount() is in progress resulting in
the "Busy inodes after unmount" messages...

Get a reference to vfsmount during quotaon() so that we are guarded against
umount (as was the old code using filp_open()).

Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/fs/dquot.c               |   45 ++++++++++++++++++++++++++++-----------
 25-akpm/include/linux/quota.h    |    1 
 25-akpm/include/linux/quotaops.h |    2 -
 3 files changed, 35 insertions(+), 13 deletions(-)

diff -puN fs/dquot.c~quota-umount-race-fix fs/dquot.c
--- 25/fs/dquot.c~quota-umount-race-fix	Tue Nov 23 17:11:34 2004
+++ 25-akpm/fs/dquot.c	Tue Nov 23 17:11:34 2004
@@ -1314,12 +1314,14 @@ int vfs_quota_off(struct super_block *sb
 {
 	int cnt;
 	struct quota_info *dqopt = sb_dqopt(sb);
-	struct inode *toput[MAXQUOTAS];
+	struct inode *toputinode[MAXQUOTAS];
+	struct vfsmount *toputmnt[MAXQUOTAS];
 
 	/* We need to serialize quota_off() for device */
 	down(&dqopt->dqonoff_sem);
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-		toput[cnt] = NULL;
+		toputinode[cnt] = NULL;
+		toputmnt[cnt] = NULL;
 		if (type != -1 && cnt != type)
 			continue;
 		if (!sb_has_quota_enabled(sb, cnt))
@@ -1339,8 +1341,10 @@ int vfs_quota_off(struct super_block *sb
 			dqopt->ops[cnt]->free_file_info(sb, cnt);
 		put_quota_format(dqopt->info[cnt].dqi_format);
 
-		toput[cnt] = dqopt->files[cnt];
+		toputinode[cnt] = dqopt->files[cnt];
+		toputmnt[cnt] = dqopt->mnt[cnt];
 		dqopt->files[cnt] = NULL;
+		dqopt->mnt[cnt] = NULL;
 		dqopt->info[cnt].dqi_flags = 0;
 		dqopt->info[cnt].dqi_igrace = 0;
 		dqopt->info[cnt].dqi_bgrace = 0;
@@ -1348,7 +1352,10 @@ int vfs_quota_off(struct super_block *sb
 	}
 	up(&dqopt->dqonoff_sem);
 	/* Sync the superblock so that buffers with quota data are written to
-         * disk (and so userspace sees correct data afterwards) */
+	 * disk (and so userspace sees correct data afterwards).
+	 * The reference to vfsmnt we are still holding protects us from
+	 * umount (we don't have it only when quotas are turned on/off for
+	 * journal replay but in that case we are guarded by the fs anyway). */
 	if (sb->s_op->sync_fs)
 		sb->s_op->sync_fs(sb, 1);
 	sync_blockdev(sb->s_bdev);
@@ -1358,13 +1365,24 @@ int vfs_quota_off(struct super_block *sb
	 * must also discard the blockdev buffers so that we see the
	 * changes done by userspace on the next quotaon() */
	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-		if (toput[cnt]) {
-			down(&toput[cnt]->i_sem);
-			toput[cnt]->i_flags &= ~(S_IMMUTABLE | S_NOATIME | S_NOQUOTA);
-			truncate_inode_pages(&toput[cnt]->i_data, 0);
-			up(&toput[cnt]->i_sem);
-			mark_inode_dirty(toput[cnt]);
-			iput(toput[cnt]);
+		if (toputinode[cnt]) {
+			down(&dqopt->dqonoff_sem);
+			/* If quota was reenabled in the meantime, we have
+			 * nothing to do */
+			if (!sb_has_quota_enabled(sb, cnt)) {
+				down(&toputinode[cnt]->i_sem);
+				toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
+				  S_NOATIME | S_NOQUOTA);
+				truncate_inode_pages(&toputinode[cnt]->i_data, 0);
+				up(&toputinode[cnt]->i_sem);
+				mark_inode_dirty(toputinode[cnt]);
+				iput(toputinode[cnt]);
+			}
+			up(&dqopt->dqonoff_sem);
+			/* We don't hold the reference when we turned on quotas
+			 * just for the journal replay... */
+			if (toputmnt[cnt])
+				mntput(toputmnt[cnt]);
		}
	invalidate_bdev(sb->s_bdev, 0);
	return 0;
@@ -1478,8 +1496,11 @@ int vfs_quota_on(struct super_block *sb,
 	/* Quota file not on the same filesystem? */
 	if (nd.mnt->mnt_sb != sb)
 		error = -EXDEV;
-	else
+	else {
 		error = vfs_quota_on_inode(nd.dentry->d_inode, type, format_id);
+		if (!error)
+			sb_dqopt(sb)->mnt[type] = mntget(nd.mnt);
+	}
 out_path:
 	path_release(&nd);
 	return error;
diff -puN include/linux/quota.h~quota-umount-race-fix include/linux/quota.h
--- 25/include/linux/quota.h~quota-umount-race-fix	Tue Nov 23 17:11:34 2004
+++ 25-akpm/include/linux/quota.h	Tue Nov 23 17:11:34 2004
@@ -286,6 +286,7 @@ struct quota_info {
 	struct semaphore dqonoff_sem;		/* Serialize quotaon & quotaoff */
 	struct rw_semaphore dqptr_sem;		/* serialize ops using quota_info struct, pointers from inode to dquots */
 	struct inode *files[MAXQUOTAS];		/* inodes of quotafiles */
+	struct vfsmount *mnt[MAXQUOTAS];	/* mountpoint entries of filesystems with quota files */
 	struct mem_dqinfo info[MAXQUOTAS];	/* Information for each quota type */
 	struct quota_format_ops *ops[MAXQUOTAS];	/* Operations for each type */
 };
diff -puN include/linux/quotaops.h~quota-umount-race-fix include/linux/quotaops.h
--- 25/include/linux/quotaops.h~quota-umount-race-fix	Tue Nov 23 17:11:34 2004
+++ 25-akpm/include/linux/quotaops.h	Tue Nov 23 17:11:34 2004
@@ -177,7 +177,7 @@ static __inline__ int DQUOT_OFF(struct s
 {
 	int ret = -ENOSYS;
 
-	if (sb->s_qcop && sb->s_qcop->quota_off)
+	if (sb_any_quota_enabled(sb) && sb->s_qcop && sb->s_qcop->quota_off)
 		ret = sb->s_qcop->quota_off(sb, -1);
 	return ret;
 }
_