diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2008-03-22 00:21:53 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-03-27 20:47:52 -0400 |
commit | bcc5c7d2b692e5319db00b0dd020ce98723103b1 (patch) | |
tree | e485f36b5e6d8744ea5035b95b497b9dc3ba30db /fs | |
parent | 7c4b93d8269b9d35971a8239426b1f6ddc3d5ef7 (diff) |
[PATCH] sanitize locking in mark_mounts_for_expiry() and shrink_submounts()
... and fix a race on access of ->mnt_share et.al. without namespace_sem
in the latter.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namespace.c | 105 |
1 files changed, 24 insertions, 81 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index c175218ebae1..1c78917ec930 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -1210,75 +1210,6 @@ unlock: | |||
1210 | 1210 | ||
1211 | EXPORT_SYMBOL_GPL(do_add_mount); | 1211 | EXPORT_SYMBOL_GPL(do_add_mount); |
1212 | 1212 | ||
1213 | static void expire_mount(struct vfsmount *mnt, struct list_head *mounts, | ||
1214 | struct list_head *umounts) | ||
1215 | { | ||
1216 | spin_lock(&vfsmount_lock); | ||
1217 | |||
1218 | /* | ||
1219 | * Check if mount is still attached, if not, let whoever holds it deal | ||
1220 | * with the sucker | ||
1221 | */ | ||
1222 | if (mnt->mnt_parent == mnt) { | ||
1223 | spin_unlock(&vfsmount_lock); | ||
1224 | return; | ||
1225 | } | ||
1226 | |||
1227 | /* | ||
1228 | * Check that it is still dead: the count should now be 2 - as | ||
1229 | * contributed by the vfsmount parent and the mntget above | ||
1230 | */ | ||
1231 | if (!propagate_mount_busy(mnt, 2)) { | ||
1232 | /* delete from the namespace */ | ||
1233 | touch_mnt_namespace(mnt->mnt_ns); | ||
1234 | list_del_init(&mnt->mnt_list); | ||
1235 | mnt->mnt_ns = NULL; | ||
1236 | umount_tree(mnt, 1, umounts); | ||
1237 | spin_unlock(&vfsmount_lock); | ||
1238 | } else { | ||
1239 | /* | ||
1240 | * Someone brought it back to life whilst we didn't have any | ||
1241 | * locks held so return it to the expiration list | ||
1242 | */ | ||
1243 | list_add_tail(&mnt->mnt_expire, mounts); | ||
1244 | spin_unlock(&vfsmount_lock); | ||
1245 | } | ||
1246 | } | ||
1247 | |||
1248 | /* | ||
1249 | * go through the vfsmounts we've just consigned to the graveyard to | ||
1250 | * - check that they're still dead | ||
1251 | * - delete the vfsmount from the appropriate namespace under lock | ||
1252 | * - dispose of the corpse | ||
1253 | */ | ||
1254 | static void expire_mount_list(struct list_head *graveyard, struct list_head *mounts) | ||
1255 | { | ||
1256 | struct mnt_namespace *ns; | ||
1257 | struct vfsmount *mnt; | ||
1258 | |||
1259 | while (!list_empty(graveyard)) { | ||
1260 | LIST_HEAD(umounts); | ||
1261 | mnt = list_first_entry(graveyard, struct vfsmount, mnt_expire); | ||
1262 | list_del_init(&mnt->mnt_expire); | ||
1263 | |||
1264 | /* don't do anything if the namespace is dead - all the | ||
1265 | * vfsmounts from it are going away anyway */ | ||
1266 | ns = mnt->mnt_ns; | ||
1267 | if (!ns || !ns->root) | ||
1268 | continue; | ||
1269 | get_mnt_ns(ns); | ||
1270 | |||
1271 | spin_unlock(&vfsmount_lock); | ||
1272 | down_write(&namespace_sem); | ||
1273 | expire_mount(mnt, mounts, &umounts); | ||
1274 | up_write(&namespace_sem); | ||
1275 | release_mounts(&umounts); | ||
1276 | mntput(mnt); | ||
1277 | put_mnt_ns(ns); | ||
1278 | spin_lock(&vfsmount_lock); | ||
1279 | } | ||
1280 | } | ||
1281 | |||
1282 | /* | 1213 | /* |
1283 | * process a list of expirable mountpoints with the intent of discarding any | 1214 | * process a list of expirable mountpoints with the intent of discarding any |
1284 | * mountpoints that aren't in use and haven't been touched since last we came | 1215 | * mountpoints that aren't in use and haven't been touched since last we came |
@@ -1288,10 +1219,12 @@ void mark_mounts_for_expiry(struct list_head *mounts) | |||
1288 | { | 1219 | { |
1289 | struct vfsmount *mnt, *next; | 1220 | struct vfsmount *mnt, *next; |
1290 | LIST_HEAD(graveyard); | 1221 | LIST_HEAD(graveyard); |
1222 | LIST_HEAD(umounts); | ||
1291 | 1223 | ||
1292 | if (list_empty(mounts)) | 1224 | if (list_empty(mounts)) |
1293 | return; | 1225 | return; |
1294 | 1226 | ||
1227 | down_write(&namespace_sem); | ||
1295 | spin_lock(&vfsmount_lock); | 1228 | spin_lock(&vfsmount_lock); |
1296 | 1229 | ||
1297 | /* extract from the expiration list every vfsmount that matches the | 1230 | /* extract from the expiration list every vfsmount that matches the |
@@ -1302,16 +1235,19 @@ void mark_mounts_for_expiry(struct list_head *mounts) | |||
1302 | */ | 1235 | */ |
1303 | list_for_each_entry_safe(mnt, next, mounts, mnt_expire) { | 1236 | list_for_each_entry_safe(mnt, next, mounts, mnt_expire) { |
1304 | if (!xchg(&mnt->mnt_expiry_mark, 1) || | 1237 | if (!xchg(&mnt->mnt_expiry_mark, 1) || |
1305 | atomic_read(&mnt->mnt_count) != 1) | 1238 | propagate_mount_busy(mnt, 1)) |
1306 | continue; | 1239 | continue; |
1307 | |||
1308 | mntget(mnt); | ||
1309 | list_move(&mnt->mnt_expire, &graveyard); | 1240 | list_move(&mnt->mnt_expire, &graveyard); |
1310 | } | 1241 | } |
1311 | 1242 | while (!list_empty(&graveyard)) { | |
1312 | expire_mount_list(&graveyard, mounts); | 1243 | mnt = list_first_entry(&graveyard, struct vfsmount, mnt_expire); |
1313 | 1244 | touch_mnt_namespace(mnt->mnt_ns); | |
1245 | umount_tree(mnt, 1, &umounts); | ||
1246 | } | ||
1314 | spin_unlock(&vfsmount_lock); | 1247 | spin_unlock(&vfsmount_lock); |
1248 | up_write(&namespace_sem); | ||
1249 | |||
1250 | release_mounts(&umounts); | ||
1315 | } | 1251 | } |
1316 | 1252 | ||
1317 | EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); | 1253 | EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); |
@@ -1347,7 +1283,6 @@ resume: | |||
1347 | } | 1283 | } |
1348 | 1284 | ||
1349 | if (!propagate_mount_busy(mnt, 1)) { | 1285 | if (!propagate_mount_busy(mnt, 1)) { |
1350 | mntget(mnt); | ||
1351 | list_move_tail(&mnt->mnt_expire, graveyard); | 1286 | list_move_tail(&mnt->mnt_expire, graveyard); |
1352 | found++; | 1287 | found++; |
1353 | } | 1288 | } |
@@ -1370,15 +1305,23 @@ resume: | |||
1370 | void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts) | 1305 | void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts) |
1371 | { | 1306 | { |
1372 | LIST_HEAD(graveyard); | 1307 | LIST_HEAD(graveyard); |
1373 | int found; | 1308 | LIST_HEAD(umounts); |
1309 | struct vfsmount *mnt; | ||
1374 | 1310 | ||
1311 | down_write(&namespace_sem); | ||
1375 | spin_lock(&vfsmount_lock); | 1312 | spin_lock(&vfsmount_lock); |
1376 | |||
1377 | /* extract submounts of 'mountpoint' from the expiration list */ | 1313 | /* extract submounts of 'mountpoint' from the expiration list */ |
1378 | while ((found = select_submounts(mountpoint, &graveyard)) != 0) | 1314 | while (select_submounts(mountpoint, &graveyard)) { |
1379 | expire_mount_list(&graveyard, mounts); | 1315 | while (!list_empty(&graveyard)) { |
1380 | 1316 | mnt = list_first_entry(&graveyard, struct vfsmount, | |
1317 | mnt_expire); | ||
1318 | touch_mnt_namespace(mnt->mnt_ns); | ||
1319 | umount_tree(mnt, 1, &umounts); | ||
1320 | } | ||
1321 | } | ||
1381 | spin_unlock(&vfsmount_lock); | 1322 | spin_unlock(&vfsmount_lock); |
1323 | up_write(&namespace_sem); | ||
1324 | release_mounts(&umounts); | ||
1382 | } | 1325 | } |
1383 | 1326 | ||
1384 | EXPORT_SYMBOL_GPL(shrink_submounts); | 1327 | EXPORT_SYMBOL_GPL(shrink_submounts); |