Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 79c1cb7a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6:
  [patch 7/7] vfs: mountinfo: show dominating group id
  [patch 6/7] vfs: mountinfo: add /proc/<pid>/mountinfo
  [patch 5/7] vfs: mountinfo: allow using process root
  [patch 4/7] vfs: mountinfo: add mount peer group ID
  [patch 3/7] vfs: mountinfo: add mount ID
  [patch 2/7] vfs: mountinfo: add seq_file_root()
  [patch 1/7] vfs: mountinfo: add dentry_path()
  [PATCH] remove unused label in xattr.c (noise from ro-bind)
parents b0d19a37 97e7e0f7
Loading
Loading
Loading
Loading
+38 −0
Original line number Original line Diff line number Diff line
@@ -43,6 +43,7 @@ Table of Contents
  2.13	/proc/<pid>/oom_score - Display current oom-killer score
  2.13	/proc/<pid>/oom_score - Display current oom-killer score
  2.14	/proc/<pid>/io - Display the IO accounting fields
  2.14	/proc/<pid>/io - Display the IO accounting fields
  2.15	/proc/<pid>/coredump_filter - Core dump filtering settings
  2.15	/proc/<pid>/coredump_filter - Core dump filtering settings
  2.16	/proc/<pid>/mountinfo - Information about mounts


------------------------------------------------------------------------------
------------------------------------------------------------------------------
Preface
Preface
@@ -2348,4 +2349,41 @@ For example:
  $ echo 0x7 > /proc/self/coredump_filter
  $ echo 0x7 > /proc/self/coredump_filter
  $ ./some_program
  $ ./some_program


2.16	/proc/<pid>/mountinfo - Information about mounts
--------------------------------------------------------

This file contains lines of the form:

36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3)   (4)   (5)      (6)      (7)   (8) (9)   (10)         (11)

(1) mount ID:  unique identifier of the mount (may be reused after umount)
(2) parent ID:  ID of parent (or of self for the top of the mount tree)
(3) major:minor:  value of st_dev for files on filesystem
(4) root:  root of the mount within the filesystem
(5) mount point:  mount point relative to the process's root
(6) mount options:  per mount options
(7) optional fields:  zero or more fields of the form "tag[:value]"
(8) separator:  marks the end of the optional fields
(9) filesystem type:  name of filesystem of the form "type[.subtype]"
(10) mount source:  filesystem specific information or "none"
(11) super options:  per super block options

Parsers should ignore all unrecognised optional fields.  Currently the
possible optional fields are:

shared:X  mount is shared in peer group X
master:X  mount is slave to peer group X
propagate_from:X  mount is slave and receives propagation from peer group X (*)
unbindable  mount is unbindable

(*) X is the closest dominant peer group under the process's root.  If
X is the immediate master of the mount, or if there's no dominant peer
group under the same root, then only the "master:X" field is present
and not the "propagate_from:X" field.

For more information on mount propagation see:

  Documentation/filesystems/sharedsubtree.txt

------------------------------------------------------------------------------
------------------------------------------------------------------------------
+82 −32
Original line number Original line Diff line number Diff line
@@ -1746,12 +1746,21 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
	goto shouldnt_be_hashed;
	goto shouldnt_be_hashed;
}
}


static int prepend(char **buffer, int *buflen, const char *str,
			  int namelen)
{
	*buflen -= namelen;
	if (*buflen < 0)
		return -ENAMETOOLONG;
	*buffer -= namelen;
	memcpy(*buffer, str, namelen);
	return 0;
}

/**
/**
 * d_path - return the path of a dentry
 * d_path - return the path of a dentry
 * @dentry: dentry to report
 * @path: the dentry/vfsmount to report
 * @vfsmnt: vfsmnt to which the dentry belongs
 * @root: root vfsmnt/dentry (may be modified by this function)
 * @root: root dentry
 * @rootmnt: vfsmnt to which the root dentry belongs
 * @buffer: buffer to return value in
 * @buffer: buffer to return value in
 * @buflen: buffer length
 * @buflen: buffer length
 *
 *
@@ -1761,23 +1770,22 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
 * Returns the buffer or an error code if the path was too long.
 * Returns the buffer or an error code if the path was too long.
 *
 *
 * "buflen" should be positive. Caller holds the dcache_lock.
 * "buflen" should be positive. Caller holds the dcache_lock.
 *
 * If path is not reachable from the supplied root, then the value of
 * root is changed (without modifying refcounts).
 */
 */
static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
char *__d_path(const struct path *path, struct path *root,
		       struct path *root, char *buffer, int buflen)
	       char *buffer, int buflen)
{
{
	struct dentry *dentry = path->dentry;
	struct vfsmount *vfsmnt = path->mnt;
	char * end = buffer+buflen;
	char * end = buffer+buflen;
	char * retval;
	char * retval;
	int namelen;


	prepend(&end, &buflen, "\0", 1);
	*--end = '\0';
	if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
	buflen--;
		(prepend(&end, &buflen, " (deleted)", 10) != 0))
	if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
		buflen -= 10;
		end -= 10;
		if (buflen < 0)
			goto Elong;
			goto Elong;
		memcpy(end, " (deleted)", 10);
	}


	if (buflen < 1)
	if (buflen < 1)
		goto Elong;
		goto Elong;
@@ -1804,13 +1812,10 @@ static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
		}
		}
		parent = dentry->d_parent;
		parent = dentry->d_parent;
		prefetch(parent);
		prefetch(parent);
		namelen = dentry->d_name.len;
		if ((prepend(&end, &buflen, dentry->d_name.name,
		buflen -= namelen + 1;
				dentry->d_name.len) != 0) ||
		if (buflen < 0)
		    (prepend(&end, &buflen, "/", 1) != 0))
			goto Elong;
			goto Elong;
		end -= namelen;
		memcpy(end, dentry->d_name.name, namelen);
		*--end = '/';
		retval = end;
		retval = end;
		dentry = parent;
		dentry = parent;
	}
	}
@@ -1818,12 +1823,12 @@ static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
	return retval;
	return retval;


global_root:
global_root:
	namelen = dentry->d_name.len;
	retval += 1;	/* hit the slash */
	buflen -= namelen;
	if (prepend(&retval, &buflen, dentry->d_name.name,
	if (buflen < 0)
		    dentry->d_name.len) != 0)
		goto Elong;
		goto Elong;
	retval -= namelen-1;	/* hit the slash */
	root->mnt = vfsmnt;
	memcpy(retval, dentry->d_name.name, namelen);
	root->dentry = dentry;
	return retval;
	return retval;
Elong:
Elong:
	return ERR_PTR(-ENAMETOOLONG);
	return ERR_PTR(-ENAMETOOLONG);
@@ -1846,6 +1851,7 @@ char *d_path(struct path *path, char *buf, int buflen)
{
{
	char *res;
	char *res;
	struct path root;
	struct path root;
	struct path tmp;


	/*
	/*
	 * We have various synthetic filesystems that never get mounted.  On
	 * We have various synthetic filesystems that never get mounted.  On
@@ -1859,10 +1865,11 @@ char *d_path(struct path *path, char *buf, int buflen)


	read_lock(&current->fs->lock);
	read_lock(&current->fs->lock);
	root = current->fs->root;
	root = current->fs->root;
	path_get(&current->fs->root);
	path_get(&root);
	read_unlock(&current->fs->lock);
	read_unlock(&current->fs->lock);
	spin_lock(&dcache_lock);
	spin_lock(&dcache_lock);
	res = __d_path(path->dentry, path->mnt, &root, buf, buflen);
	tmp = root;
	res = __d_path(path, &tmp, buf, buflen);
	spin_unlock(&dcache_lock);
	spin_unlock(&dcache_lock);
	path_put(&root);
	path_put(&root);
	return res;
	return res;
@@ -1889,6 +1896,48 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen,
	return memcpy(buffer, temp, sz);
	return memcpy(buffer, temp, sz);
}
}


/*
 * Write full pathname from the root of the filesystem into the buffer.
 */
char *dentry_path(struct dentry *dentry, char *buf, int buflen)
{
	char *end = buf + buflen;
	char *retval;

	spin_lock(&dcache_lock);
	prepend(&end, &buflen, "\0", 1);
	if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
		(prepend(&end, &buflen, "//deleted", 9) != 0))
			goto Elong;
	if (buflen < 1)
		goto Elong;
	/* Get '/' right */
	retval = end-1;
	*retval = '/';

	for (;;) {
		struct dentry *parent;
		if (IS_ROOT(dentry))
			break;

		parent = dentry->d_parent;
		prefetch(parent);

		if ((prepend(&end, &buflen, dentry->d_name.name,
				dentry->d_name.len) != 0) ||
		    (prepend(&end, &buflen, "/", 1) != 0))
			goto Elong;

		retval = end;
		dentry = parent;
	}
	spin_unlock(&dcache_lock);
	return retval;
Elong:
	spin_unlock(&dcache_lock);
	return ERR_PTR(-ENAMETOOLONG);
}

/*
/*
 * NOTE! The user-level library version returns a
 * NOTE! The user-level library version returns a
 * character pointer. The kernel system call just
 * character pointer. The kernel system call just
@@ -1918,9 +1967,9 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size)


	read_lock(&current->fs->lock);
	read_lock(&current->fs->lock);
	pwd = current->fs->pwd;
	pwd = current->fs->pwd;
	path_get(&current->fs->pwd);
	path_get(&pwd);
	root = current->fs->root;
	root = current->fs->root;
	path_get(&current->fs->root);
	path_get(&root);
	read_unlock(&current->fs->lock);
	read_unlock(&current->fs->lock);


	error = -ENOENT;
	error = -ENOENT;
@@ -1928,9 +1977,10 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
	spin_lock(&dcache_lock);
	spin_lock(&dcache_lock);
	if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) {
	if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) {
		unsigned long len;
		unsigned long len;
		struct path tmp = root;
		char * cwd;
		char * cwd;


		cwd = __d_path(pwd.dentry, pwd.mnt, &root, page, PAGE_SIZE);
		cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE);
		spin_unlock(&dcache_lock);
		spin_unlock(&dcache_lock);


		error = PTR_ERR(cwd);
		error = PTR_ERR(cwd);
+233 −32
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/mount.h>
#include <linux/mount.h>
#include <linux/ramfs.h>
#include <linux/ramfs.h>
#include <linux/log2.h>
#include <linux/log2.h>
#include <linux/idr.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/unistd.h>
#include "pnode.h"
#include "pnode.h"
@@ -39,6 +40,8 @@
__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);


static int event;
static int event;
static DEFINE_IDA(mnt_id_ida);
static DEFINE_IDA(mnt_group_ida);


static struct list_head *mount_hashtable __read_mostly;
static struct list_head *mount_hashtable __read_mostly;
static struct kmem_cache *mnt_cache __read_mostly;
static struct kmem_cache *mnt_cache __read_mostly;
@@ -58,10 +61,63 @@ static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)


#define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16)
#define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16)


/* allocation is serialized by namespace_sem */
static int mnt_alloc_id(struct vfsmount *mnt)
{
	int res;

retry:
	ida_pre_get(&mnt_id_ida, GFP_KERNEL);
	spin_lock(&vfsmount_lock);
	res = ida_get_new(&mnt_id_ida, &mnt->mnt_id);
	spin_unlock(&vfsmount_lock);
	if (res == -EAGAIN)
		goto retry;

	return res;
}

static void mnt_free_id(struct vfsmount *mnt)
{
	spin_lock(&vfsmount_lock);
	ida_remove(&mnt_id_ida, mnt->mnt_id);
	spin_unlock(&vfsmount_lock);
}

/*
 * Allocate a new peer group ID
 *
 * mnt_group_ida is protected by namespace_sem
 */
static int mnt_alloc_group_id(struct vfsmount *mnt)
{
	if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
		return -ENOMEM;

	return ida_get_new_above(&mnt_group_ida, 1, &mnt->mnt_group_id);
}

/*
 * Release a peer group ID
 */
void mnt_release_group_id(struct vfsmount *mnt)
{
	ida_remove(&mnt_group_ida, mnt->mnt_group_id);
	mnt->mnt_group_id = 0;
}

struct vfsmount *alloc_vfsmnt(const char *name)
struct vfsmount *alloc_vfsmnt(const char *name)
{
{
	struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
	struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
	if (mnt) {
	if (mnt) {
		int err;

		err = mnt_alloc_id(mnt);
		if (err) {
			kmem_cache_free(mnt_cache, mnt);
			return NULL;
		}

		atomic_set(&mnt->mnt_count, 1);
		atomic_set(&mnt->mnt_count, 1);
		INIT_LIST_HEAD(&mnt->mnt_hash);
		INIT_LIST_HEAD(&mnt->mnt_hash);
		INIT_LIST_HEAD(&mnt->mnt_child);
		INIT_LIST_HEAD(&mnt->mnt_child);
@@ -353,6 +409,7 @@ EXPORT_SYMBOL(simple_set_mnt);
void free_vfsmnt(struct vfsmount *mnt)
void free_vfsmnt(struct vfsmount *mnt)
{
{
	kfree(mnt->mnt_devname);
	kfree(mnt->mnt_devname);
	mnt_free_id(mnt);
	kmem_cache_free(mnt_cache, mnt);
	kmem_cache_free(mnt_cache, mnt);
}
}


@@ -499,6 +556,17 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,
	struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname);
	struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname);


	if (mnt) {
	if (mnt) {
		if (flag & (CL_SLAVE | CL_PRIVATE))
			mnt->mnt_group_id = 0; /* not a peer of original */
		else
			mnt->mnt_group_id = old->mnt_group_id;

		if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) {
			int err = mnt_alloc_group_id(mnt);
			if (err)
				goto out_free;
		}

		mnt->mnt_flags = old->mnt_flags;
		mnt->mnt_flags = old->mnt_flags;
		atomic_inc(&sb->s_active);
		atomic_inc(&sb->s_active);
		mnt->mnt_sb = sb;
		mnt->mnt_sb = sb;
@@ -528,6 +596,10 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,
		}
		}
	}
	}
	return mnt;
	return mnt;

 out_free:
	free_vfsmnt(mnt);
	return NULL;
}
}


static inline void __mntput(struct vfsmount *mnt)
static inline void __mntput(struct vfsmount *mnt)
@@ -652,20 +724,21 @@ void save_mount_options(struct super_block *sb, char *options)
}
}
EXPORT_SYMBOL(save_mount_options);
EXPORT_SYMBOL(save_mount_options);


#ifdef CONFIG_PROC_FS
/* iterator */
/* iterator */
static void *m_start(struct seq_file *m, loff_t *pos)
static void *m_start(struct seq_file *m, loff_t *pos)
{
{
	struct mnt_namespace *n = m->private;
	struct proc_mounts *p = m->private;


	down_read(&namespace_sem);
	down_read(&namespace_sem);
	return seq_list_start(&n->list, *pos);
	return seq_list_start(&p->ns->list, *pos);
}
}


static void *m_next(struct seq_file *m, void *v, loff_t *pos)
static void *m_next(struct seq_file *m, void *v, loff_t *pos)
{
{
	struct mnt_namespace *n = m->private;
	struct proc_mounts *p = m->private;


	return seq_list_next(v, &n->list, pos);
	return seq_list_next(v, &p->ns->list, pos);
}
}


static void m_stop(struct seq_file *m, void *v)
static void m_stop(struct seq_file *m, void *v)
@@ -673,20 +746,30 @@ static void m_stop(struct seq_file *m, void *v)
	up_read(&namespace_sem);
	up_read(&namespace_sem);
}
}


static int show_vfsmnt(struct seq_file *m, void *v)
struct proc_fs_info {
{
	struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list);
	int err = 0;
	static struct proc_fs_info {
	int flag;
	int flag;
		char *str;
	const char *str;
	} fs_info[] = {
};

static void show_sb_opts(struct seq_file *m, struct super_block *sb)
{
	static const struct proc_fs_info fs_info[] = {
		{ MS_SYNCHRONOUS, ",sync" },
		{ MS_SYNCHRONOUS, ",sync" },
		{ MS_DIRSYNC, ",dirsync" },
		{ MS_DIRSYNC, ",dirsync" },
		{ MS_MANDLOCK, ",mand" },
		{ MS_MANDLOCK, ",mand" },
		{ 0, NULL }
		{ 0, NULL }
	};
	};
	static struct proc_fs_info mnt_info[] = {
	const struct proc_fs_info *fs_infop;

	for (fs_infop = fs_info; fs_infop->flag; fs_infop++) {
		if (sb->s_flags & fs_infop->flag)
			seq_puts(m, fs_infop->str);
	}
}

static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
{
	static const struct proc_fs_info mnt_info[] = {
		{ MNT_NOSUID, ",nosuid" },
		{ MNT_NOSUID, ",nosuid" },
		{ MNT_NODEV, ",nodev" },
		{ MNT_NODEV, ",nodev" },
		{ MNT_NOEXEC, ",noexec" },
		{ MNT_NOEXEC, ",noexec" },
@@ -695,40 +778,108 @@ static int show_vfsmnt(struct seq_file *m, void *v)
		{ MNT_RELATIME, ",relatime" },
		{ MNT_RELATIME, ",relatime" },
		{ 0, NULL }
		{ 0, NULL }
	};
	};
	struct proc_fs_info *fs_infop;
	const struct proc_fs_info *fs_infop;

	for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) {
		if (mnt->mnt_flags & fs_infop->flag)
			seq_puts(m, fs_infop->str);
	}
}

static void show_type(struct seq_file *m, struct super_block *sb)
{
	mangle(m, sb->s_type->name);
	if (sb->s_subtype && sb->s_subtype[0]) {
		seq_putc(m, '.');
		mangle(m, sb->s_subtype);
	}
}

static int show_vfsmnt(struct seq_file *m, void *v)
{
	struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list);
	int err = 0;
	struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
	struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };


	mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
	mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
	seq_putc(m, ' ');
	seq_putc(m, ' ');
	seq_path(m, &mnt_path, " \t\n\\");
	seq_path(m, &mnt_path, " \t\n\\");
	seq_putc(m, ' ');
	seq_putc(m, ' ');
	mangle(m, mnt->mnt_sb->s_type->name);
	show_type(m, mnt->mnt_sb);
	if (mnt->mnt_sb->s_subtype && mnt->mnt_sb->s_subtype[0]) {
		seq_putc(m, '.');
		mangle(m, mnt->mnt_sb->s_subtype);
	}
	seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw");
	seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw");
	for (fs_infop = fs_info; fs_infop->flag; fs_infop++) {
	show_sb_opts(m, mnt->mnt_sb);
		if (mnt->mnt_sb->s_flags & fs_infop->flag)
	show_mnt_opts(m, mnt);
			seq_puts(m, fs_infop->str);
	}
	for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) {
		if (mnt->mnt_flags & fs_infop->flag)
			seq_puts(m, fs_infop->str);
	}
	if (mnt->mnt_sb->s_op->show_options)
	if (mnt->mnt_sb->s_op->show_options)
		err = mnt->mnt_sb->s_op->show_options(m, mnt);
		err = mnt->mnt_sb->s_op->show_options(m, mnt);
	seq_puts(m, " 0 0\n");
	seq_puts(m, " 0 0\n");
	return err;
	return err;
}
}


struct seq_operations mounts_op = {
const struct seq_operations mounts_op = {
	.start	= m_start,
	.start	= m_start,
	.next	= m_next,
	.next	= m_next,
	.stop	= m_stop,
	.stop	= m_stop,
	.show	= show_vfsmnt
	.show	= show_vfsmnt
};
};


static int show_mountinfo(struct seq_file *m, void *v)
{
	struct proc_mounts *p = m->private;
	struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list);
	struct super_block *sb = mnt->mnt_sb;
	struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
	struct path root = p->root;
	int err = 0;

	seq_printf(m, "%i %i %u:%u ", mnt->mnt_id, mnt->mnt_parent->mnt_id,
		   MAJOR(sb->s_dev), MINOR(sb->s_dev));
	seq_dentry(m, mnt->mnt_root, " \t\n\\");
	seq_putc(m, ' ');
	seq_path_root(m, &mnt_path, &root, " \t\n\\");
	if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) {
		/*
		 * Mountpoint is outside root, discard that one.  Ugly,
		 * but less so than trying to do that in iterator in a
		 * race-free way (due to renames).
		 */
		return SEQ_SKIP;
	}
	seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
	show_mnt_opts(m, mnt);

	/* Tagged fields ("foo:X" or "bar") */
	if (IS_MNT_SHARED(mnt))
		seq_printf(m, " shared:%i", mnt->mnt_group_id);
	if (IS_MNT_SLAVE(mnt)) {
		int master = mnt->mnt_master->mnt_group_id;
		int dom = get_dominating_id(mnt, &p->root);
		seq_printf(m, " master:%i", master);
		if (dom && dom != master)
			seq_printf(m, " propagate_from:%i", dom);
	}
	if (IS_MNT_UNBINDABLE(mnt))
		seq_puts(m, " unbindable");

	/* Filesystem specific data */
	seq_puts(m, " - ");
	show_type(m, sb);
	seq_putc(m, ' ');
	mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
	seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw");
	show_sb_opts(m, sb);
	if (sb->s_op->show_options)
		err = sb->s_op->show_options(m, mnt);
	seq_putc(m, '\n');
	return err;
}

const struct seq_operations mountinfo_op = {
	.start	= m_start,
	.next	= m_next,
	.stop	= m_stop,
	.show	= show_mountinfo,
};

static int show_vfsstat(struct seq_file *m, void *v)
static int show_vfsstat(struct seq_file *m, void *v)
{
{
	struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list);
	struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list);
@@ -749,7 +900,7 @@ static int show_vfsstat(struct seq_file *m, void *v)


	/* file system type */
	/* file system type */
	seq_puts(m, "with fstype ");
	seq_puts(m, "with fstype ");
	mangle(m, mnt->mnt_sb->s_type->name);
	show_type(m, mnt->mnt_sb);


	/* optional statistics */
	/* optional statistics */
	if (mnt->mnt_sb->s_op->show_stats) {
	if (mnt->mnt_sb->s_op->show_stats) {
@@ -761,12 +912,13 @@ static int show_vfsstat(struct seq_file *m, void *v)
	return err;
	return err;
}
}


struct seq_operations mountstats_op = {
const struct seq_operations mountstats_op = {
	.start	= m_start,
	.start	= m_start,
	.next	= m_next,
	.next	= m_next,
	.stop	= m_stop,
	.stop	= m_stop,
	.show	= show_vfsstat,
	.show	= show_vfsstat,
};
};
#endif  /* CONFIG_PROC_FS */


/**
/**
 * may_umount_tree - check if a mount tree is busy
 * may_umount_tree - check if a mount tree is busy
@@ -1108,6 +1260,33 @@ void drop_collected_mounts(struct vfsmount *mnt)
	release_mounts(&umount_list);
	release_mounts(&umount_list);
}
}


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
 *  @source_mnt : mount tree to be attached
 *  @nd         : place the mount tree @source_mnt is attached
 *  @nd         : place the mount tree @source_mnt is attached
@@ -1178,9 +1357,16 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt,
	struct vfsmount *dest_mnt = path->mnt;
	struct vfsmount *dest_mnt = path->mnt;
	struct dentry *dest_dentry = path->dentry;
	struct dentry *dest_dentry = path->dentry;
	struct vfsmount *child, *p;
	struct vfsmount *child, *p;
	int err;


	if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list))
	if (IS_MNT_SHARED(dest_mnt)) {
		return -EINVAL;
		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)) {
	if (IS_MNT_SHARED(dest_mnt)) {
		for (p = source_mnt; p; p = next_mnt(p, source_mnt))
		for (p = source_mnt; p; p = next_mnt(p, source_mnt))
@@ -1203,6 +1389,12 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt,
	}
	}
	spin_unlock(&vfsmount_lock);
	spin_unlock(&vfsmount_lock);
	return 0;
	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)
static int graft_tree(struct vfsmount *mnt, struct path *path)
@@ -1243,6 +1435,7 @@ static noinline int do_change_type(struct nameidata *nd, int flag)
	struct vfsmount *m, *mnt = nd->path.mnt;
	struct vfsmount *m, *mnt = nd->path.mnt;
	int recurse = flag & MS_REC;
	int recurse = flag & MS_REC;
	int type = flag & ~MS_REC;
	int type = flag & ~MS_REC;
	int err = 0;


	if (!capable(CAP_SYS_ADMIN))
	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
		return -EPERM;
@@ -1251,12 +1444,20 @@ static noinline int do_change_type(struct nameidata *nd, int flag)
		return -EINVAL;
		return -EINVAL;


	down_write(&namespace_sem);
	down_write(&namespace_sem);
	if (type == MS_SHARED) {
		err = invent_group_ids(mnt, recurse);
		if (err)
			goto out_unlock;
	}

	spin_lock(&vfsmount_lock);
	spin_lock(&vfsmount_lock);
	for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL))
	for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL))
		change_mnt_propagation(m, type);
		change_mnt_propagation(m, type);
	spin_unlock(&vfsmount_lock);
	spin_unlock(&vfsmount_lock);

 out_unlock:
	up_write(&namespace_sem);
	up_write(&namespace_sem);
	return 0;
	return err;
}
}


/*
/*
+55 −1
Original line number Original line Diff line number Diff line
@@ -28,6 +28,57 @@ static inline struct vfsmount *next_slave(struct vfsmount *p)
	return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave);
	return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave);
}
}


/*
 * Return true if path is reachable from root
 *
 * namespace_sem is held, and mnt is attached
 */
static bool is_path_reachable(struct vfsmount *mnt, struct dentry *dentry,
			 const struct path *root)
{
	while (mnt != root->mnt && mnt->mnt_parent != mnt) {
		dentry = mnt->mnt_mountpoint;
		mnt = mnt->mnt_parent;
	}
	return mnt == root->mnt && is_subdir(dentry, root->dentry);
}

static struct vfsmount *get_peer_under_root(struct vfsmount *mnt,
					    struct mnt_namespace *ns,
					    const struct path *root)
{
	struct vfsmount *m = mnt;

	do {
		/* Check the namespace first for optimization */
		if (m->mnt_ns == ns && is_path_reachable(m, m->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 vfsmount *mnt, const struct path *root)
{
	struct vfsmount *m;

	for (m = mnt->mnt_master; m != NULL; m = m->mnt_master) {
		struct vfsmount *d = get_peer_under_root(m, mnt->mnt_ns, root);
		if (d)
			return d->mnt_group_id;
	}

	return 0;
}

static int do_make_slave(struct vfsmount *mnt)
static int do_make_slave(struct vfsmount *mnt)
{
{
	struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master;
	struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master;
@@ -46,7 +97,11 @@ static int do_make_slave(struct vfsmount *mnt)
		if (peer_mnt == mnt)
		if (peer_mnt == mnt)
			peer_mnt = NULL;
			peer_mnt = NULL;
	}
	}
	if (IS_MNT_SHARED(mnt) && list_empty(&mnt->mnt_share))
		mnt_release_group_id(mnt);

	list_del_init(&mnt->mnt_share);
	list_del_init(&mnt->mnt_share);
	mnt->mnt_group_id = 0;


	if (peer_mnt)
	if (peer_mnt)
		master = peer_mnt;
		master = peer_mnt;
@@ -68,7 +123,6 @@ static int do_make_slave(struct vfsmount *mnt)
	}
	}
	mnt->mnt_master = master;
	mnt->mnt_master = master;
	CLEAR_MNT_SHARED(mnt);
	CLEAR_MNT_SHARED(mnt);
	INIT_LIST_HEAD(&mnt->mnt_slave_list);
	return 0;
	return 0;
}
}


+1 −0
Original line number Original line Diff line number Diff line
@@ -36,4 +36,5 @@ int propagate_mnt(struct vfsmount *, struct dentry *, struct vfsmount *,
int propagate_umount(struct list_head *);
int propagate_umount(struct list_head *);
int propagate_mount_busy(struct vfsmount *, int);
int propagate_mount_busy(struct vfsmount *, int);
void mnt_release_group_id(struct vfsmount *);
void mnt_release_group_id(struct vfsmount *);
int get_dominating_id(struct vfsmount *mnt, const struct path *root);
#endif /* _LINUX_PNODE_H */
#endif /* _LINUX_PNODE_H */
Loading