📄 namespace.c
字号:
/* * 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/slab.h>#include <linux/smp_lock.h>#include <linux/init.h>#include <linux/quotaops.h>#include <linux/acct.h>#include <linux/module.h>#include <linux/devfs_fs_kernel.h>#include <asm/uaccess.h>#include <linux/seq_file.h>struct vfsmount *do_kern_mount(char *type, int flags, char *name, void *data);int do_remount_sb(struct super_block *sb, int flags, void * data);void kill_super(struct super_block *sb);static struct list_head *mount_hashtable;static int hash_mask, hash_bits;static kmem_cache_t *mnt_cache; static LIST_HEAD(vfsmntlist);static DECLARE_MUTEX(mount_sem);/* Will be static */struct vfsmount *root_vfsmnt;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(void){ 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); } return mnt;}void free_vfsmnt(struct vfsmount *mnt){ if (mnt->mnt_devname) kfree(mnt->mnt_devname); kmem_cache_free(mnt_cache, mnt);}void set_devname(struct vfsmount *mnt, const char *name){ if (name) { int size = strlen(name)+1; char * newname = kmalloc(size, GFP_KERNEL); if (newname) { memcpy(newname, name, size); mnt->mnt_devname = newname; } }}struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry){ struct list_head * head = mount_hashtable + hash(mnt, dentry); struct list_head * tmp = head; struct vfsmount *p; for (;;) { tmp = tmp->next; p = NULL; if (tmp == head) break; p = list_entry(tmp, struct vfsmount, mnt_hash); if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) break; } return p;}static int check_mnt(struct vfsmount *mnt){ spin_lock(&dcache_lock); while (mnt->mnt_parent != mnt) mnt = mnt->mnt_parent; spin_unlock(&dcache_lock); return mnt == root_vfsmnt;}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--;}static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd){ mnt->mnt_parent = mntget(nd->mnt); mnt->mnt_mountpoint = dget(nd->dentry); list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry)); list_add(&mnt->mnt_child, &nd->mnt->mnt_mounts); nd->dentry->d_mounted++;}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 *clone_mnt(struct vfsmount *old, struct dentry *root){ struct super_block *sb = old->mnt_sb; struct vfsmount *mnt = alloc_vfsmnt(); if (mnt) { mnt->mnt_flags = old->mnt_flags; set_devname(mnt, old->mnt_devname); atomic_inc(&sb->s_active); mnt->mnt_sb = sb; mnt->mnt_root = dget(root); mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; } return mnt;}void __mntput(struct vfsmount *mnt){ struct super_block *sb = mnt->mnt_sb; dput(mnt->mnt_root); free_vfsmnt(mnt); kill_super(sb);}/* iterator */static void *m_start(struct seq_file *m, loff_t *pos){ struct list_head *p; loff_t n = *pos; down(&mount_sem); list_for_each(p, &vfsmntlist) if (!n--) return list_entry(p, struct vfsmount, mnt_list); return NULL;}static void *m_next(struct seq_file *m, void *v, loff_t *pos){ struct list_head *p = ((struct vfsmount *)v)->mnt_list.next; (*pos)++; return p==&vfsmntlist ? NULL : list_entry(p, struct vfsmount, mnt_list);}static void m_stop(struct seq_file *m, void *v){ up(&mount_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_MANDLOCK, ",mand" }, { MS_NOATIME, ",noatime" }, { MS_NODIRATIME, ",nodiratime" }, { 0, NULL } }; static struct proc_fs_info mnt_info[] = { { MNT_NOSUID, ",nosuid" }, { MNT_NODEV, ",nodev" }, { MNT_NOEXEC, ",noexec" }, { 0, NULL } }; struct proc_fs_info *fs_infop; char *path_buf, *path; path_buf = (char *) __get_free_page(GFP_KERNEL); if (!path_buf) return -ENOMEM; path = d_path(mnt->mnt_root, mnt, path_buf, PAGE_SIZE); mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); seq_putc(m, ' '); mangle(m, path); free_page((unsigned long) path_buf); 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};/* * 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){ if (atomic_read(&mnt->mnt_count) > 2) return -EBUSY; return 0;}void umount_tree(struct vfsmount *mnt){ struct vfsmount *p; LIST_HEAD(kill); for (p = mnt; p; p = next_mnt(p, mnt)) { list_del(&p->mnt_list); list_add(&p->mnt_list, &kill); } while (!list_empty(&kill)) { mnt = list_entry(kill.next, struct vfsmount, mnt_list); list_del_init(&mnt->mnt_list); if (mnt->mnt_parent == mnt) { spin_unlock(&dcache_lock); } else { struct nameidata old_nd; detach_mnt(mnt, &old_nd); spin_unlock(&dcache_lock); path_release(&old_nd); } mntput(mnt); spin_lock(&dcache_lock); }}static int do_umount(struct vfsmount *mnt, int flags){ struct super_block * sb = mnt->mnt_sb; int retval = 0; /* * If we may have to abort operations to get out of this * mount, and they will themselves hold resources we must * allow the fs to do things. In the Unix tradition of * 'Gee thats tricky lets do it in userspace' the umount_begin * might fail to complete on the first run through as other tasks * must return, and the like. Thats for the mount program to worry * about for the moment. */ lock_kernel(); if( (flags&MNT_FORCE) && sb->s_op->umount_begin) sb->s_op->umount_begin(sb); unlock_kernel(); /* * No sense to grab the lock for this test, but test itself looks * somewhat bogus. Suggestions for better replacement? * Ho-hum... In principle, we might treat that as umount + switch * to rootfs. GC would eventually take care of the old vfsmount. * Actually it makes sense, especially if rootfs would contain a * /reboot - static binary that would close all descriptors and * call reboot(9). Then init(8) could umount root and exec /reboot. */ if (mnt == current->fs->rootmnt && !(flags & MNT_DETACH)) { /* * 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(); retval = do_remount_sb(sb, MS_RDONLY, 0); unlock_kernel(); } up_write(&sb->s_umount); return retval; } down(&mount_sem); spin_lock(&dcache_lock); if (atomic_read(&sb->s_active) == 1) { /* last instance - try to be smart */ spin_unlock(&dcache_lock); lock_kernel(); DQUOT_OFF(sb); acct_auto_close(sb->s_dev); unlock_kernel(); spin_lock(&dcache_lock); } retval = -EBUSY; if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) { if (!list_empty(&mnt->mnt_list)) umount_tree(mnt); retval = 0; } spin_unlock(&dcache_lock); up(&mount_sem); 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 * name, int flags){ struct nameidata nd; char *kname; int retval; kname = getname(name); retval = PTR_ERR(kname); if (IS_ERR(kname)) goto out; retval = 0; if (path_init(kname, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd)) retval = path_walk(kname, &nd); putname(kname); if (retval) goto out; retval = -EINVAL; if (nd.dentry != nd.mnt->mnt_root) goto dput_and_out; if (!check_mnt(nd.mnt)) goto dput_and_out; retval = -EPERM; if (!capable(CAP_SYS_ADMIN)) goto dput_and_out; retval = do_umount(nd.mnt, flags);dput_and_out: path_release(&nd);out: return retval;}/* * The 2.0 compatible umount. No flags. */ asmlinkage long sys_oldumount(char * name){ return sys_umount(name,0);}static int mount_is_safe(struct nameidata *nd){ if (capable(CAP_SYS_ADMIN)) return 0; return -EPERM;#ifdef notyet if (S_ISLNK(nd->dentry->d_inode->i_mode)) return -EPERM; if (nd->dentry->d_inode->i_mode & S_ISVTX) { if (current->uid != nd->dentry->d_inode->i_uid) return -EPERM; } if (permission(nd->dentry->d_inode, MAY_WRITE)) return -EPERM; return 0;#endif}static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry){ struct vfsmount *p, *next, *q, *res; struct nameidata nd; p = mnt; res = nd.mnt = q = clone_mnt(p, dentry); if (!q) goto Enomem; q->mnt_parent = q; q->mnt_mountpoint = p->mnt_mountpoint; while ( (next = next_mnt(p, mnt)) != NULL) { while (p != next->mnt_parent) { p = p->mnt_parent; q = q->mnt_parent; } p = next; nd.mnt = q; nd.dentry = p->mnt_mountpoint; q = clone_mnt(p, p->mnt_root); if (!q) goto Enomem; spin_lock(&dcache_lock); list_add_tail(&q->mnt_list, &res->mnt_list); attach_mnt(q, &nd); spin_unlock(&dcache_lock); } return res;Enomem: if (res) { spin_lock(&dcache_lock); umount_tree(res); spin_unlock(&dcache_lock); } return NULL;}/* Will become static */int graft_tree(struct vfsmount *mnt, struct nameidata *nd){ int err; if (mnt->mnt_sb->s_flags & MS_NOUSER) return -EINVAL; if (S_ISDIR(nd->dentry->d_inode->i_mode) != S_ISDIR(mnt->mnt_root->d_inode->i_mode)) return -ENOTDIR; err = -ENOENT; down(&nd->dentry->d_inode->i_zombie); if (IS_DEADDIR(nd->dentry->d_inode)) goto out_unlock; spin_lock(&dcache_lock); if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) { struct list_head head; attach_mnt(mnt, nd); list_add_tail(&head, &mnt->mnt_list); list_splice(&head, vfsmntlist.prev); mntget(mnt); err = 0; } spin_unlock(&dcache_lock);out_unlock: up(&nd->dentry->d_inode->i_zombie); return err;}/* * do loopback mount. */static 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; if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd)) err = path_walk(old_name, &old_nd); if (err) return err; down(&mount_sem); err = -EINVAL; if (check_mnt(nd->mnt) && (!recurse || check_mnt(old_nd.mnt))) { err = -ENOMEM; if (recurse) mnt = copy_tree(old_nd.mnt, old_nd.dentry); else mnt = clone_mnt(old_nd.mnt, old_nd.dentry); } if (mnt) { err = graft_tree(mnt, nd); if (err) { spin_lock(&dcache_lock); umount_tree(mnt); spin_unlock(&dcache_lock); } else mntput(mnt); } up(&mount_sem); path_release(&old_nd); return err;}/* * 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. */static int do_remount(struct nameidata *nd,int flags,int mnt_flags,void *data){ int err; struct super_block * sb = nd->mnt->mnt_sb; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!check_mnt(nd->mnt)) return -EINVAL; if (nd->dentry != nd->mnt->mnt_root) return -EINVAL; down_write(&sb->s_umount); err = do_remount_sb(sb, flags, data); if (!err) nd->mnt->mnt_flags=mnt_flags;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -