1803 lines · 45.1 KB · C
RawDownload
/* *  linux/fs/namespace.c * * (C) Copyright Al Viro 2000, 2001 *	Released under GPL v2. * * Based on code from fs/super.c, copyright Linus Torvalds and others. * Heavily rewritten. */ #include <linux/config.h>#include <linux/syscalls.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/smp_lock.h>#include <linux/init.h>#include <linux/quotaops.h>#include <linux/acct.h>#include <linux/capability.h>#include <linux/module.h>#include <linux/seq_file.h>#include <linux/namespace.h>#include <linux/namei.h>#include <linux/security.h>#include <linux/mount.h>#include <asm/uaccess.h>#include <asm/unistd.h>#include "pnode.h" extern int __init init_rootfs(void); #ifdef CONFIG_SYSFSextern int __init sysfs_init(void);#elsestatic inline int sysfs_init(void){	return 0;}#endif /* spinlock for vfsmount related operations, inplace of dcache_lock */__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); static int event; static struct list_head *mount_hashtable __read_mostly;static int hash_mask __read_mostly, hash_bits __read_mostly;static kmem_cache_t *mnt_cache __read_mostly;static struct rw_semaphore namespace_sem; /* /sys/fs */decl_subsys(fs, NULL, NULL);EXPORT_SYMBOL_GPL(fs_subsys); static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry){	unsigned long tmp = ((unsigned long)mnt / L1_CACHE_BYTES);	tmp += ((unsigned long)dentry / L1_CACHE_BYTES);	tmp = tmp + (tmp >> hash_bits);	return tmp & hash_mask;} struct vfsmount *alloc_vfsmnt(const char *name){	struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL);	if (mnt) {		memset(mnt, 0, sizeof(struct vfsmount));		atomic_set(&mnt->mnt_count, 1);		INIT_LIST_HEAD(&mnt->mnt_hash);		INIT_LIST_HEAD(&mnt->mnt_child);		INIT_LIST_HEAD(&mnt->mnt_mounts);		INIT_LIST_HEAD(&mnt->mnt_list);		INIT_LIST_HEAD(&mnt->mnt_expire);		INIT_LIST_HEAD(&mnt->mnt_share);		INIT_LIST_HEAD(&mnt->mnt_slave_list);		INIT_LIST_HEAD(&mnt->mnt_slave);		if (name) {			int size = strlen(name) + 1;			char *newname = kmalloc(size, GFP_KERNEL);			if (newname) {				memcpy(newname, name, size);				mnt->mnt_devname = newname;			}		}	}	return mnt;} void free_vfsmnt(struct vfsmount *mnt){	kfree(mnt->mnt_devname);	kmem_cache_free(mnt_cache, mnt);} /* * find the first or last mount at @dentry on vfsmount @mnt depending on * @dir. If @dir is set return the first mount else return the last mount. */struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry,			      int dir){	struct list_head *head = mount_hashtable + hash(mnt, dentry);	struct list_head *tmp = head;	struct vfsmount *p, *found = NULL; 	for (;;) {		tmp = dir ? tmp->next : tmp->prev;		p = NULL;		if (tmp == head)			break;		p = list_entry(tmp, struct vfsmount, mnt_hash);		if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) {			found = p;			break;		}	}	return found;} /* * lookup_mnt increments the ref count before returning * the vfsmount struct. */struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry){	struct vfsmount *child_mnt;	spin_lock(&vfsmount_lock);	if ((child_mnt = __lookup_mnt(mnt, dentry, 1)))		mntget(child_mnt);	spin_unlock(&vfsmount_lock);	return child_mnt;} static inline int check_mnt(struct vfsmount *mnt){	return mnt->mnt_namespace == current->namespace;} static void touch_namespace(struct namespace *ns){	if (ns) {		ns->event = ++event;		wake_up_interruptible(&ns->poll);	}} static void __touch_namespace(struct namespace *ns){	if (ns && ns->event != event) {		ns->event = event;		wake_up_interruptible(&ns->poll);	}} static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd){	old_nd->dentry = mnt->mnt_mountpoint;	old_nd->mnt = mnt->mnt_parent;	mnt->mnt_parent = mnt;	mnt->mnt_mountpoint = mnt->mnt_root;	list_del_init(&mnt->mnt_child);	list_del_init(&mnt->mnt_hash);	old_nd->dentry->d_mounted--;} void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,			struct vfsmount *child_mnt){	child_mnt->mnt_parent = mntget(mnt);	child_mnt->mnt_mountpoint = dget(dentry);	dentry->d_mounted++;} static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd){	mnt_set_mountpoint(nd->mnt, nd->dentry, mnt);	list_add_tail(&mnt->mnt_hash, mount_hashtable +			hash(nd->mnt, nd->dentry));	list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts);} /* * the caller must hold vfsmount_lock */static void commit_tree(struct vfsmount *mnt){	struct vfsmount *parent = mnt->mnt_parent;	struct vfsmount *m;	LIST_HEAD(head);	struct namespace *n = parent->mnt_namespace; 	BUG_ON(parent == mnt); 	list_add_tail(&head, &mnt->mnt_list);	list_for_each_entry(m, &head, mnt_list)		m->mnt_namespace = n;	list_splice(&head, n->list.prev); 	list_add_tail(&mnt->mnt_hash, mount_hashtable +				hash(parent, mnt->mnt_mountpoint));	list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);	touch_namespace(n);} static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root){	struct list_head *next = p->mnt_mounts.next;	if (next == &p->mnt_mounts) {		while (1) {			if (p == root)				return NULL;			next = p->mnt_child.next;			if (next != &p->mnt_parent->mnt_mounts)				break;			p = p->mnt_parent;		}	}	return list_entry(next, struct vfsmount, mnt_child);} static struct vfsmount *skip_mnt_tree(struct vfsmount *p){	struct list_head *prev = p->mnt_mounts.prev;	while (prev != &p->mnt_mounts) {		p = list_entry(prev, struct vfsmount, mnt_child);		prev = p->mnt_mounts.prev;	}	return p;} static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,					int flag){	struct super_block *sb = old->mnt_sb;	struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); 	if (mnt) {		mnt->mnt_flags = old->mnt_flags;		atomic_inc(&sb->s_active);		mnt->mnt_sb = sb;		mnt->mnt_root = dget(root);		mnt->mnt_mountpoint = mnt->mnt_root;		mnt->mnt_parent = mnt; 		if (flag & CL_SLAVE) {			list_add(&mnt->mnt_slave, &old->mnt_slave_list);			mnt->mnt_master = old;			CLEAR_MNT_SHARED(mnt);		} else {			if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old))				list_add(&mnt->mnt_share, &old->mnt_share);			if (IS_MNT_SLAVE(old))				list_add(&mnt->mnt_slave, &old->mnt_slave);			mnt->mnt_master = old->mnt_master;		}		if (flag & CL_MAKE_SHARED)			set_mnt_shared(mnt); 		/* stick the duplicate mount on the same expiry list		 * as the original if that was on one */		if (flag & CL_EXPIRE) {			spin_lock(&vfsmount_lock);			if (!list_empty(&old->mnt_expire))				list_add(&mnt->mnt_expire, &old->mnt_expire);			spin_unlock(&vfsmount_lock);		}	}	return mnt;} static inline void __mntput(struct vfsmount *mnt){	struct super_block *sb = mnt->mnt_sb;	dput(mnt->mnt_root);	free_vfsmnt(mnt);	deactivate_super(sb);} void mntput_no_expire(struct vfsmount *mnt){repeat:	if (atomic_dec_and_lock(&mnt->mnt_count, &vfsmount_lock)) {		if (likely(!mnt->mnt_pinned)) {			spin_unlock(&vfsmount_lock);			__mntput(mnt);			return;		}		atomic_add(mnt->mnt_pinned + 1, &mnt->mnt_count);		mnt->mnt_pinned = 0;		spin_unlock(&vfsmount_lock);		acct_auto_close_mnt(mnt);		security_sb_umount_close(mnt);		goto repeat;	}} EXPORT_SYMBOL(mntput_no_expire); void mnt_pin(struct vfsmount *mnt){	spin_lock(&vfsmount_lock);	mnt->mnt_pinned++;	spin_unlock(&vfsmount_lock);} EXPORT_SYMBOL(mnt_pin); void mnt_unpin(struct vfsmount *mnt){	spin_lock(&vfsmount_lock);	if (mnt->mnt_pinned) {		atomic_inc(&mnt->mnt_count);		mnt->mnt_pinned--;	}	spin_unlock(&vfsmount_lock);} EXPORT_SYMBOL(mnt_unpin); /* iterator */static void *m_start(struct seq_file *m, loff_t *pos){	struct namespace *n = m->private;	struct list_head *p;	loff_t l = *pos; 	down_read(&namespace_sem);	list_for_each(p, &n->list)		if (!l--)			return list_entry(p, struct vfsmount, mnt_list);	return NULL;} static void *m_next(struct seq_file *m, void *v, loff_t *pos){	struct namespace *n = m->private;	struct list_head *p = ((struct vfsmount *)v)->mnt_list.next;	(*pos)++;	return p == &n->list ? NULL : list_entry(p, struct vfsmount, mnt_list);} static void m_stop(struct seq_file *m, void *v){	up_read(&namespace_sem);} static inline void mangle(struct seq_file *m, const char *s){	seq_escape(m, s, " \t\n\\");} static int show_vfsmnt(struct seq_file *m, void *v){	struct vfsmount *mnt = v;	int err = 0;	static struct proc_fs_info {		int flag;		char *str;	} fs_info[] = {		{ MS_SYNCHRONOUS, ",sync" },		{ MS_DIRSYNC, ",dirsync" },		{ MS_MANDLOCK, ",mand" },		{ 0, NULL }	};	static struct proc_fs_info mnt_info[] = {		{ MNT_NOSUID, ",nosuid" },		{ MNT_NODEV, ",nodev" },		{ MNT_NOEXEC, ",noexec" },		{ MNT_NOATIME, ",noatime" },		{ MNT_NODIRATIME, ",nodiratime" },		{ 0, NULL }	};	struct proc_fs_info *fs_infop; 	mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");	seq_putc(m, ' ');	seq_path(m, mnt, mnt->mnt_root, " \t\n\\");	seq_putc(m, ' ');	mangle(m, mnt->mnt_sb->s_type->name);	seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw");	for (fs_infop = fs_info; fs_infop->flag; fs_infop++) {		if (mnt->mnt_sb->s_flags & fs_infop->flag)			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)		err = mnt->mnt_sb->s_op->show_options(m, mnt);	seq_puts(m, " 0 0\n");	return err;} struct seq_operations mounts_op = {	.start	= m_start,	.next	= m_next,	.stop	= m_stop,	.show	= show_vfsmnt}; static int show_vfsstat(struct seq_file *m, void *v){	struct vfsmount *mnt = v;	int err = 0; 	/* device */	if (mnt->mnt_devname) {		seq_puts(m, "device ");		mangle(m, mnt->mnt_devname);	} else		seq_puts(m, "no device"); 	/* mount point */	seq_puts(m, " mounted on ");	seq_path(m, mnt, mnt->mnt_root, " \t\n\\");	seq_putc(m, ' '); 	/* file system type */	seq_puts(m, "with fstype ");	mangle(m, mnt->mnt_sb->s_type->name); 	/* optional statistics */	if (mnt->mnt_sb->s_op->show_stats) {		seq_putc(m, ' ');		err = mnt->mnt_sb->s_op->show_stats(m, mnt);	} 	seq_putc(m, '\n');	return err;} struct seq_operations mountstats_op = {	.start	= m_start,	.next	= m_next,	.stop	= m_stop,	.show	= show_vfsstat,}; /** * may_umount_tree - check if a mount tree is busy * @mnt: root of mount tree * * This is called to check if a tree of mounts has any * open files, pwds, chroots or sub mounts that are * busy. */int may_umount_tree(struct vfsmount *mnt){	int actual_refs = 0;	int minimum_refs = 0;	struct vfsmount *p; 	spin_lock(&vfsmount_lock);	for (p = mnt; p; p = next_mnt(p, mnt)) {		actual_refs += atomic_read(&p->mnt_count);		minimum_refs += 2;	}	spin_unlock(&vfsmount_lock); 	if (actual_refs > minimum_refs)		return 0; 	return 1;} EXPORT_SYMBOL(may_umount_tree); /** * may_umount - check if a mount point is busy * @mnt: root of mount * * This is called to check if a mount point has any * open files, pwds, chroots or sub mounts. If the * mount has sub mounts this will return busy * regardless of whether the sub mounts are busy. * * Doesn't take quota and stuff into account. IOW, in some cases it will * give false negatives. The main reason why it's here is that we need * a non-destructive way to look for easily umountable filesystems. */int may_umount(struct vfsmount *mnt){	int ret = 1;	spin_lock(&vfsmount_lock);	if (propagate_mount_busy(mnt, 2))		ret = 0;	spin_unlock(&vfsmount_lock);	return ret;} EXPORT_SYMBOL(may_umount); void release_mounts(struct list_head *head){	struct vfsmount *mnt;	while (!list_empty(head)) {		mnt = list_entry(head->next, struct vfsmount, mnt_hash);		list_del_init(&mnt->mnt_hash);		if (mnt->mnt_parent != mnt) {

Lines 1–500 of 1803.