diff options
Diffstat (limited to 'fs/namespace.c')
| -rw-r--r-- | fs/namespace.c | 142 | 
1 files changed, 97 insertions, 45 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 182bc41cd887..ef42d9bee212 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -16,7 +16,6 @@  #include <linux/namei.h>  #include <linux/security.h>  #include <linux/idr.h> -#include <linux/acct.h>		/* acct_auto_close_mnt */  #include <linux/init.h>		/* init_rootfs */  #include <linux/fs_struct.h>	/* get_fs_root et.al. */  #include <linux/fsnotify.h>	/* fsnotify_vfsmount_delete */ @@ -779,6 +778,20 @@ static void attach_mnt(struct mount *mnt,  	list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);  } +static void attach_shadowed(struct mount *mnt, +			struct mount *parent, +			struct mount *shadows) +{ +	if (shadows) { +		hlist_add_behind_rcu(&mnt->mnt_hash, &shadows->mnt_hash); +		list_add(&mnt->mnt_child, &shadows->mnt_child); +	} else { +		hlist_add_head_rcu(&mnt->mnt_hash, +				m_hash(&parent->mnt, mnt->mnt_mountpoint)); +		list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); +	} +} +  /*   * vfsmount lock must be held for write   */ @@ -797,12 +810,7 @@ static void commit_tree(struct mount *mnt, struct mount *shadows)  	list_splice(&head, n->list.prev); -	if (shadows) -		hlist_add_after_rcu(&shadows->mnt_hash, &mnt->mnt_hash); -	else -		hlist_add_head_rcu(&mnt->mnt_hash, -				m_hash(&parent->mnt, mnt->mnt_mountpoint)); -	list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); +	attach_shadowed(mnt, parent, shadows);  	touch_mnt_namespace(n);  } @@ -890,8 +898,21 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,  	mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED);  	/* Don't allow unprivileged users to change mount flags */ -	if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) -		mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; +	if (flag & CL_UNPRIVILEGED) { +		mnt->mnt.mnt_flags |= MNT_LOCK_ATIME; + +		if (mnt->mnt.mnt_flags & MNT_READONLY) +			mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; + +		if (mnt->mnt.mnt_flags & MNT_NODEV) +			mnt->mnt.mnt_flags |= MNT_LOCK_NODEV; + +		if (mnt->mnt.mnt_flags & MNT_NOSUID) +			mnt->mnt.mnt_flags |= MNT_LOCK_NOSUID; + +		if (mnt->mnt.mnt_flags & MNT_NOEXEC) +			mnt->mnt.mnt_flags |= MNT_LOCK_NOEXEC; +	}  	/* Don't allow unprivileged users to reveal what is under a mount */  	if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) @@ -938,7 +959,6 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,  static void mntput_no_expire(struct mount *mnt)  { -put_again:  	rcu_read_lock();  	mnt_add_count(mnt, -1);  	if (likely(mnt->mnt_ns)) { /* shouldn't be the last one */ @@ -951,14 +971,6 @@ put_again:  		unlock_mount_hash();  		return;  	} -	if (unlikely(mnt->mnt_pinned)) { -		mnt_add_count(mnt, mnt->mnt_pinned + 1); -		mnt->mnt_pinned = 0; -		rcu_read_unlock(); -		unlock_mount_hash(); -		acct_auto_close_mnt(&mnt->mnt); -		goto put_again; -	}  	if (unlikely(mnt->mnt.mnt_flags & MNT_DOOMED)) {  		rcu_read_unlock();  		unlock_mount_hash(); @@ -981,6 +993,8 @@ put_again:  	 * so mnt_get_writers() below is safe.  	 */  	WARN_ON(mnt_get_writers(mnt)); +	if (unlikely(mnt->mnt_pins.first)) +		mnt_pin_kill(mnt);  	fsnotify_vfsmount_delete(&mnt->mnt);  	dput(mnt->mnt.mnt_root);  	deactivate_super(mnt->mnt.mnt_sb); @@ -1008,25 +1022,15 @@ struct vfsmount *mntget(struct vfsmount *mnt)  }  EXPORT_SYMBOL(mntget); -void mnt_pin(struct vfsmount *mnt) -{ -	lock_mount_hash(); -	real_mount(mnt)->mnt_pinned++; -	unlock_mount_hash(); -} -EXPORT_SYMBOL(mnt_pin); - -void mnt_unpin(struct vfsmount *m) +struct vfsmount *mnt_clone_internal(struct path *path)  { -	struct mount *mnt = real_mount(m); -	lock_mount_hash(); -	if (mnt->mnt_pinned) { -		mnt_add_count(mnt, 1); -		mnt->mnt_pinned--; -	} -	unlock_mount_hash(); +	struct mount *p; +	p = clone_mnt(real_mount(path->mnt), path->dentry, CL_PRIVATE); +	if (IS_ERR(p)) +		return ERR_CAST(p); +	p->mnt.mnt_flags |= MNT_INTERNAL; +	return &p->mnt;  } -EXPORT_SYMBOL(mnt_unpin);  static inline void mangle(struct seq_file *m, const char *s)  { @@ -1213,6 +1217,11 @@ static void namespace_unlock(void)  	head.first->pprev = &head.first;  	INIT_HLIST_HEAD(&unmounted); +	/* undo decrements we'd done in umount_tree() */ +	hlist_for_each_entry(mnt, &head, mnt_hash) +		if (mnt->mnt_ex_mountpoint.mnt) +			mntget(mnt->mnt_ex_mountpoint.mnt); +  	up_write(&namespace_sem);  	synchronize_rcu(); @@ -1249,6 +1258,9 @@ void umount_tree(struct mount *mnt, int how)  		hlist_add_head(&p->mnt_hash, &tmp_list);  	} +	hlist_for_each_entry(p, &tmp_list, mnt_hash) +		list_del_init(&p->mnt_child); +  	if (how)  		propagate_umount(&tmp_list); @@ -1259,9 +1271,9 @@ void umount_tree(struct mount *mnt, int how)  		p->mnt_ns = NULL;  		if (how < 2)  			p->mnt.mnt_flags |= MNT_SYNC_UMOUNT; -		list_del_init(&p->mnt_child);  		if (mnt_has_parent(p)) {  			put_mountpoint(p->mnt_mp); +			mnt_add_count(p->mnt_parent, -1);  			/* move the reference to mountpoint into ->mnt_ex_mountpoint */  			p->mnt_ex_mountpoint.dentry = p->mnt_mountpoint;  			p->mnt_ex_mountpoint.mnt = &p->mnt_parent->mnt; @@ -1492,6 +1504,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,  			continue;  		for (s = r; s; s = next_mnt(s, r)) { +			struct mount *t = NULL;  			if (!(flag & CL_COPY_UNBINDABLE) &&  			    IS_MNT_UNBINDABLE(s)) {  				s = skip_mnt_tree(s); @@ -1513,7 +1526,14 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,  				goto out;  			lock_mount_hash();  			list_add_tail(&q->mnt_list, &res->mnt_list); -			attach_mnt(q, parent, p->mnt_mp); +			mnt_set_mountpoint(parent, p->mnt_mp, q); +			if (!list_empty(&parent->mnt_mounts)) { +				t = list_last_entry(&parent->mnt_mounts, +					struct mount, mnt_child); +				if (t->mnt_mp != p->mnt_mp) +					t = NULL; +			} +			attach_shadowed(q, parent, t);  			unlock_mount_hash();  		}  	} @@ -1896,9 +1916,6 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags)  	if (readonly_request == __mnt_is_readonly(mnt))  		return 0; -	if (mnt->mnt_flags & MNT_LOCK_READONLY) -		return -EPERM; -  	if (readonly_request)  		error = mnt_make_readonly(real_mount(mnt));  	else @@ -1924,6 +1941,33 @@ static int do_remount(struct path *path, int flags, int mnt_flags,  	if (path->dentry != path->mnt->mnt_root)  		return -EINVAL; +	/* Don't allow changing of locked mnt flags. +	 * +	 * No locks need to be held here while testing the various +	 * MNT_LOCK flags because those flags can never be cleared +	 * once they are set. +	 */ +	if ((mnt->mnt.mnt_flags & MNT_LOCK_READONLY) && +	    !(mnt_flags & MNT_READONLY)) { +		return -EPERM; +	} +	if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) && +	    !(mnt_flags & MNT_NODEV)) { +		return -EPERM; +	} +	if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) && +	    !(mnt_flags & MNT_NOSUID)) { +		return -EPERM; +	} +	if ((mnt->mnt.mnt_flags & MNT_LOCK_NOEXEC) && +	    !(mnt_flags & MNT_NOEXEC)) { +		return -EPERM; +	} +	if ((mnt->mnt.mnt_flags & MNT_LOCK_ATIME) && +	    ((mnt->mnt.mnt_flags & MNT_ATIME_MASK) != (mnt_flags & MNT_ATIME_MASK))) { +		return -EPERM; +	} +  	err = security_sb_remount(sb, data);  	if (err)  		return err; @@ -1937,7 +1981,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags,  		err = do_remount_sb(sb, flags, data, 0);  	if (!err) {  		lock_mount_hash(); -		mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK; +		mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;  		mnt->mnt.mnt_flags = mnt_flags;  		touch_mnt_namespace(mnt->mnt_ns);  		unlock_mount_hash(); @@ -2122,7 +2166,7 @@ static int do_new_mount(struct path *path, const char *fstype, int flags,  		 */  		if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) {  			flags |= MS_NODEV; -			mnt_flags |= MNT_NODEV; +			mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV;  		}  	} @@ -2436,6 +2480,14 @@ long do_mount(const char *dev_name, const char *dir_name,  	if (flags & MS_RDONLY)  		mnt_flags |= MNT_READONLY; +	/* The default atime for remount is preservation */ +	if ((flags & MS_REMOUNT) && +	    ((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME | +		       MS_STRICTATIME)) == 0)) { +		mnt_flags &= ~MNT_ATIME_MASK; +		mnt_flags |= path.mnt->mnt_flags & MNT_ATIME_MASK; +	} +  	flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |  		   MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |  		   MS_STRICTATIME); @@ -2972,13 +3024,13 @@ static void *mntns_get(struct task_struct *task)  	struct mnt_namespace *ns = NULL;  	struct nsproxy *nsproxy; -	rcu_read_lock(); -	nsproxy = task_nsproxy(task); +	task_lock(task); +	nsproxy = task->nsproxy;  	if (nsproxy) {  		ns = nsproxy->mnt_ns;  		get_mnt_ns(ns);  	} -	rcu_read_unlock(); +	task_unlock(task);  	return ns;  }  |