650 lines · 16.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  linux/fs/pnode.c * * (C) Copyright IBM Corporation 2005. *	Author : Ram Pai (linuxram@us.ibm.com) */#include <linux/mnt_namespace.h>#include <linux/mount.h>#include <linux/fs.h>#include <linux/nsproxy.h>#include <uapi/linux/mount.h>#include "internal.h"#include "pnode.h" /* return the next shared peer mount of @p */static inline struct mount *next_peer(struct mount *p){	return list_entry(p->mnt_share.next, struct mount, mnt_share);} static inline struct mount *first_slave(struct mount *p){	return hlist_entry(p->mnt_slave_list.first, struct mount, mnt_slave);} static inline struct mount *next_slave(struct mount *p){	return hlist_entry(p->mnt_slave.next, struct mount, mnt_slave);} static struct mount *get_peer_under_root(struct mount *mnt,					 struct mnt_namespace *ns,					 const struct path *root){	struct mount *m = mnt; 	do {		/* Check the namespace first for optimization */		if (m->mnt_ns == ns && is_path_reachable(m, m->mnt.mnt_root, root))			return m; 		m = next_peer(m);	} while (m != mnt); 	return NULL;} /* * Get ID of closest dominating peer group having a representative * under the given root. * * Caller must hold namespace_sem */int get_dominating_id(struct mount *mnt, const struct path *root){	struct mount *m; 	for (m = mnt->mnt_master; m != NULL; m = m->mnt_master) {		struct mount *d = get_peer_under_root(m, mnt->mnt_ns, root);		if (d)			return d->mnt_group_id;	} 	return 0;} static inline bool will_be_unmounted(struct mount *m){	return m->mnt.mnt_flags & MNT_UMOUNT;} static struct mount *propagation_source(struct mount *mnt){	do {		struct mount *m;		for (m = next_peer(mnt); m != mnt; m = next_peer(m)) {			if (!will_be_unmounted(m))				return m;		}		mnt = mnt->mnt_master;	} while (mnt && will_be_unmounted(mnt));	return mnt;} static void transfer_propagation(struct mount *mnt, struct mount *to){	struct hlist_node *p = NULL, *n;	struct mount *m; 	hlist_for_each_entry_safe(m, n, &mnt->mnt_slave_list, mnt_slave) {		m->mnt_master = to;		if (!to)			hlist_del_init(&m->mnt_slave);		else			p = &m->mnt_slave;	}	if (p)		hlist_splice_init(&mnt->mnt_slave_list, p, &to->mnt_slave_list);} /* * EXCL[namespace_sem] */void change_mnt_propagation(struct mount *mnt, int type){	struct mount *m = mnt->mnt_master; 	if (type == MS_SHARED) {		set_mnt_shared(mnt);		return;	}	if (IS_MNT_SHARED(mnt)) {		if (type == MS_SLAVE || !hlist_empty(&mnt->mnt_slave_list))			m = propagation_source(mnt);		if (list_empty(&mnt->mnt_share)) {			mnt_release_group_id(mnt);		} else {			list_del_init(&mnt->mnt_share);			mnt->mnt_group_id = 0;		}		CLEAR_MNT_SHARED(mnt);		transfer_propagation(mnt, m);	}	hlist_del_init(&mnt->mnt_slave);	if (type == MS_SLAVE) {		mnt->mnt_master = m;		if (m)			hlist_add_head(&mnt->mnt_slave, &m->mnt_slave_list);	} else {		mnt->mnt_master = NULL;		if (type == MS_UNBINDABLE)			mnt->mnt_t_flags |= T_UNBINDABLE;		else			mnt->mnt_t_flags &= ~T_UNBINDABLE;	}} static struct mount *__propagation_next(struct mount *m,					 struct mount *origin){	while (1) {		struct mount *master = m->mnt_master; 		if (master == origin->mnt_master) {			struct mount *next = next_peer(m);			return (next == origin) ? NULL : next;		} else if (m->mnt_slave.next)			return next_slave(m); 		/* back at master */		m = master;	}} /* * get the next mount in the propagation tree. * @m: the mount seen last * @origin: the original mount from where the tree walk initiated * * Note that peer groups form contiguous segments of slave lists. * We rely on that in get_source() to be able to find out if * vfsmount found while iterating with propagation_next() is * a peer of one we'd found earlier. */static struct mount *propagation_next(struct mount *m,					 struct mount *origin){	/* are there any slaves of this mount? */	if (!IS_MNT_NEW(m) && !hlist_empty(&m->mnt_slave_list))		return first_slave(m); 	return __propagation_next(m, origin);} static struct mount *skip_propagation_subtree(struct mount *m,						struct mount *origin){	/*	 * Advance m past everything that gets propagation from it.	 */	struct mount *p = __propagation_next(m, origin); 	while (p && peers(m, p))		p = __propagation_next(p, origin); 	return p;} static struct mount *next_group(struct mount *m, struct mount *origin){	while (1) {		while (1) {			struct mount *next;			if (!IS_MNT_NEW(m) && !hlist_empty(&m->mnt_slave_list))				return first_slave(m);			next = next_peer(m);			if (m->mnt_group_id == origin->mnt_group_id) {				if (next == origin)					return NULL;			} else if (m->mnt_slave.next != &next->mnt_slave)				break;			m = next;		}		/* m is the last peer */		while (1) {			struct mount *master = m->mnt_master;			if (m->mnt_slave.next)				return next_slave(m);			m = next_peer(master);			if (master->mnt_group_id == origin->mnt_group_id)				break;			if (master->mnt_slave.next == &m->mnt_slave)				break;			m = master;		}		if (m == origin)			return NULL;	}} static bool need_secondary(struct mount *m, struct mountpoint *dest_mp){	/* skip ones added by this propagate_mnt() */	if (IS_MNT_NEW(m))		return false;	/* skip if mountpoint isn't visible in m */	if (!is_subdir(dest_mp->m_dentry, m->mnt.mnt_root))		return false;	/* skip if m is in the anon_ns */	if (is_anon_ns(m->mnt_ns))		return false;	return true;} static struct mount *find_master(struct mount *m,				struct mount *last_copy,				struct mount *original){	struct mount *p; 	// ascend until there's a copy for something with the same master	for (;;) {		p = m->mnt_master;		if (!p || IS_MNT_MARKED(p))			break;		m = p;	}	while (!peers(last_copy, original)) {		struct mount *parent = last_copy->mnt_parent;		if (parent->mnt_master == p) {			if (!peers(parent, m))				last_copy = last_copy->mnt_master;			break;		}		last_copy = last_copy->mnt_master;	}	return last_copy;} /** * propagate_mnt() - create secondary copies for tree attachment * @dest_mnt:    destination mount. * @dest_mp:     destination mountpoint. * @source_mnt:  source mount. * @tree_list:   list of secondaries to be attached. * * Create secondary copies for attaching a tree with root @source_mnt * at mount @dest_mnt with mountpoint @dest_mp.  Link all new mounts * into a propagation graph.  Set mountpoints for all secondaries, * link their roots into @tree_list via ->mnt_hash. */int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,		  struct mount *source_mnt, struct hlist_head *tree_list){	struct mount *m, *n, *copy, *this;	int err = 0, type; 	if (dest_mnt->mnt_master)		SET_MNT_MARK(dest_mnt->mnt_master); 	/* iterate over peer groups, depth first */	for (m = dest_mnt; m && !err; m = next_group(m, dest_mnt)) {		if (m == dest_mnt) { // have one for dest_mnt itself			copy = source_mnt;			type = CL_MAKE_SHARED;			n = next_peer(m);			if (n == m)				continue;		} else {			type = CL_SLAVE;			/* beginning of peer group among the slaves? */			if (IS_MNT_SHARED(m))				type |= CL_MAKE_SHARED;			n = m;		}		do {			if (!need_secondary(n, dest_mp))				continue;			if (type & CL_SLAVE) // first in this peer group				copy = find_master(n, copy, source_mnt);			this = copy_tree(copy, copy->mnt.mnt_root, type);			if (IS_ERR(this)) {				err = PTR_ERR(this);				break;			}			read_seqlock_excl(&mount_lock);			mnt_set_mountpoint(n, dest_mp, this);			read_sequnlock_excl(&mount_lock);			if (n->mnt_master)				SET_MNT_MARK(n->mnt_master);			copy = this;			hlist_add_head(&this->mnt_hash, tree_list);			err = count_mounts(n->mnt_ns, this);			if (err)				break;			type = CL_MAKE_SHARED;		} while ((n = next_peer(n)) != m);	} 	hlist_for_each_entry(n, tree_list, mnt_hash) {		m = n->mnt_parent;		if (m->mnt_master)			CLEAR_MNT_MARK(m->mnt_master);	}	if (dest_mnt->mnt_master)		CLEAR_MNT_MARK(dest_mnt->mnt_master);	return err;} /* * return true if the refcount is greater than count */static inline int do_refcount_check(struct mount *mnt, int count){	return mnt_get_count(mnt) > count;} /** * propagation_would_overmount - check whether propagation from @from *                               would overmount @to * @from: shared mount * @to:   mount to check * @mp:   future mountpoint of @to on @from * * If @from propagates mounts to @to, @from and @to must either be peers * or one of the masters in the hierarchy of masters of @to must be a * peer of @from. * * If the root of the @to mount is equal to the future mountpoint @mp of * the @to mount on @from then @to will be overmounted by whatever is * propagated to it. * * Context: This function expects namespace_lock() to be held and that *          @mp is stable. * Return: If @from overmounts @to, true is returned, false if not. */bool propagation_would_overmount(const struct mount *from,				 const struct mount *to,				 const struct mountpoint *mp){	if (!IS_MNT_SHARED(from))		return false; 	if (to->mnt.mnt_root != mp->m_dentry)		return false; 	for (const struct mount *m = to; m; m = m->mnt_master) {		if (peers(from, m))			return true;	} 	return false;} /* * check if the mount 'mnt' can be unmounted successfully. * @mnt: the mount to be checked for unmount * NOTE: unmounting 'mnt' would naturally propagate to all * other mounts its parent propagates to. * Check if any of these mounts that **do not have submounts** * have more references than 'refcnt'. If so return busy. * * vfsmount lock must be held for write */int propagate_mount_busy(struct mount *mnt, int refcnt){	struct mount *parent = mnt->mnt_parent; 	/*	 * quickly check if the current mount can be unmounted.	 * If not, we don't have to go checking for all other	 * mounts	 */	if (!list_empty(&mnt->mnt_mounts) || do_refcount_check(mnt, refcnt))		return 1; 	if (mnt == parent)		return 0; 	for (struct mount *m = propagation_next(parent, parent); m;	     		m = propagation_next(m, parent)) {		struct list_head *head;		struct mount *child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint); 		if (!child)			continue; 		head = &child->mnt_mounts;		if (!list_empty(head)) {			/*			 * a mount that covers child completely wouldn't prevent			 * it being pulled out; any other would.			 */			if (!list_is_singular(head) || !child->overmount)				continue;		}		if (do_refcount_check(child, 1))			return 1;	}	return 0;} /* * Clear MNT_LOCKED when it can be shown to be safe. * * mount_lock lock must be held for write */void propagate_mount_unlock(struct mount *mnt){	struct mount *parent = mnt->mnt_parent;	struct mount *m, *child; 	BUG_ON(parent == mnt); 	for (m = propagation_next(parent, parent); m;			m = propagation_next(m, parent)) {		child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint);		if (child)			child->mnt.mnt_flags &= ~MNT_LOCKED;	}} static inline bool is_candidate(struct mount *m){	return m->mnt_t_flags & T_UMOUNT_CANDIDATE;} static void umount_one(struct mount *m, struct list_head *to_umount){	m->mnt.mnt_flags |= MNT_UMOUNT;	list_del_init(&m->mnt_child);	move_from_ns(m);	list_add_tail(&m->mnt_list, to_umount);} static void remove_from_candidate_list(struct mount *m){	m->mnt_t_flags &= ~(T_MARKED | T_UMOUNT_CANDIDATE);	list_del_init(&m->mnt_list);} static void gather_candidates(struct list_head *set,			      struct list_head *candidates){	struct mount *m, *p, *q; 	list_for_each_entry(m, set, mnt_list) {		if (is_candidate(m))			continue;		m->mnt_t_flags |= T_UMOUNT_CANDIDATE;		p = m->mnt_parent;		q = propagation_next(p, p);		while (q) {			struct mount *child = __lookup_mnt(&q->mnt,							   m->mnt_mountpoint);			if (child) {				/*				 * We might've already run into this one.  That				 * must've happened on earlier iteration of the				 * outer loop; in that case we can skip those				 * parents that get propagation from q - there				 * will be nothing new on those as well.				 */				if (is_candidate(child)) {					q = skip_propagation_subtree(q, p);					continue;				}				child->mnt_t_flags |= T_UMOUNT_CANDIDATE;				if (!will_be_unmounted(child))					list_add(&child->mnt_list, candidates);			}			q = propagation_next(q, p);		}	}	list_for_each_entry(m, set, mnt_list)		m->mnt_t_flags &= ~T_UMOUNT_CANDIDATE;} /*

Lines 1–500 of 650.