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 | |
| 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>
| -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); |
