aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/fork.c
diff options
context:
space:
mode:
authorJANAK DESAI <janak@us.ibm.com>2006-02-07 15:58:58 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-02-07 19:12:34 -0500
commitcf2e340f4249b781b3d2beb41e891d08581f0e10 (patch)
tree77130c96dbf86a2659bd346f14ccc3b0857a3e7c /kernel/fork.c
parent0d4c3e7a8c65892c7d6a748fdbb4499e988880db (diff)
[PATCH] unshare system call -v5: system call handler function
sys_unshare system call handler function accepts the same flags as clone system call, checks constraints on each of the flags and invokes corresponding unshare functions to disassociate respective process context if it was being shared with another task. Here is the link to a program for testing unshare system call. http://prdownloads.sourceforge.net/audit/unshare_test.c?download Please note that because of a problem in rmdir associated with bind mounts and clone with CLONE_NEWNS, the test fails while trying to remove temporary test directory. You can remove that temporary directory by doing rmdir, twice, from the command line. The first will fail with EBUSY, but the second will succeed. I have reported the problem to Ram Pai and Al Viro with a small program which reproduces the problem. Al told us yesterday that he will be looking at the problem soon. I have tried multiple rmdirs from the unshare_test program itself, but for some reason that is not working. Doing two rmdirs from command line does seem to remove the directory. Signed-off-by: Janak Desai <janak@us.ibm.com> Cc: Al Viro <viro@ftp.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de> Cc: Michael Kerrisk <mtk-manpages@gmx.net> Cc: Andi Kleen <ak@muc.de> Cc: Paul Mackerras <paulus@samba.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/fork.c')
-rw-r--r--kernel/fork.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/kernel/fork.c b/kernel/fork.c
index 7f0ab5ee948c..6eb9362775f9 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1323,3 +1323,235 @@ void __init proc_caches_init(void)
1323 sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN, 1323 sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN,
1324 SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); 1324 SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
1325} 1325}
1326
1327
1328/*
1329 * Check constraints on flags passed to the unshare system call and
1330 * force unsharing of additional process context as appropriate.
1331 */
1332static inline void check_unshare_flags(unsigned long *flags_ptr)
1333{
1334 /*
1335 * If unsharing a thread from a thread group, must also
1336 * unshare vm.
1337 */
1338 if (*flags_ptr & CLONE_THREAD)
1339 *flags_ptr |= CLONE_VM;
1340
1341 /*
1342 * If unsharing vm, must also unshare signal handlers.
1343 */
1344 if (*flags_ptr & CLONE_VM)
1345 *flags_ptr |= CLONE_SIGHAND;
1346
1347 /*
1348 * If unsharing signal handlers and the task was created
1349 * using CLONE_THREAD, then must unshare the thread
1350 */
1351 if ((*flags_ptr & CLONE_SIGHAND) &&
1352 (atomic_read(&current->signal->count) > 1))
1353 *flags_ptr |= CLONE_THREAD;
1354
1355 /*
1356 * If unsharing namespace, must also unshare filesystem information.
1357 */
1358 if (*flags_ptr & CLONE_NEWNS)
1359 *flags_ptr |= CLONE_FS;
1360}
1361
1362/*
1363 * Unsharing of tasks created with CLONE_THREAD is not supported yet
1364 */
1365static int unshare_thread(unsigned long unshare_flags)
1366{
1367 if (unshare_flags & CLONE_THREAD)
1368 return -EINVAL;
1369
1370 return 0;
1371}
1372
1373/*
1374 * Unsharing of fs info for tasks created with CLONE_FS is not supported yet
1375 */
1376static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
1377{
1378 struct fs_struct *fs = current->fs;
1379
1380 if ((unshare_flags & CLONE_FS) &&
1381 (fs && atomic_read(&fs->count) > 1))
1382 return -EINVAL;
1383
1384 return 0;
1385}
1386
1387/*
1388 * Unsharing of namespace for tasks created without CLONE_NEWNS is not
1389 * supported yet
1390 */
1391static int unshare_namespace(unsigned long unshare_flags, struct namespace **new_nsp)
1392{
1393 struct namespace *ns = current->namespace;
1394
1395 if ((unshare_flags & CLONE_NEWNS) &&
1396 (ns && atomic_read(&ns->count) > 1))
1397 return -EINVAL;
1398
1399 return 0;
1400}
1401
1402/*
1403 * Unsharing of sighand for tasks created with CLONE_SIGHAND is not
1404 * supported yet
1405 */
1406static int unshare_sighand(unsigned long unshare_flags, struct sighand_struct **new_sighp)
1407{
1408 struct sighand_struct *sigh = current->sighand;
1409
1410 if ((unshare_flags & CLONE_SIGHAND) &&
1411 (sigh && atomic_read(&sigh->count) > 1))
1412 return -EINVAL;
1413 else
1414 return 0;
1415}
1416
1417/*
1418 * Unsharing of vm for tasks created with CLONE_VM is not supported yet
1419 */
1420static int unshare_vm(unsigned long unshare_flags, struct mm_struct **new_mmp)
1421{
1422 struct mm_struct *mm = current->mm;
1423
1424 if ((unshare_flags & CLONE_VM) &&
1425 (mm && atomic_read(&mm->mm_users) > 1))
1426 return -EINVAL;
1427
1428 return 0;
1429
1430}
1431
1432/*
1433 * Unsharing of files for tasks created with CLONE_FILES is not supported yet
1434 */
1435static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp)
1436{
1437 struct files_struct *fd = current->files;
1438
1439 if ((unshare_flags & CLONE_FILES) &&
1440 (fd && atomic_read(&fd->count) > 1))
1441 return -EINVAL;
1442
1443 return 0;
1444}
1445
1446/*
1447 * Unsharing of semundo for tasks created with CLONE_SYSVSEM is not
1448 * supported yet
1449 */
1450static int unshare_semundo(unsigned long unshare_flags, struct sem_undo_list **new_ulistp)
1451{
1452 if (unshare_flags & CLONE_SYSVSEM)
1453 return -EINVAL;
1454
1455 return 0;
1456}
1457
1458/*
1459 * unshare allows a process to 'unshare' part of the process
1460 * context which was originally shared using clone. copy_*
1461 * functions used by do_fork() cannot be used here directly
1462 * because they modify an inactive task_struct that is being
1463 * constructed. Here we are modifying the current, active,
1464 * task_struct.
1465 */
1466asmlinkage long sys_unshare(unsigned long unshare_flags)
1467{
1468 int err = 0;
1469 struct fs_struct *fs, *new_fs = NULL;
1470 struct namespace *ns, *new_ns = NULL;
1471 struct sighand_struct *sigh, *new_sigh = NULL;
1472 struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL;
1473 struct files_struct *fd, *new_fd = NULL;
1474 struct sem_undo_list *new_ulist = NULL;
1475
1476 check_unshare_flags(&unshare_flags);
1477
1478 if ((err = unshare_thread(unshare_flags)))
1479 goto bad_unshare_out;
1480 if ((err = unshare_fs(unshare_flags, &new_fs)))
1481 goto bad_unshare_cleanup_thread;
1482 if ((err = unshare_namespace(unshare_flags, &new_ns)))
1483 goto bad_unshare_cleanup_fs;
1484 if ((err = unshare_sighand(unshare_flags, &new_sigh)))
1485 goto bad_unshare_cleanup_ns;
1486 if ((err = unshare_vm(unshare_flags, &new_mm)))
1487 goto bad_unshare_cleanup_sigh;
1488 if ((err = unshare_fd(unshare_flags, &new_fd)))
1489 goto bad_unshare_cleanup_vm;
1490 if ((err = unshare_semundo(unshare_flags, &new_ulist)))
1491 goto bad_unshare_cleanup_fd;
1492
1493 if (new_fs || new_ns || new_sigh || new_mm || new_fd || new_ulist) {
1494
1495 task_lock(current);
1496
1497 if (new_fs) {
1498 fs = current->fs;
1499 current->fs = new_fs;
1500 new_fs = fs;
1501 }
1502
1503 if (new_ns) {
1504 ns = current->namespace;
1505 current->namespace = new_ns;
1506 new_ns = ns;
1507 }
1508
1509 if (new_sigh) {
1510 sigh = current->sighand;
1511 current->sighand = new_sigh;
1512 new_sigh = sigh;
1513 }
1514
1515 if (new_mm) {
1516 mm = current->mm;
1517 active_mm = current->active_mm;
1518 current->mm = new_mm;
1519 current->active_mm = new_mm;
1520 activate_mm(active_mm, new_mm);
1521 new_mm = mm;
1522 }
1523
1524 if (new_fd) {
1525 fd = current->files;
1526 current->files = new_fd;
1527 new_fd = fd;
1528 }
1529
1530 task_unlock(current);
1531 }
1532
1533bad_unshare_cleanup_fd:
1534 if (new_fd)
1535 put_files_struct(new_fd);
1536
1537bad_unshare_cleanup_vm:
1538 if (new_mm)
1539 mmput(new_mm);
1540
1541bad_unshare_cleanup_sigh:
1542 if (new_sigh)
1543 if (atomic_dec_and_test(&new_sigh->count))
1544 kmem_cache_free(sighand_cachep, new_sigh);
1545
1546bad_unshare_cleanup_ns:
1547 if (new_ns)
1548 put_namespace(new_ns);
1549
1550bad_unshare_cleanup_fs:
1551 if (new_fs)
1552 put_fs_struct(new_fs);
1553
1554bad_unshare_cleanup_thread:
1555bad_unshare_out:
1556 return err;
1557}