diff options
author | Badari Pulavarty <pbadari@us.ibm.com> | 2007-05-08 03:25:21 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:00 -0400 |
commit | e3222c4ecc649c4ae568e61dda9349482401b501 (patch) | |
tree | d96614ef67d947a3dd8ab0929a4755bce9fdbcc1 /kernel | |
parent | 4fc75ff4816c3483b4b772b2f6cb3d8fd88ca547 (diff) |
Merge sys_clone()/sys_unshare() nsproxy and namespace handling
sys_clone() and sys_unshare() both makes copies of nsproxy and its associated
namespaces. But they have different code paths.
This patch merges all the nsproxy and its associated namespace copy/clone
handling (as much as possible). Posted on container list earlier for
feedback.
- Create a new nsproxy and its associated namespaces and pass it back to
caller to attach it to right process.
- Changed all copy_*_ns() routines to return a new copy of namespace
instead of attaching it to task->nsproxy.
- Moved the CAP_SYS_ADMIN checks out of copy_*_ns() routines.
- Removed unnessary !ns checks from copy_*_ns() and added BUG_ON()
just incase.
- Get rid of all individual unshare_*_ns() routines and make use of
copy_*_ns() instead.
[akpm@osdl.org: cleanups, warning fix]
[clg@fr.ibm.com: remove dup_namespaces() declaration]
[serue@us.ibm.com: fix CONFIG_IPC_NS=n, clone(CLONE_NEWIPC) retval]
[akpm@linux-foundation.org: fix build with CONFIG_SYSVIPC=n]
Signed-off-by: Badari Pulavarty <pbadari@us.ibm.com>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Cc: Cedric Le Goater <clg@fr.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: <containers@lists.osdl.org>
Signed-off-by: Cedric Le Goater <clg@fr.ibm.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/fork.c | 85 | ||||
-rw-r--r-- | kernel/nsproxy.c | 139 | ||||
-rw-r--r-- | kernel/pid.c | 11 | ||||
-rw-r--r-- | kernel/utsname.c | 41 |
4 files changed, 98 insertions, 178 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index b7d169def942..fd211b9dddd4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1516,26 +1516,6 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp) | |||
1516 | } | 1516 | } |
1517 | 1517 | ||
1518 | /* | 1518 | /* |
1519 | * Unshare the mnt_namespace structure if it is being shared | ||
1520 | */ | ||
1521 | static int unshare_mnt_namespace(unsigned long unshare_flags, | ||
1522 | struct mnt_namespace **new_nsp, struct fs_struct *new_fs) | ||
1523 | { | ||
1524 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; | ||
1525 | |||
1526 | if ((unshare_flags & CLONE_NEWNS) && ns) { | ||
1527 | if (!capable(CAP_SYS_ADMIN)) | ||
1528 | return -EPERM; | ||
1529 | |||
1530 | *new_nsp = dup_mnt_ns(current, new_fs ? new_fs : current->fs); | ||
1531 | if (!*new_nsp) | ||
1532 | return -ENOMEM; | ||
1533 | } | ||
1534 | |||
1535 | return 0; | ||
1536 | } | ||
1537 | |||
1538 | /* | ||
1539 | * Unsharing of sighand is not supported yet | 1519 | * Unsharing of sighand is not supported yet |
1540 | */ | 1520 | */ |
1541 | static int unshare_sighand(unsigned long unshare_flags, struct sighand_struct **new_sighp) | 1521 | static int unshare_sighand(unsigned long unshare_flags, struct sighand_struct **new_sighp) |
@@ -1593,16 +1573,6 @@ static int unshare_semundo(unsigned long unshare_flags, struct sem_undo_list **n | |||
1593 | return 0; | 1573 | return 0; |
1594 | } | 1574 | } |
1595 | 1575 | ||
1596 | #ifndef CONFIG_IPC_NS | ||
1597 | static inline int unshare_ipcs(unsigned long flags, struct ipc_namespace **ns) | ||
1598 | { | ||
1599 | if (flags & CLONE_NEWIPC) | ||
1600 | return -EINVAL; | ||
1601 | |||
1602 | return 0; | ||
1603 | } | ||
1604 | #endif | ||
1605 | |||
1606 | /* | 1576 | /* |
1607 | * unshare allows a process to 'unshare' part of the process | 1577 | * unshare allows a process to 'unshare' part of the process |
1608 | * context which was originally shared using clone. copy_* | 1578 | * context which was originally shared using clone. copy_* |
@@ -1615,14 +1585,11 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) | |||
1615 | { | 1585 | { |
1616 | int err = 0; | 1586 | int err = 0; |
1617 | struct fs_struct *fs, *new_fs = NULL; | 1587 | struct fs_struct *fs, *new_fs = NULL; |
1618 | struct mnt_namespace *ns, *new_ns = NULL; | ||
1619 | struct sighand_struct *new_sigh = NULL; | 1588 | struct sighand_struct *new_sigh = NULL; |
1620 | struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL; | 1589 | struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL; |
1621 | struct files_struct *fd, *new_fd = NULL; | 1590 | struct files_struct *fd, *new_fd = NULL; |
1622 | struct sem_undo_list *new_ulist = NULL; | 1591 | struct sem_undo_list *new_ulist = NULL; |
1623 | struct nsproxy *new_nsproxy = NULL, *old_nsproxy = NULL; | 1592 | struct nsproxy *new_nsproxy = NULL, *old_nsproxy = NULL; |
1624 | struct uts_namespace *uts, *new_uts = NULL; | ||
1625 | struct ipc_namespace *ipc, *new_ipc = NULL; | ||
1626 | 1593 | ||
1627 | check_unshare_flags(&unshare_flags); | 1594 | check_unshare_flags(&unshare_flags); |
1628 | 1595 | ||
@@ -1637,36 +1604,24 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) | |||
1637 | goto bad_unshare_out; | 1604 | goto bad_unshare_out; |
1638 | if ((err = unshare_fs(unshare_flags, &new_fs))) | 1605 | if ((err = unshare_fs(unshare_flags, &new_fs))) |
1639 | goto bad_unshare_cleanup_thread; | 1606 | goto bad_unshare_cleanup_thread; |
1640 | if ((err = unshare_mnt_namespace(unshare_flags, &new_ns, new_fs))) | ||
1641 | goto bad_unshare_cleanup_fs; | ||
1642 | if ((err = unshare_sighand(unshare_flags, &new_sigh))) | 1607 | if ((err = unshare_sighand(unshare_flags, &new_sigh))) |
1643 | goto bad_unshare_cleanup_ns; | 1608 | goto bad_unshare_cleanup_fs; |
1644 | if ((err = unshare_vm(unshare_flags, &new_mm))) | 1609 | if ((err = unshare_vm(unshare_flags, &new_mm))) |
1645 | goto bad_unshare_cleanup_sigh; | 1610 | goto bad_unshare_cleanup_sigh; |
1646 | if ((err = unshare_fd(unshare_flags, &new_fd))) | 1611 | if ((err = unshare_fd(unshare_flags, &new_fd))) |
1647 | goto bad_unshare_cleanup_vm; | 1612 | goto bad_unshare_cleanup_vm; |
1648 | if ((err = unshare_semundo(unshare_flags, &new_ulist))) | 1613 | if ((err = unshare_semundo(unshare_flags, &new_ulist))) |
1649 | goto bad_unshare_cleanup_fd; | 1614 | goto bad_unshare_cleanup_fd; |
1650 | if ((err = unshare_utsname(unshare_flags, &new_uts))) | 1615 | if ((err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, |
1616 | new_fs))) | ||
1651 | goto bad_unshare_cleanup_semundo; | 1617 | goto bad_unshare_cleanup_semundo; |
1652 | if ((err = unshare_ipcs(unshare_flags, &new_ipc))) | ||
1653 | goto bad_unshare_cleanup_uts; | ||
1654 | |||
1655 | if (new_ns || new_uts || new_ipc) { | ||
1656 | old_nsproxy = current->nsproxy; | ||
1657 | new_nsproxy = dup_namespaces(old_nsproxy); | ||
1658 | if (!new_nsproxy) { | ||
1659 | err = -ENOMEM; | ||
1660 | goto bad_unshare_cleanup_ipc; | ||
1661 | } | ||
1662 | } | ||
1663 | 1618 | ||
1664 | if (new_fs || new_ns || new_mm || new_fd || new_ulist || | 1619 | if (new_fs || new_mm || new_fd || new_ulist || new_nsproxy) { |
1665 | new_uts || new_ipc) { | ||
1666 | 1620 | ||
1667 | task_lock(current); | 1621 | task_lock(current); |
1668 | 1622 | ||
1669 | if (new_nsproxy) { | 1623 | if (new_nsproxy) { |
1624 | old_nsproxy = current->nsproxy; | ||
1670 | current->nsproxy = new_nsproxy; | 1625 | current->nsproxy = new_nsproxy; |
1671 | new_nsproxy = old_nsproxy; | 1626 | new_nsproxy = old_nsproxy; |
1672 | } | 1627 | } |
@@ -1677,12 +1632,6 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) | |||
1677 | new_fs = fs; | 1632 | new_fs = fs; |
1678 | } | 1633 | } |
1679 | 1634 | ||
1680 | if (new_ns) { | ||
1681 | ns = current->nsproxy->mnt_ns; | ||
1682 | current->nsproxy->mnt_ns = new_ns; | ||
1683 | new_ns = ns; | ||
1684 | } | ||
1685 | |||
1686 | if (new_mm) { | 1635 | if (new_mm) { |
1687 | mm = current->mm; | 1636 | mm = current->mm; |
1688 | active_mm = current->active_mm; | 1637 | active_mm = current->active_mm; |
@@ -1698,32 +1647,12 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) | |||
1698 | new_fd = fd; | 1647 | new_fd = fd; |
1699 | } | 1648 | } |
1700 | 1649 | ||
1701 | if (new_uts) { | ||
1702 | uts = current->nsproxy->uts_ns; | ||
1703 | current->nsproxy->uts_ns = new_uts; | ||
1704 | new_uts = uts; | ||
1705 | } | ||
1706 | |||
1707 | if (new_ipc) { | ||
1708 | ipc = current->nsproxy->ipc_ns; | ||
1709 | current->nsproxy->ipc_ns = new_ipc; | ||
1710 | new_ipc = ipc; | ||
1711 | } | ||
1712 | |||
1713 | task_unlock(current); | 1650 | task_unlock(current); |
1714 | } | 1651 | } |
1715 | 1652 | ||
1716 | if (new_nsproxy) | 1653 | if (new_nsproxy) |
1717 | put_nsproxy(new_nsproxy); | 1654 | put_nsproxy(new_nsproxy); |
1718 | 1655 | ||
1719 | bad_unshare_cleanup_ipc: | ||
1720 | if (new_ipc) | ||
1721 | put_ipc_ns(new_ipc); | ||
1722 | |||
1723 | bad_unshare_cleanup_uts: | ||
1724 | if (new_uts) | ||
1725 | put_uts_ns(new_uts); | ||
1726 | |||
1727 | bad_unshare_cleanup_semundo: | 1656 | bad_unshare_cleanup_semundo: |
1728 | bad_unshare_cleanup_fd: | 1657 | bad_unshare_cleanup_fd: |
1729 | if (new_fd) | 1658 | if (new_fd) |
@@ -1738,10 +1667,6 @@ bad_unshare_cleanup_sigh: | |||
1738 | if (atomic_dec_and_test(&new_sigh->count)) | 1667 | if (atomic_dec_and_test(&new_sigh->count)) |
1739 | kmem_cache_free(sighand_cachep, new_sigh); | 1668 | kmem_cache_free(sighand_cachep, new_sigh); |
1740 | 1669 | ||
1741 | bad_unshare_cleanup_ns: | ||
1742 | if (new_ns) | ||
1743 | put_mnt_ns(new_ns); | ||
1744 | |||
1745 | bad_unshare_cleanup_fs: | 1670 | bad_unshare_cleanup_fs: |
1746 | if (new_fs) | 1671 | if (new_fs) |
1747 | put_fs_struct(new_fs); | 1672 | put_fs_struct(new_fs); |
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index f5b9ee6f6bbb..1bc4b55241a8 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c | |||
@@ -38,10 +38,8 @@ void get_task_namespaces(struct task_struct *tsk) | |||
38 | 38 | ||
39 | /* | 39 | /* |
40 | * creates a copy of "orig" with refcount 1. | 40 | * creates a copy of "orig" with refcount 1. |
41 | * This does not grab references to the contained namespaces, | ||
42 | * so that needs to be done by dup_namespaces. | ||
43 | */ | 41 | */ |
44 | static inline struct nsproxy *clone_namespaces(struct nsproxy *orig) | 42 | static inline struct nsproxy *clone_nsproxy(struct nsproxy *orig) |
45 | { | 43 | { |
46 | struct nsproxy *ns; | 44 | struct nsproxy *ns; |
47 | 45 | ||
@@ -52,26 +50,49 @@ static inline struct nsproxy *clone_namespaces(struct nsproxy *orig) | |||
52 | } | 50 | } |
53 | 51 | ||
54 | /* | 52 | /* |
55 | * copies the nsproxy, setting refcount to 1, and grabbing a | 53 | * Create new nsproxy and all of its the associated namespaces. |
56 | * reference to all contained namespaces. Called from | 54 | * Return the newly created nsproxy. Do not attach this to the task, |
57 | * sys_unshare() | 55 | * leave it to the caller to do proper locking and attach it to task. |
58 | */ | 56 | */ |
59 | struct nsproxy *dup_namespaces(struct nsproxy *orig) | 57 | static struct nsproxy *create_new_namespaces(int flags, struct task_struct *tsk, |
58 | struct fs_struct *new_fs) | ||
60 | { | 59 | { |
61 | struct nsproxy *ns = clone_namespaces(orig); | 60 | struct nsproxy *new_nsp; |
62 | 61 | ||
63 | if (ns) { | 62 | new_nsp = clone_nsproxy(tsk->nsproxy); |
64 | if (ns->mnt_ns) | 63 | if (!new_nsp) |
65 | get_mnt_ns(ns->mnt_ns); | 64 | return ERR_PTR(-ENOMEM); |
66 | if (ns->uts_ns) | ||
67 | get_uts_ns(ns->uts_ns); | ||
68 | if (ns->ipc_ns) | ||
69 | get_ipc_ns(ns->ipc_ns); | ||
70 | if (ns->pid_ns) | ||
71 | get_pid_ns(ns->pid_ns); | ||
72 | } | ||
73 | 65 | ||
74 | return ns; | 66 | new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, new_fs); |
67 | if (IS_ERR(new_nsp->mnt_ns)) | ||
68 | goto out_ns; | ||
69 | |||
70 | new_nsp->uts_ns = copy_utsname(flags, tsk->nsproxy->uts_ns); | ||
71 | if (IS_ERR(new_nsp->uts_ns)) | ||
72 | goto out_uts; | ||
73 | |||
74 | new_nsp->ipc_ns = copy_ipcs(flags, tsk->nsproxy->ipc_ns); | ||
75 | if (IS_ERR(new_nsp->ipc_ns)) | ||
76 | goto out_ipc; | ||
77 | |||
78 | new_nsp->pid_ns = copy_pid_ns(flags, tsk->nsproxy->pid_ns); | ||
79 | if (IS_ERR(new_nsp->pid_ns)) | ||
80 | goto out_pid; | ||
81 | |||
82 | return new_nsp; | ||
83 | |||
84 | out_pid: | ||
85 | if (new_nsp->ipc_ns) | ||
86 | put_ipc_ns(new_nsp->ipc_ns); | ||
87 | out_ipc: | ||
88 | if (new_nsp->uts_ns) | ||
89 | put_uts_ns(new_nsp->uts_ns); | ||
90 | out_uts: | ||
91 | if (new_nsp->mnt_ns) | ||
92 | put_mnt_ns(new_nsp->mnt_ns); | ||
93 | out_ns: | ||
94 | kfree(new_nsp); | ||
95 | return ERR_PTR(-ENOMEM); | ||
75 | } | 96 | } |
76 | 97 | ||
77 | /* | 98 | /* |
@@ -92,47 +113,21 @@ int copy_namespaces(int flags, struct task_struct *tsk) | |||
92 | if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC))) | 113 | if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC))) |
93 | return 0; | 114 | return 0; |
94 | 115 | ||
95 | new_ns = clone_namespaces(old_ns); | 116 | if (!capable(CAP_SYS_ADMIN)) { |
96 | if (!new_ns) { | 117 | err = -EPERM; |
97 | err = -ENOMEM; | ||
98 | goto out; | 118 | goto out; |
99 | } | 119 | } |
100 | 120 | ||
101 | tsk->nsproxy = new_ns; | 121 | new_ns = create_new_namespaces(flags, tsk, tsk->fs); |
102 | 122 | if (IS_ERR(new_ns)) { | |
103 | err = copy_mnt_ns(flags, tsk); | 123 | err = PTR_ERR(new_ns); |
104 | if (err) | 124 | goto out; |
105 | goto out_ns; | 125 | } |
106 | |||
107 | err = copy_utsname(flags, tsk); | ||
108 | if (err) | ||
109 | goto out_uts; | ||
110 | |||
111 | err = copy_ipcs(flags, tsk); | ||
112 | if (err) | ||
113 | goto out_ipc; | ||
114 | |||
115 | err = copy_pid_ns(flags, tsk); | ||
116 | if (err) | ||
117 | goto out_pid; | ||
118 | 126 | ||
127 | tsk->nsproxy = new_ns; | ||
119 | out: | 128 | out: |
120 | put_nsproxy(old_ns); | 129 | put_nsproxy(old_ns); |
121 | return err; | 130 | return err; |
122 | |||
123 | out_pid: | ||
124 | if (new_ns->ipc_ns) | ||
125 | put_ipc_ns(new_ns->ipc_ns); | ||
126 | out_ipc: | ||
127 | if (new_ns->uts_ns) | ||
128 | put_uts_ns(new_ns->uts_ns); | ||
129 | out_uts: | ||
130 | if (new_ns->mnt_ns) | ||
131 | put_mnt_ns(new_ns->mnt_ns); | ||
132 | out_ns: | ||
133 | tsk->nsproxy = old_ns; | ||
134 | kfree(new_ns); | ||
135 | goto out; | ||
136 | } | 131 | } |
137 | 132 | ||
138 | void free_nsproxy(struct nsproxy *ns) | 133 | void free_nsproxy(struct nsproxy *ns) |
@@ -147,3 +142,41 @@ void free_nsproxy(struct nsproxy *ns) | |||
147 | put_pid_ns(ns->pid_ns); | 142 | put_pid_ns(ns->pid_ns); |
148 | kfree(ns); | 143 | kfree(ns); |
149 | } | 144 | } |
145 | |||
146 | /* | ||
147 | * Called from unshare. Unshare all the namespaces part of nsproxy. | ||
148 | * On sucess, returns the new nsproxy and a reference to old nsproxy | ||
149 | * to make sure it stays around. | ||
150 | */ | ||
151 | int unshare_nsproxy_namespaces(unsigned long unshare_flags, | ||
152 | struct nsproxy **new_nsp, struct fs_struct *new_fs) | ||
153 | { | ||
154 | struct nsproxy *old_ns = current->nsproxy; | ||
155 | int err = 0; | ||
156 | |||
157 | if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC))) | ||
158 | return 0; | ||
159 | |||
160 | #ifndef CONFIG_IPC_NS | ||
161 | if (unshare_flags & CLONE_NEWIPC) | ||
162 | return -EINVAL; | ||
163 | #endif | ||
164 | |||
165 | #ifndef CONFIG_UTS_NS | ||
166 | if (unshare_flags & CLONE_NEWUTS) | ||
167 | return -EINVAL; | ||
168 | #endif | ||
169 | |||
170 | if (!capable(CAP_SYS_ADMIN)) | ||
171 | return -EPERM; | ||
172 | |||
173 | get_nsproxy(old_ns); | ||
174 | |||
175 | *new_nsp = create_new_namespaces(unshare_flags, current, | ||
176 | new_fs ? new_fs : current->fs); | ||
177 | if (IS_ERR(*new_nsp)) { | ||
178 | err = PTR_ERR(*new_nsp); | ||
179 | put_nsproxy(old_ns); | ||
180 | } | ||
181 | return err; | ||
182 | } | ||
diff --git a/kernel/pid.c b/kernel/pid.c index 9c80bc23d6b8..d3ad724afa83 100644 --- a/kernel/pid.c +++ b/kernel/pid.c | |||
@@ -360,16 +360,11 @@ struct pid *find_ge_pid(int nr) | |||
360 | } | 360 | } |
361 | EXPORT_SYMBOL_GPL(find_get_pid); | 361 | EXPORT_SYMBOL_GPL(find_get_pid); |
362 | 362 | ||
363 | int copy_pid_ns(int flags, struct task_struct *tsk) | 363 | struct pid_namespace *copy_pid_ns(int flags, struct pid_namespace *old_ns) |
364 | { | 364 | { |
365 | struct pid_namespace *old_ns = tsk->nsproxy->pid_ns; | 365 | BUG_ON(!old_ns); |
366 | int err = 0; | ||
367 | |||
368 | if (!old_ns) | ||
369 | return 0; | ||
370 | |||
371 | get_pid_ns(old_ns); | 366 | get_pid_ns(old_ns); |
372 | return err; | 367 | return old_ns; |
373 | } | 368 | } |
374 | 369 | ||
375 | void free_pid_ns(struct kref *kref) | 370 | void free_pid_ns(struct kref *kref) |
diff --git a/kernel/utsname.c b/kernel/utsname.c index c859164a6993..160c8c5136bd 100644 --- a/kernel/utsname.c +++ b/kernel/utsname.c | |||
@@ -32,58 +32,25 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns) | |||
32 | } | 32 | } |
33 | 33 | ||
34 | /* | 34 | /* |
35 | * unshare the current process' utsname namespace. | ||
36 | * called only in sys_unshare() | ||
37 | */ | ||
38 | int unshare_utsname(unsigned long unshare_flags, struct uts_namespace **new_uts) | ||
39 | { | ||
40 | if (unshare_flags & CLONE_NEWUTS) { | ||
41 | if (!capable(CAP_SYS_ADMIN)) | ||
42 | return -EPERM; | ||
43 | |||
44 | *new_uts = clone_uts_ns(current->nsproxy->uts_ns); | ||
45 | if (!*new_uts) | ||
46 | return -ENOMEM; | ||
47 | } | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * Copy task tsk's utsname namespace, or clone it if flags | 35 | * Copy task tsk's utsname namespace, or clone it if flags |
54 | * specifies CLONE_NEWUTS. In latter case, changes to the | 36 | * specifies CLONE_NEWUTS. In latter case, changes to the |
55 | * utsname of this process won't be seen by parent, and vice | 37 | * utsname of this process won't be seen by parent, and vice |
56 | * versa. | 38 | * versa. |
57 | */ | 39 | */ |
58 | int copy_utsname(int flags, struct task_struct *tsk) | 40 | struct uts_namespace *copy_utsname(int flags, struct uts_namespace *old_ns) |
59 | { | 41 | { |
60 | struct uts_namespace *old_ns = tsk->nsproxy->uts_ns; | ||
61 | struct uts_namespace *new_ns; | 42 | struct uts_namespace *new_ns; |
62 | int err = 0; | ||
63 | |||
64 | if (!old_ns) | ||
65 | return 0; | ||
66 | 43 | ||
44 | BUG_ON(!old_ns); | ||
67 | get_uts_ns(old_ns); | 45 | get_uts_ns(old_ns); |
68 | 46 | ||
69 | if (!(flags & CLONE_NEWUTS)) | 47 | if (!(flags & CLONE_NEWUTS)) |
70 | return 0; | 48 | return old_ns; |
71 | |||
72 | if (!capable(CAP_SYS_ADMIN)) { | ||
73 | err = -EPERM; | ||
74 | goto out; | ||
75 | } | ||
76 | 49 | ||
77 | new_ns = clone_uts_ns(old_ns); | 50 | new_ns = clone_uts_ns(old_ns); |
78 | if (!new_ns) { | ||
79 | err = -ENOMEM; | ||
80 | goto out; | ||
81 | } | ||
82 | tsk->nsproxy->uts_ns = new_ns; | ||
83 | 51 | ||
84 | out: | ||
85 | put_uts_ns(old_ns); | 52 | put_uts_ns(old_ns); |
86 | return err; | 53 | return new_ns; |
87 | } | 54 | } |
88 | 55 | ||
89 | void free_uts_ns(struct kref *kref) | 56 | void free_uts_ns(struct kref *kref) |