Newer
Older
/*
* Special case for "unmounting" root ...
* we just try to remount it readonly.
*/
down_write(&sb->s_umount);
if (!(sb->s_flags & MS_RDONLY)) {
lock_kernel();
DQUOT_OFF(sb);
retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
unlock_kernel();
}
up_write(&sb->s_umount);
return retval;
}
if (!(flags & MNT_DETACH))
shrink_submounts(mnt, &umount_list);
if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
retval = 0;
}
spin_unlock(&vfsmount_lock);
if (retval)
security_sb_umount_busy(mnt);
return retval;
}
/*
* Now umount can handle mount points as well as block devices.
* This is important for filesystems which use unnamed block devices.
*
* We now support a flag for forced unmount like the other 'big iron'
* unixes. Our API is identical to OSF/1 to avoid making a mess of AMD
*/
asmlinkage long sys_umount(char __user * name, int flags)
{
struct nameidata nd;
int retval;
retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
if (retval)
goto out;
retval = -EINVAL;
if (nd.path.dentry != nd.path.mnt->mnt_root)
if (!check_mnt(nd.path.mnt))
goto dput_and_out;
retval = -EPERM;
if (!capable(CAP_SYS_ADMIN))
goto dput_and_out;
retval = do_umount(nd.path.mnt, flags);
/* we mustn't call path_put() as that would clear mnt_expiry_mark */
dput(nd.path.dentry);
mntput_no_expire(nd.path.mnt);
out:
return retval;
}
#ifdef __ARCH_WANT_SYS_OLDUMOUNT
/*
*/
asmlinkage long sys_oldumount(char __user * name)
{
}
#endif
static int mount_is_safe(struct nameidata *nd)
{
if (capable(CAP_SYS_ADMIN))
return 0;
return -EPERM;
#ifdef notyet
if (S_ISLNK(nd->path.dentry->d_inode->i_mode))
if (nd->path.dentry->d_inode->i_mode & S_ISVTX) {
if (current->uid != nd->path.dentry->d_inode->i_uid)
if (vfs_permission(nd, MAY_WRITE))
return -EPERM;
return 0;
#endif
}
static int lives_below_in_same_fs(struct dentry *d, struct dentry *dentry)
{
while (1) {
if (d == dentry)
return 1;
if (d == NULL || d == d->d_parent)
return 0;
d = d->d_parent;
}
}
struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
return NULL;
if (!q)
goto Enomem;
q->mnt_mountpoint = mnt->mnt_mountpoint;
p = mnt;
list_for_each_entry(r, &mnt->mnt_mounts, mnt_child) {
if (!lives_below_in_same_fs(r->mnt_mountpoint, dentry))
continue;
for (s = r; s; s = next_mnt(s, r)) {
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) {
s = skip_mnt_tree(s);
continue;
}
while (p != s->mnt_parent) {
p = p->mnt_parent;
q = q->mnt_parent;
}
p = s;
path.mnt = q;
path.dentry = p->mnt_mountpoint;
if (!q)
goto Enomem;
spin_lock(&vfsmount_lock);
list_add_tail(&q->mnt_list, &res->mnt_list);
spin_unlock(&vfsmount_lock);
}
}
return res;
struct vfsmount *collect_mounts(struct vfsmount *mnt, struct dentry *dentry)
{
struct vfsmount *tree;
down_write(&namespace_sem);
tree = copy_tree(mnt, dentry, CL_COPY_ALL | CL_PRIVATE);
up_write(&namespace_sem);
return tree;
}
void drop_collected_mounts(struct vfsmount *mnt)
{
LIST_HEAD(umount_list);
down_write(&namespace_sem);
spin_lock(&vfsmount_lock);
umount_tree(mnt, 0, &umount_list);
spin_unlock(&vfsmount_lock);
up_write(&namespace_sem);
release_mounts(&umount_list);
}
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end)
{
struct vfsmount *p;
for (p = mnt; p != end; p = next_mnt(p, mnt)) {
if (p->mnt_group_id && !IS_MNT_SHARED(p))
mnt_release_group_id(p);
}
}
static int invent_group_ids(struct vfsmount *mnt, bool recurse)
{
struct vfsmount *p;
for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) {
if (!p->mnt_group_id && !IS_MNT_SHARED(p)) {
int err = mnt_alloc_group_id(p);
if (err) {
cleanup_group_ids(mnt, p);
return err;
}
}
}
return 0;
}
/*
* @source_mnt : mount tree to be attached
* @nd : place the mount tree @source_mnt is attached
* @parent_nd : if non-null, detach the source_mnt from its parent and
* store the parent mount and mountpoint dentry.
* (done when source_mnt is moved)
*
* NOTE: in the table below explains the semantics when a source mount
* of a given type is attached to a destination mount of a given type.
* ---------------------------------------------------------------------------
* | BIND MOUNT OPERATION |
* |**************************************************************************
* | source-->| shared | private | slave | unbindable |
* | dest | | | | |
* | | | | | | |
* | v | | | | |
* |**************************************************************************
* | shared | shared (++) | shared (+) | shared(+++)| invalid |
* | | | | | |
* |non-shared| shared (+) | private | slave (*) | invalid |
* ***************************************************************************
* A bind operation clones the source mount and mounts the clone on the
* destination mount.
*
* (++) the cloned mount is propagated to all the mounts in the propagation
* tree of the destination mount and the cloned mount is added to
* the peer group of the source mount.
* (+) the cloned mount is created under the destination mount and is marked
* as shared. The cloned mount is added to the peer group of the source
* mount.
* (+++) the mount is propagated to all the mounts in the propagation tree
* of the destination mount and the cloned mount is made slave
* of the same master as that of the source mount. The cloned mount
* is marked as 'shared and slave'.
* (*) the cloned mount is made a slave of the same master as that of the
* source mount.
*
* ---------------------------------------------------------------------------
* | MOVE MOUNT OPERATION |
* |**************************************************************************
* | source-->| shared | private | slave | unbindable |
* | dest | | | | |
* | | | | | | |
* | v | | | | |
* |**************************************************************************
* | shared | shared (+) | shared (+) | shared(+++) | invalid |
* | | | | | |
* |non-shared| shared (+*) | private | slave (*) | unbindable |
* ***************************************************************************
*
* (+) the mount is moved to the destination. And is then propagated to
* all the mounts in the propagation tree of the destination mount.
* (+*) the mount is moved to the destination.
* (+++) the mount is moved to the destination and is then propagated to
* all the mounts belonging to the destination mount's propagation tree.
* the mount is marked as 'shared and slave'.
* (*) the mount continues to be a slave at the new location.
*
* if the source mount is a tree, the operations explained above is
* applied to each mount in the tree.
* Must be called without spinlocks held, since this function can sleep
* in allocations.
*/
static int attach_recursive_mnt(struct vfsmount *source_mnt,
struct path *path, struct path *parent_path)
struct vfsmount *dest_mnt = path->mnt;
struct dentry *dest_dentry = path->dentry;
if (IS_MNT_SHARED(dest_mnt)) {
err = invent_group_ids(source_mnt, true);
if (err)
goto out;
}
err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list);
if (err)
goto out_cleanup_ids;
if (IS_MNT_SHARED(dest_mnt)) {
for (p = source_mnt; p; p = next_mnt(p, source_mnt))
set_mnt_shared(p);
}
spin_lock(&vfsmount_lock);
if (parent_path) {
detach_mnt(source_mnt, parent_path);
attach_mnt(source_mnt, path);
touch_mnt_namespace(current->nsproxy->mnt_ns);
} else {
mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
commit_tree(source_mnt);
}
list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
list_del_init(&child->mnt_hash);
commit_tree(child);
}
spin_unlock(&vfsmount_lock);
return 0;
out_cleanup_ids:
if (IS_MNT_SHARED(dest_mnt))
cleanup_group_ids(source_mnt, NULL);
out:
return err;
static int graft_tree(struct vfsmount *mnt, struct path *path)
{
int err;
if (mnt->mnt_sb->s_flags & MS_NOUSER)
return -EINVAL;
if (S_ISDIR(path->dentry->d_inode->i_mode) !=
S_ISDIR(mnt->mnt_root->d_inode->i_mode))
return -ENOTDIR;
err = -ENOENT;
mutex_lock(&path->dentry->d_inode->i_mutex);
if (IS_DEADDIR(path->dentry->d_inode))
err = security_sb_check_sb(mnt, path);
if (err)
goto out_unlock;
err = -ENOENT;
if (IS_ROOT(path->dentry) || !d_unhashed(path->dentry))
err = attach_recursive_mnt(mnt, path, NULL);
mutex_unlock(&path->dentry->d_inode->i_mutex);
security_sb_post_addmount(mnt, path);
/*
* recursively change the type of the mountpoint.
* noinline this do_mount helper to save do_mount stack space.
static noinline int do_change_type(struct nameidata *nd, int flag)
struct vfsmount *m, *mnt = nd->path.mnt;
int recurse = flag & MS_REC;
int type = flag & ~MS_REC;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (nd->path.dentry != nd->path.mnt->mnt_root)
return -EINVAL;
down_write(&namespace_sem);
if (type == MS_SHARED) {
err = invent_group_ids(mnt, recurse);
if (err)
goto out_unlock;
}
spin_lock(&vfsmount_lock);
for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL))
change_mnt_propagation(m, type);
spin_unlock(&vfsmount_lock);
* noinline this do_mount helper to save do_mount stack space.
static noinline int do_loopback(struct nameidata *nd, char *old_name,
int recurse)
{
struct nameidata old_nd;
struct vfsmount *mnt = NULL;
int err = mount_is_safe(nd);
if (err)
return err;
if (!old_name || !*old_name)
return -EINVAL;
err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd);
if (err)
return err;
if (IS_MNT_UNBINDABLE(old_nd.path.mnt))
goto out;
if (!check_mnt(nd->path.mnt) || !check_mnt(old_nd.path.mnt))
err = -ENOMEM;
if (recurse)
mnt = copy_tree(old_nd.path.mnt, old_nd.path.dentry, 0);
mnt = clone_mnt(old_nd.path.mnt, old_nd.path.dentry, 0);
if (!mnt)
goto out;
err = graft_tree(mnt, &nd->path);
static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
{
int error = 0;
int readonly_request = 0;
if (ms_flags & MS_RDONLY)
readonly_request = 1;
if (readonly_request == __mnt_is_readonly(mnt))
return 0;
if (readonly_request)
error = mnt_make_readonly(mnt);
else
__mnt_unmake_readonly(mnt);
return error;
}
/*
* change filesystem flags. dir should be a physical root of filesystem.
* If you've mounted a non-root directory somewhere and want to do remount
* on it - tough luck.
* noinline this do_mount helper to save do_mount stack space.
static noinline int do_remount(struct nameidata *nd, int flags, int mnt_flags,
struct super_block *sb = nd->path.mnt->mnt_sb;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (!check_mnt(nd->path.mnt))
if (nd->path.dentry != nd->path.mnt->mnt_root)
if (flags & MS_BIND)
err = change_mount_flags(nd->path.mnt, flags);
else
err = do_remount_sb(sb, flags, data, 0);
nd->path.mnt->mnt_flags = mnt_flags;
security_sb_post_remount(nd->path.mnt, flags, data);
static inline int tree_contains_unbindable(struct vfsmount *mnt)
{
struct vfsmount *p;
for (p = mnt; p; p = next_mnt(p, mnt)) {
if (IS_MNT_UNBINDABLE(p))
return 1;
}
return 0;
}
/*
* noinline this do_mount helper to save do_mount stack space.
*/
static noinline int do_move_mount(struct nameidata *nd, char *old_name)
struct nameidata old_nd;
struct path parent_path;
struct vfsmount *p;
int err = 0;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (!old_name || !*old_name)
return -EINVAL;
err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd);
if (err)
return err;
while (d_mountpoint(nd->path.dentry) &&
follow_down(&nd->path.mnt, &nd->path.dentry))
if (!check_mnt(nd->path.mnt) || !check_mnt(old_nd.path.mnt))
mutex_lock(&nd->path.dentry->d_inode->i_mutex);
if (IS_DEADDIR(nd->path.dentry->d_inode))
if (!IS_ROOT(nd->path.dentry) && d_unhashed(nd->path.dentry))
if (old_nd.path.dentry != old_nd.path.mnt->mnt_root)
if (old_nd.path.mnt == old_nd.path.mnt->mnt_parent)
if (S_ISDIR(nd->path.dentry->d_inode->i_mode) !=
S_ISDIR(old_nd.path.dentry->d_inode->i_mode))
goto out1;
/*
* Don't move a mount residing in a shared parent.
*/
if (old_nd.path.mnt->mnt_parent &&
IS_MNT_SHARED(old_nd.path.mnt->mnt_parent))
/*
* Don't move a mount tree containing unbindable mounts to a destination
* mount which is shared.
*/
if (IS_MNT_SHARED(nd->path.mnt) &&
tree_contains_unbindable(old_nd.path.mnt))
for (p = nd->path.mnt; p->mnt_parent != p; p = p->mnt_parent)
if (p == old_nd.path.mnt)
err = attach_recursive_mnt(old_nd.path.mnt, &nd->path, &parent_path);
if (err)
/* if the mount is moved, it should no longer be expire
* automatically */
list_del_init(&old_nd.path.mnt->mnt_expire);
mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
return err;
}
/*
* create a new mount for userspace and request it to be added into the
* namespace's tree
* noinline this do_mount helper to save do_mount stack space.
static noinline int do_new_mount(struct nameidata *nd, char *type, int flags,
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
int mnt_flags, char *name, void *data)
{
struct vfsmount *mnt;
if (!type || !memchr(type, 0, PAGE_SIZE))
return -EINVAL;
/* we need capabilities... */
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
mnt = do_kern_mount(type, flags, name, data);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
return do_add_mount(mnt, nd, mnt_flags, NULL);
}
/*
* add a mount into a namespace's mount tree
* - provide the option of adding the new mount to an expiration list
*/
int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
int mnt_flags, struct list_head *fslist)
{
int err;
while (d_mountpoint(nd->path.dentry) &&
follow_down(&nd->path.mnt, &nd->path.dentry))
if (!check_mnt(nd->path.mnt))
goto unlock;
/* Refuse the same filesystem on the same mount point */
err = -EBUSY;
if (nd->path.mnt->mnt_sb == newmnt->mnt_sb &&
nd->path.mnt->mnt_root == nd->path.dentry)
goto unlock;
err = -EINVAL;
if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
goto unlock;
newmnt->mnt_flags = mnt_flags;
if ((err = graft_tree(newmnt, &nd->path)))
if (fslist) /* add to the specified expiration list */
list_add_tail(&newmnt->mnt_expire, fslist);
mntput(newmnt);
return err;
}
EXPORT_SYMBOL_GPL(do_add_mount);
/*
* process a list of expirable mountpoints with the intent of discarding any
* mountpoints that aren't in use and haven't been touched since last we came
* here
*/
void mark_mounts_for_expiry(struct list_head *mounts)
{
struct vfsmount *mnt, *next;
LIST_HEAD(graveyard);
LIST_HEAD(umounts);
down_write(&namespace_sem);
spin_lock(&vfsmount_lock);
/* extract from the expiration list every vfsmount that matches the
* following criteria:
* - only referenced by its parent vfsmount
* - still marked for expiry (marked on the last call here; marks are
* cleared by mntput())
*/
list_for_each_entry_safe(mnt, next, mounts, mnt_expire) {
propagate_mount_busy(mnt, 1))
list_move(&mnt->mnt_expire, &graveyard);
while (!list_empty(&graveyard)) {
mnt = list_first_entry(&graveyard, struct vfsmount, mnt_expire);
touch_mnt_namespace(mnt->mnt_ns);
umount_tree(mnt, 1, &umounts);
}
up_write(&namespace_sem);
release_mounts(&umounts);
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
}
EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
/*
* Ripoff of 'select_parent()'
*
* search the list of submounts for a given mountpoint, and move any
* shrinkable submounts to the 'graveyard' list.
*/
static int select_submounts(struct vfsmount *parent, struct list_head *graveyard)
{
struct vfsmount *this_parent = parent;
struct list_head *next;
int found = 0;
repeat:
next = this_parent->mnt_mounts.next;
resume:
while (next != &this_parent->mnt_mounts) {
struct list_head *tmp = next;
struct vfsmount *mnt = list_entry(tmp, struct vfsmount, mnt_child);
next = tmp->next;
if (!(mnt->mnt_flags & MNT_SHRINKABLE))
/*
* Descend a level if the d_mounts list is non-empty.
*/
if (!list_empty(&mnt->mnt_mounts)) {
this_parent = mnt;
goto repeat;
}
if (!propagate_mount_busy(mnt, 1)) {
list_move_tail(&mnt->mnt_expire, graveyard);
found++;
}
/*
* All done at this level ... ascend and resume the search
*/
if (this_parent != parent) {
next = this_parent->mnt_child.next;
this_parent = this_parent->mnt_parent;
goto resume;
}
return found;
}
/*
* process a list of expirable mountpoints with the intent of discarding any
* submounts of a specific parent mountpoint
*/
static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts)
/* extract submounts of 'mountpoint' from the expiration list */
while (select_submounts(mnt, &graveyard)) {
while (!list_empty(&graveyard)) {
m = list_first_entry(&graveyard, struct vfsmount,
mnt_expire);
touch_mnt_namespace(mnt->mnt_ns);
}
}
}
/*
* Some copy_from_user() implementations do not return the exact number of
* bytes remaining to copy on a fault. But copy_mount_options() requires that.
* Note that this function differs from copy_from_user() in that it will oops
* on bad values of `to', rather than returning a short copy.
*/
static long exact_copy_from_user(void *to, const void __user * from,
unsigned long n)
{
char *t = to;
const char __user *f = from;
char c;
if (!access_ok(VERIFY_READ, from, n))
return n;
while (n) {
if (__get_user(c, f)) {
memset(t, 0, n);
break;
}
*t++ = c;
f++;
n--;
}
return n;
}
int copy_mount_options(const void __user * data, unsigned long *where)
{
int i;
unsigned long page;
unsigned long size;
*where = 0;
if (!data)
return 0;
if (!(page = __get_free_page(GFP_KERNEL)))
return -ENOMEM;
/* We only care that *some* data at the address the user
* gave us is valid. Just in case, we'll zero
* the remainder of the page.
*/
/* copy_from_user cannot cross TASK_SIZE ! */
size = TASK_SIZE - (unsigned long)data;
if (size > PAGE_SIZE)
size = PAGE_SIZE;
i = size - exact_copy_from_user((void *)page, data, size);
if (!i) {
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
return -EFAULT;
}
if (i != PAGE_SIZE)
memset((char *)page + i, 0, PAGE_SIZE - i);
*where = page;
return 0;
}
/*
* Flags is a 32-bit value that allows up to 31 non-fs dependent flags to
* be given to the mount() call (ie: read-only, no-dev, no-suid etc).
*
* data is a (void *) that can point to any structure up to
* PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent
* information (or be NULL).
*
* Pre-0.97 versions of mount() didn't have a flags word.
* When the flags word was introduced its top half was required
* to have the magic value 0xC0ED, and this remained so until 2.4.0-test9.
* Therefore, if this magic number is present, it carries no information
* and must be discarded.
*/
long do_mount(char *dev_name, char *dir_name, char *type_page,
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
unsigned long flags, void *data_page)
{
struct nameidata nd;
int retval = 0;
int mnt_flags = 0;
/* Discard magic */
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
flags &= ~MS_MGC_MSK;
/* Basic sanity checks */
if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
return -EINVAL;
if (dev_name && !memchr(dev_name, 0, PAGE_SIZE))
return -EINVAL;
if (data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
/* Separate the per-mountpoint flags */
if (flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;
if (flags & MS_NODEV)
mnt_flags |= MNT_NODEV;
if (flags & MS_NOEXEC)
mnt_flags |= MNT_NOEXEC;
if (flags & MS_NOATIME)
mnt_flags |= MNT_NOATIME;
if (flags & MS_NODIRATIME)
mnt_flags |= MNT_NODIRATIME;
if (flags & MS_RELATIME)
mnt_flags |= MNT_RELATIME;
if (flags & MS_RDONLY)
mnt_flags |= MNT_READONLY;
flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT);
/* ... and get the mountpoint */
retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd);
if (retval)
return retval;
retval = security_sb_mount(dev_name, &nd.path,
type_page, flags, data_page);
if (retval)
goto dput_out;
if (flags & MS_REMOUNT)
retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
data_page);
else if (flags & MS_BIND)
retval = do_loopback(&nd, dev_name, flags & MS_REC);
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
retval = do_change_type(&nd, flags);
else if (flags & MS_MOVE)
retval = do_move_mount(&nd, dev_name);
else
retval = do_new_mount(&nd, type_page, flags, mnt_flags,
dev_name, data_page);
dput_out:
/*
* Allocate a new namespace structure and populate it with contents
* copied from the namespace of the passed in task structure.
*/
static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
struct fs_struct *fs)
struct mnt_namespace *new_ns;
struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL;
struct vfsmount *p, *q;
new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
atomic_set(&new_ns->count, 1);
INIT_LIST_HEAD(&new_ns->list);
init_waitqueue_head(&new_ns->poll);
new_ns->event = 0;
new_ns->root = copy_tree(mnt_ns->root, mnt_ns->root->mnt_root,
}
spin_lock(&vfsmount_lock);
list_add_tail(&new_ns->list, &new_ns->root->mnt_list);
spin_unlock(&vfsmount_lock);
/*
* Second pass: switch the tsk->fs->* elements and mark new vfsmounts
* as belonging to new namespace. We have already acquired a private
* fs_struct, so tsk->fs->lock is not needed.
*/
p = mnt_ns->root;
q->mnt_ns = new_ns;
p = next_mnt(p, mnt_ns->root);
if (rootmnt)
mntput(rootmnt);
if (pwdmnt)
mntput(pwdmnt);
if (altrootmnt)
mntput(altrootmnt);
return new_ns;
}
struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
struct fs_struct *new_fs)
struct mnt_namespace *new_ns;
BUG_ON(!ns);
get_mnt_ns(ns);
if (!(flags & CLONE_NEWNS))
return ns;
new_ns = dup_mnt_ns(ns, new_fs);
put_mnt_ns(ns);
return new_ns;
}
asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name,
char __user * type, unsigned long flags,
void __user * data)
{
int retval;
unsigned long data_page;
unsigned long type_page;
unsigned long dev_page;
char *dir_page;
retval = copy_mount_options(type, &type_page);
if (retval < 0)
return retval;
dir_page = getname(dir_name);
retval = PTR_ERR(dir_page);
if (IS_ERR(dir_page))
goto out1;