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>
This commit is contained in:
Badari Pulavarty 2007-05-08 00:25:21 -07:00 committed by Linus Torvalds
parent 4fc75ff481
commit e3222c4ecc
11 changed files with 131 additions and 268 deletions

View file

@ -1441,10 +1441,9 @@ dput_out:
* Allocate a new namespace structure and populate it with contents * Allocate a new namespace structure and populate it with contents
* copied from the namespace of the passed in task structure. * copied from the namespace of the passed in task structure.
*/ */
struct mnt_namespace *dup_mnt_ns(struct task_struct *tsk, static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
struct fs_struct *fs) struct fs_struct *fs)
{ {
struct mnt_namespace *mnt_ns = tsk->nsproxy->mnt_ns;
struct mnt_namespace *new_ns; struct mnt_namespace *new_ns;
struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL; struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL;
struct vfsmount *p, *q; struct vfsmount *p, *q;
@ -1509,36 +1508,21 @@ struct mnt_namespace *dup_mnt_ns(struct task_struct *tsk,
return new_ns; return new_ns;
} }
int copy_mnt_ns(int flags, struct task_struct *tsk) struct mnt_namespace *copy_mnt_ns(int flags, struct mnt_namespace *ns,
struct fs_struct *new_fs)
{ {
struct mnt_namespace *ns = tsk->nsproxy->mnt_ns;
struct mnt_namespace *new_ns; struct mnt_namespace *new_ns;
int err = 0;
if (!ns)
return 0;
BUG_ON(!ns);
get_mnt_ns(ns); get_mnt_ns(ns);
if (!(flags & CLONE_NEWNS)) if (!(flags & CLONE_NEWNS))
return 0; return ns;
if (!capable(CAP_SYS_ADMIN)) { new_ns = dup_mnt_ns(ns, new_fs);
err = -EPERM;
goto out;
}
new_ns = dup_mnt_ns(tsk, tsk->fs);
if (!new_ns) {
err = -ENOMEM;
goto out;
}
tsk->nsproxy->mnt_ns = new_ns;
out:
put_mnt_ns(ns); put_mnt_ns(ns);
return err; return new_ns;
} }
asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name, asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name,

View file

@ -92,16 +92,19 @@ extern struct ipc_namespace init_ipc_ns;
#ifdef CONFIG_SYSVIPC #ifdef CONFIG_SYSVIPC
#define INIT_IPC_NS(ns) .ns = &init_ipc_ns, #define INIT_IPC_NS(ns) .ns = &init_ipc_ns,
extern int copy_ipcs(unsigned long flags, struct task_struct *tsk); extern struct ipc_namespace *copy_ipcs(unsigned long flags,
struct ipc_namespace *ns);
#else #else
#define INIT_IPC_NS(ns) #define INIT_IPC_NS(ns)
static inline int copy_ipcs(unsigned long flags, struct task_struct *tsk) static inline struct ipc_namespace *copy_ipcs(unsigned long flags,
{ return 0; } struct ipc_namespace *ns)
{
return ns;
}
#endif #endif
#ifdef CONFIG_IPC_NS #ifdef CONFIG_IPC_NS
extern void free_ipc_ns(struct kref *kref); extern void free_ipc_ns(struct kref *kref);
extern int unshare_ipcs(unsigned long flags, struct ipc_namespace **ns);
#endif #endif
static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns) static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns)

View file

@ -14,10 +14,9 @@ struct mnt_namespace {
int event; int event;
}; };
extern int copy_mnt_ns(int, struct task_struct *); extern struct mnt_namespace *copy_mnt_ns(int, struct mnt_namespace *,
extern void __put_mnt_ns(struct mnt_namespace *ns);
extern struct mnt_namespace *dup_mnt_ns(struct task_struct *,
struct fs_struct *); struct fs_struct *);
extern void __put_mnt_ns(struct mnt_namespace *ns);
static inline void put_mnt_ns(struct mnt_namespace *ns) static inline void put_mnt_ns(struct mnt_namespace *ns)
{ {

View file

@ -31,10 +31,11 @@ struct nsproxy {
}; };
extern struct nsproxy init_nsproxy; extern struct nsproxy init_nsproxy;
struct nsproxy *dup_namespaces(struct nsproxy *orig);
int copy_namespaces(int flags, struct task_struct *tsk); int copy_namespaces(int flags, struct task_struct *tsk);
void get_task_namespaces(struct task_struct *tsk); void get_task_namespaces(struct task_struct *tsk);
void free_nsproxy(struct nsproxy *ns); void free_nsproxy(struct nsproxy *ns);
int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **,
struct fs_struct *);
static inline void put_nsproxy(struct nsproxy *ns) static inline void put_nsproxy(struct nsproxy *ns)
{ {

View file

@ -29,7 +29,7 @@ static inline void get_pid_ns(struct pid_namespace *ns)
kref_get(&ns->kref); kref_get(&ns->kref);
} }
extern int copy_pid_ns(int flags, struct task_struct *tsk); extern struct pid_namespace *copy_pid_ns(int flags, struct pid_namespace *ns);
extern void free_pid_ns(struct kref *kref); extern void free_pid_ns(struct kref *kref);
static inline void put_pid_ns(struct pid_namespace *ns) static inline void put_pid_ns(struct pid_namespace *ns)

View file

@ -49,9 +49,7 @@ static inline void get_uts_ns(struct uts_namespace *ns)
} }
#ifdef CONFIG_UTS_NS #ifdef CONFIG_UTS_NS
extern int unshare_utsname(unsigned long unshare_flags, extern struct uts_namespace *copy_utsname(int flags, struct uts_namespace *ns);
struct uts_namespace **new_uts);
extern int copy_utsname(int flags, struct task_struct *tsk);
extern void free_uts_ns(struct kref *kref); extern void free_uts_ns(struct kref *kref);
static inline void put_uts_ns(struct uts_namespace *ns) static inline void put_uts_ns(struct uts_namespace *ns)
@ -59,21 +57,12 @@ static inline void put_uts_ns(struct uts_namespace *ns)
kref_put(&ns->kref, free_uts_ns); kref_put(&ns->kref, free_uts_ns);
} }
#else #else
static inline int unshare_utsname(unsigned long unshare_flags, static inline struct uts_namespace *copy_utsname(int flags,
struct uts_namespace **new_uts) struct uts_namespace *ns)
{ {
if (unshare_flags & CLONE_NEWUTS) return ns;
return -EINVAL;
return 0;
} }
static inline int copy_utsname(int flags, struct task_struct *tsk)
{
if (flags & CLONE_NEWUTS)
return -EINVAL;
return 0;
}
static inline void put_uts_ns(struct uts_namespace *ns) static inline void put_uts_ns(struct uts_namespace *ns)
{ {
} }

View file

@ -85,53 +85,20 @@ err_mem:
return ERR_PTR(err); return ERR_PTR(err);
} }
int unshare_ipcs(unsigned long unshare_flags, struct ipc_namespace **new_ipc) struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns)
{ {
struct ipc_namespace *new;
if (unshare_flags & CLONE_NEWIPC) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
new = clone_ipc_ns(current->nsproxy->ipc_ns);
if (IS_ERR(new))
return PTR_ERR(new);
*new_ipc = new;
}
return 0;
}
int copy_ipcs(unsigned long flags, struct task_struct *tsk)
{
struct ipc_namespace *old_ns = tsk->nsproxy->ipc_ns;
struct ipc_namespace *new_ns; struct ipc_namespace *new_ns;
int err = 0;
if (!old_ns) BUG_ON(!ns);
return 0; get_ipc_ns(ns);
get_ipc_ns(old_ns);
if (!(flags & CLONE_NEWIPC)) if (!(flags & CLONE_NEWIPC))
return 0; return ns;
if (!capable(CAP_SYS_ADMIN)) { new_ns = clone_ipc_ns(ns);
err = -EPERM;
goto out;
}
new_ns = clone_ipc_ns(old_ns); put_ipc_ns(ns);
if (!new_ns) { return new_ns;
err = -ENOMEM;
goto out;
}
tsk->nsproxy->ipc_ns = new_ns;
out:
put_ipc_ns(old_ns);
return err;
} }
void free_ipc_ns(struct kref *kref) void free_ipc_ns(struct kref *kref)
@ -145,11 +112,11 @@ void free_ipc_ns(struct kref *kref)
kfree(ns); kfree(ns);
} }
#else #else
int copy_ipcs(unsigned long flags, struct task_struct *tsk) struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns)
{ {
if (flags & CLONE_NEWIPC) if (flags & CLONE_NEWIPC)
return -EINVAL; return ERR_PTR(-EINVAL);
return 0; return ns;
} }
#endif #endif

View file

@ -1515,26 +1515,6 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
return 0; return 0;
} }
/*
* Unshare the mnt_namespace structure if it is being shared
*/
static int unshare_mnt_namespace(unsigned long unshare_flags,
struct mnt_namespace **new_nsp, struct fs_struct *new_fs)
{
struct mnt_namespace *ns = current->nsproxy->mnt_ns;
if ((unshare_flags & CLONE_NEWNS) && ns) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
*new_nsp = dup_mnt_ns(current, new_fs ? new_fs : current->fs);
if (!*new_nsp)
return -ENOMEM;
}
return 0;
}
/* /*
* Unsharing of sighand is not supported yet * Unsharing of sighand is not supported yet
*/ */
@ -1593,16 +1573,6 @@ static int unshare_semundo(unsigned long unshare_flags, struct sem_undo_list **n
return 0; return 0;
} }
#ifndef CONFIG_IPC_NS
static inline int unshare_ipcs(unsigned long flags, struct ipc_namespace **ns)
{
if (flags & CLONE_NEWIPC)
return -EINVAL;
return 0;
}
#endif
/* /*
* unshare allows a process to 'unshare' part of the process * unshare allows a process to 'unshare' part of the process
* context which was originally shared using clone. copy_* * context which was originally shared using clone. copy_*
@ -1615,14 +1585,11 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
{ {
int err = 0; int err = 0;
struct fs_struct *fs, *new_fs = NULL; struct fs_struct *fs, *new_fs = NULL;
struct mnt_namespace *ns, *new_ns = NULL;
struct sighand_struct *new_sigh = NULL; struct sighand_struct *new_sigh = NULL;
struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL; struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL;
struct files_struct *fd, *new_fd = NULL; struct files_struct *fd, *new_fd = NULL;
struct sem_undo_list *new_ulist = NULL; struct sem_undo_list *new_ulist = NULL;
struct nsproxy *new_nsproxy = NULL, *old_nsproxy = NULL; struct nsproxy *new_nsproxy = NULL, *old_nsproxy = NULL;
struct uts_namespace *uts, *new_uts = NULL;
struct ipc_namespace *ipc, *new_ipc = NULL;
check_unshare_flags(&unshare_flags); check_unshare_flags(&unshare_flags);
@ -1637,36 +1604,24 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
goto bad_unshare_out; goto bad_unshare_out;
if ((err = unshare_fs(unshare_flags, &new_fs))) if ((err = unshare_fs(unshare_flags, &new_fs)))
goto bad_unshare_cleanup_thread; goto bad_unshare_cleanup_thread;
if ((err = unshare_mnt_namespace(unshare_flags, &new_ns, new_fs)))
goto bad_unshare_cleanup_fs;
if ((err = unshare_sighand(unshare_flags, &new_sigh))) if ((err = unshare_sighand(unshare_flags, &new_sigh)))
goto bad_unshare_cleanup_ns; goto bad_unshare_cleanup_fs;
if ((err = unshare_vm(unshare_flags, &new_mm))) if ((err = unshare_vm(unshare_flags, &new_mm)))
goto bad_unshare_cleanup_sigh; goto bad_unshare_cleanup_sigh;
if ((err = unshare_fd(unshare_flags, &new_fd))) if ((err = unshare_fd(unshare_flags, &new_fd)))
goto bad_unshare_cleanup_vm; goto bad_unshare_cleanup_vm;
if ((err = unshare_semundo(unshare_flags, &new_ulist))) if ((err = unshare_semundo(unshare_flags, &new_ulist)))
goto bad_unshare_cleanup_fd; goto bad_unshare_cleanup_fd;
if ((err = unshare_utsname(unshare_flags, &new_uts))) if ((err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy,
new_fs)))
goto bad_unshare_cleanup_semundo; goto bad_unshare_cleanup_semundo;
if ((err = unshare_ipcs(unshare_flags, &new_ipc)))
goto bad_unshare_cleanup_uts;
if (new_ns || new_uts || new_ipc) { if (new_fs || new_mm || new_fd || new_ulist || new_nsproxy) {
old_nsproxy = current->nsproxy;
new_nsproxy = dup_namespaces(old_nsproxy);
if (!new_nsproxy) {
err = -ENOMEM;
goto bad_unshare_cleanup_ipc;
}
}
if (new_fs || new_ns || new_mm || new_fd || new_ulist ||
new_uts || new_ipc) {
task_lock(current); task_lock(current);
if (new_nsproxy) { if (new_nsproxy) {
old_nsproxy = current->nsproxy;
current->nsproxy = new_nsproxy; current->nsproxy = new_nsproxy;
new_nsproxy = old_nsproxy; new_nsproxy = old_nsproxy;
} }
@ -1677,12 +1632,6 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
new_fs = fs; new_fs = fs;
} }
if (new_ns) {
ns = current->nsproxy->mnt_ns;
current->nsproxy->mnt_ns = new_ns;
new_ns = ns;
}
if (new_mm) { if (new_mm) {
mm = current->mm; mm = current->mm;
active_mm = current->active_mm; active_mm = current->active_mm;
@ -1698,32 +1647,12 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
new_fd = fd; new_fd = fd;
} }
if (new_uts) {
uts = current->nsproxy->uts_ns;
current->nsproxy->uts_ns = new_uts;
new_uts = uts;
}
if (new_ipc) {
ipc = current->nsproxy->ipc_ns;
current->nsproxy->ipc_ns = new_ipc;
new_ipc = ipc;
}
task_unlock(current); task_unlock(current);
} }
if (new_nsproxy) if (new_nsproxy)
put_nsproxy(new_nsproxy); put_nsproxy(new_nsproxy);
bad_unshare_cleanup_ipc:
if (new_ipc)
put_ipc_ns(new_ipc);
bad_unshare_cleanup_uts:
if (new_uts)
put_uts_ns(new_uts);
bad_unshare_cleanup_semundo: bad_unshare_cleanup_semundo:
bad_unshare_cleanup_fd: bad_unshare_cleanup_fd:
if (new_fd) if (new_fd)
@ -1738,10 +1667,6 @@ bad_unshare_cleanup_sigh:
if (atomic_dec_and_test(&new_sigh->count)) if (atomic_dec_and_test(&new_sigh->count))
kmem_cache_free(sighand_cachep, new_sigh); kmem_cache_free(sighand_cachep, new_sigh);
bad_unshare_cleanup_ns:
if (new_ns)
put_mnt_ns(new_ns);
bad_unshare_cleanup_fs: bad_unshare_cleanup_fs:
if (new_fs) if (new_fs)
put_fs_struct(new_fs); put_fs_struct(new_fs);

View file

@ -38,10 +38,8 @@ void get_task_namespaces(struct task_struct *tsk)
/* /*
* creates a copy of "orig" with refcount 1. * creates a copy of "orig" with refcount 1.
* This does not grab references to the contained namespaces,
* so that needs to be done by dup_namespaces.
*/ */
static inline struct nsproxy *clone_namespaces(struct nsproxy *orig) static inline struct nsproxy *clone_nsproxy(struct nsproxy *orig)
{ {
struct nsproxy *ns; struct nsproxy *ns;
@ -52,26 +50,49 @@ static inline struct nsproxy *clone_namespaces(struct nsproxy *orig)
} }
/* /*
* copies the nsproxy, setting refcount to 1, and grabbing a * Create new nsproxy and all of its the associated namespaces.
* reference to all contained namespaces. Called from * Return the newly created nsproxy. Do not attach this to the task,
* sys_unshare() * leave it to the caller to do proper locking and attach it to task.
*/ */
struct nsproxy *dup_namespaces(struct nsproxy *orig) static struct nsproxy *create_new_namespaces(int flags, struct task_struct *tsk,
struct fs_struct *new_fs)
{ {
struct nsproxy *ns = clone_namespaces(orig); struct nsproxy *new_nsp;
if (ns) { new_nsp = clone_nsproxy(tsk->nsproxy);
if (ns->mnt_ns) if (!new_nsp)
get_mnt_ns(ns->mnt_ns); return ERR_PTR(-ENOMEM);
if (ns->uts_ns)
get_uts_ns(ns->uts_ns);
if (ns->ipc_ns)
get_ipc_ns(ns->ipc_ns);
if (ns->pid_ns)
get_pid_ns(ns->pid_ns);
}
return ns; new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, new_fs);
if (IS_ERR(new_nsp->mnt_ns))
goto out_ns;
new_nsp->uts_ns = copy_utsname(flags, tsk->nsproxy->uts_ns);
if (IS_ERR(new_nsp->uts_ns))
goto out_uts;
new_nsp->ipc_ns = copy_ipcs(flags, tsk->nsproxy->ipc_ns);
if (IS_ERR(new_nsp->ipc_ns))
goto out_ipc;
new_nsp->pid_ns = copy_pid_ns(flags, tsk->nsproxy->pid_ns);
if (IS_ERR(new_nsp->pid_ns))
goto out_pid;
return new_nsp;
out_pid:
if (new_nsp->ipc_ns)
put_ipc_ns(new_nsp->ipc_ns);
out_ipc:
if (new_nsp->uts_ns)
put_uts_ns(new_nsp->uts_ns);
out_uts:
if (new_nsp->mnt_ns)
put_mnt_ns(new_nsp->mnt_ns);
out_ns:
kfree(new_nsp);
return ERR_PTR(-ENOMEM);
} }
/* /*
@ -92,47 +113,21 @@ int copy_namespaces(int flags, struct task_struct *tsk)
if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC))) if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC)))
return 0; return 0;
new_ns = clone_namespaces(old_ns); if (!capable(CAP_SYS_ADMIN)) {
if (!new_ns) { err = -EPERM;
err = -ENOMEM; goto out;
}
new_ns = create_new_namespaces(flags, tsk, tsk->fs);
if (IS_ERR(new_ns)) {
err = PTR_ERR(new_ns);
goto out; goto out;
} }
tsk->nsproxy = new_ns; tsk->nsproxy = new_ns;
err = copy_mnt_ns(flags, tsk);
if (err)
goto out_ns;
err = copy_utsname(flags, tsk);
if (err)
goto out_uts;
err = copy_ipcs(flags, tsk);
if (err)
goto out_ipc;
err = copy_pid_ns(flags, tsk);
if (err)
goto out_pid;
out: out:
put_nsproxy(old_ns); put_nsproxy(old_ns);
return err; return err;
out_pid:
if (new_ns->ipc_ns)
put_ipc_ns(new_ns->ipc_ns);
out_ipc:
if (new_ns->uts_ns)
put_uts_ns(new_ns->uts_ns);
out_uts:
if (new_ns->mnt_ns)
put_mnt_ns(new_ns->mnt_ns);
out_ns:
tsk->nsproxy = old_ns;
kfree(new_ns);
goto out;
} }
void free_nsproxy(struct nsproxy *ns) void free_nsproxy(struct nsproxy *ns)
@ -147,3 +142,41 @@ void free_nsproxy(struct nsproxy *ns)
put_pid_ns(ns->pid_ns); put_pid_ns(ns->pid_ns);
kfree(ns); kfree(ns);
} }
/*
* Called from unshare. Unshare all the namespaces part of nsproxy.
* On sucess, returns the new nsproxy and a reference to old nsproxy
* to make sure it stays around.
*/
int unshare_nsproxy_namespaces(unsigned long unshare_flags,
struct nsproxy **new_nsp, struct fs_struct *new_fs)
{
struct nsproxy *old_ns = current->nsproxy;
int err = 0;
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC)))
return 0;
#ifndef CONFIG_IPC_NS
if (unshare_flags & CLONE_NEWIPC)
return -EINVAL;
#endif
#ifndef CONFIG_UTS_NS
if (unshare_flags & CLONE_NEWUTS)
return -EINVAL;
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
get_nsproxy(old_ns);
*new_nsp = create_new_namespaces(unshare_flags, current,
new_fs ? new_fs : current->fs);
if (IS_ERR(*new_nsp)) {
err = PTR_ERR(*new_nsp);
put_nsproxy(old_ns);
}
return err;
}

View file

@ -360,16 +360,11 @@ struct pid *find_ge_pid(int nr)
} }
EXPORT_SYMBOL_GPL(find_get_pid); EXPORT_SYMBOL_GPL(find_get_pid);
int copy_pid_ns(int flags, struct task_struct *tsk) struct pid_namespace *copy_pid_ns(int flags, struct pid_namespace *old_ns)
{ {
struct pid_namespace *old_ns = tsk->nsproxy->pid_ns; BUG_ON(!old_ns);
int err = 0;
if (!old_ns)
return 0;
get_pid_ns(old_ns); get_pid_ns(old_ns);
return err; return old_ns;
} }
void free_pid_ns(struct kref *kref) void free_pid_ns(struct kref *kref)

View file

@ -31,59 +31,26 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)
return ns; return ns;
} }
/*
* unshare the current process' utsname namespace.
* called only in sys_unshare()
*/
int unshare_utsname(unsigned long unshare_flags, struct uts_namespace **new_uts)
{
if (unshare_flags & CLONE_NEWUTS) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
*new_uts = clone_uts_ns(current->nsproxy->uts_ns);
if (!*new_uts)
return -ENOMEM;
}
return 0;
}
/* /*
* Copy task tsk's utsname namespace, or clone it if flags * Copy task tsk's utsname namespace, or clone it if flags
* specifies CLONE_NEWUTS. In latter case, changes to the * specifies CLONE_NEWUTS. In latter case, changes to the
* utsname of this process won't be seen by parent, and vice * utsname of this process won't be seen by parent, and vice
* versa. * versa.
*/ */
int copy_utsname(int flags, struct task_struct *tsk) struct uts_namespace *copy_utsname(int flags, struct uts_namespace *old_ns)
{ {
struct uts_namespace *old_ns = tsk->nsproxy->uts_ns;
struct uts_namespace *new_ns; struct uts_namespace *new_ns;
int err = 0;
if (!old_ns)
return 0;
BUG_ON(!old_ns);
get_uts_ns(old_ns); get_uts_ns(old_ns);
if (!(flags & CLONE_NEWUTS)) if (!(flags & CLONE_NEWUTS))
return 0; return old_ns;
if (!capable(CAP_SYS_ADMIN)) {
err = -EPERM;
goto out;
}
new_ns = clone_uts_ns(old_ns); new_ns = clone_uts_ns(old_ns);
if (!new_ns) {
err = -ENOMEM;
goto out;
}
tsk->nsproxy->uts_ns = new_ns;
out:
put_uts_ns(old_ns); put_uts_ns(old_ns);
return err; return new_ns;
} }
void free_uts_ns(struct kref *kref) void free_uts_ns(struct kref *kref)