Commit f539abec authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6:
  fs: call security_d_instantiate in d_obtain_alias V2
  lose 'mounting_here' argument in ->d_manage()
  don't pass 'mounting_here' flag to follow_down()
  change the locking order for namespace_sem
  fix deadlock in pivot_root()
  vfs: split off vfsmount-related parts of vfs_kern_mount()
  Some fixes for pstore
  kill simple_set_mnt()
parents 3f6f7e6d 24ff6663
...@@ -873,7 +873,7 @@ struct dentry_operations { ...@@ -873,7 +873,7 @@ struct dentry_operations {
void (*d_iput)(struct dentry *, struct inode *); void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int); char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *); struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool, bool); int (*d_manage)(struct dentry *, bool);
}; };
d_revalidate: called when the VFS needs to revalidate a dentry. This d_revalidate: called when the VFS needs to revalidate a dentry. This
...@@ -969,10 +969,6 @@ struct dentry_operations { ...@@ -969,10 +969,6 @@ struct dentry_operations {
mounted on it and not to check the automount flag. Any other error mounted on it and not to check the automount flag. Any other error
code will abort pathwalk completely. code will abort pathwalk completely.
If the 'mounting_here' parameter is true, then namespace_sem is being
held by the caller and the function should not initiate any mounts or
unmounts that it will then wait for.
If the 'rcu_walk' parameter is true, then the caller is doing a If the 'rcu_walk' parameter is true, then the caller is doing a
pathwalk in RCU-walk mode. Sleeping is not permitted in this mode, pathwalk in RCU-walk mode. Sleeping is not permitted in this mode,
and the caller can be asked to leave it and call again by returing and the caller can be asked to leave it and call again by returing
......
...@@ -36,7 +36,7 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); ...@@ -36,7 +36,7 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long);
static int autofs4_dir_open(struct inode *inode, struct file *file); static int autofs4_dir_open(struct inode *inode, struct file *file);
static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
static struct vfsmount *autofs4_d_automount(struct path *); static struct vfsmount *autofs4_d_automount(struct path *);
static int autofs4_d_manage(struct dentry *, bool, bool); static int autofs4_d_manage(struct dentry *, bool);
static void autofs4_dentry_release(struct dentry *); static void autofs4_dentry_release(struct dentry *);
const struct file_operations autofs4_root_operations = { const struct file_operations autofs4_root_operations = {
...@@ -446,7 +446,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path) ...@@ -446,7 +446,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
return NULL; return NULL;
} }
int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk) int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
{ {
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
...@@ -454,7 +454,7 @@ int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk) ...@@ -454,7 +454,7 @@ int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk)
dentry, dentry->d_name.len, dentry->d_name.name); dentry, dentry->d_name.len, dentry->d_name.name);
/* The daemon never waits. */ /* The daemon never waits. */
if (autofs4_oz_mode(sbi) || mounting_here) { if (autofs4_oz_mode(sbi)) {
if (!d_mountpoint(dentry)) if (!d_mountpoint(dentry))
return -EISDIR; return -EISDIR;
return 0; return 0;
......
...@@ -1612,10 +1612,13 @@ struct dentry *d_obtain_alias(struct inode *inode) ...@@ -1612,10 +1612,13 @@ struct dentry *d_obtain_alias(struct inode *inode)
__bit_spin_unlock(0, (unsigned long *)&tmp->d_sb->s_anon.first); __bit_spin_unlock(0, (unsigned long *)&tmp->d_sb->s_anon.first);
spin_unlock(&tmp->d_lock); spin_unlock(&tmp->d_lock);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
security_d_instantiate(tmp, inode);
return tmp; return tmp;
out_iput: out_iput:
if (res && !IS_ERR(res))
security_d_instantiate(res, inode);
iput(inode); iput(inode);
return res; return res;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/lglock.h> #include <linux/lglock.h>
struct super_block; struct super_block;
struct file_system_type;
struct linux_binprm; struct linux_binprm;
struct path; struct path;
...@@ -61,8 +62,6 @@ extern int check_unsafe_exec(struct linux_binprm *); ...@@ -61,8 +62,6 @@ extern int check_unsafe_exec(struct linux_binprm *);
extern int copy_mount_options(const void __user *, unsigned long *); extern int copy_mount_options(const void __user *, unsigned long *);
extern int copy_mount_string(const void __user *, char **); extern int copy_mount_string(const void __user *, char **);
extern void free_vfsmnt(struct vfsmount *);
extern struct vfsmount *alloc_vfsmnt(const char *);
extern unsigned int mnt_get_count(struct vfsmount *mnt); extern unsigned int mnt_get_count(struct vfsmount *mnt);
extern struct vfsmount *__lookup_mnt(struct vfsmount *, struct dentry *, int); extern struct vfsmount *__lookup_mnt(struct vfsmount *, struct dentry *, int);
extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *, extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
...@@ -99,6 +98,8 @@ extern struct file *get_empty_filp(void); ...@@ -99,6 +98,8 @@ extern struct file *get_empty_filp(void);
extern int do_remount_sb(struct super_block *, int, void *, int); extern int do_remount_sb(struct super_block *, int, void *, int);
extern void __put_super(struct super_block *sb); extern void __put_super(struct super_block *sb);
extern void put_super(struct super_block *sb); extern void put_super(struct super_block *sb);
extern struct dentry *mount_fs(struct file_system_type *,
int, const char *, void *);
/* /*
* open.c * open.c
......
...@@ -933,8 +933,7 @@ static int follow_managed(struct path *path, unsigned flags) ...@@ -933,8 +933,7 @@ static int follow_managed(struct path *path, unsigned flags)
if (managed & DCACHE_MANAGE_TRANSIT) { if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op); BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage); BUG_ON(!path->dentry->d_op->d_manage);
ret = path->dentry->d_op->d_manage(path->dentry, ret = path->dentry->d_op->d_manage(path->dentry, false);
false, false);
if (ret < 0) if (ret < 0)
return ret == -EISDIR ? 0 : ret; return ret == -EISDIR ? 0 : ret;
} }
...@@ -999,7 +998,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, ...@@ -999,7 +998,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
struct vfsmount *mounted; struct vfsmount *mounted;
if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) && if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
!reverse_transit && !reverse_transit &&
path->dentry->d_op->d_manage(path->dentry, false, true) < 0) path->dentry->d_op->d_manage(path->dentry, true) < 0)
return false; return false;
mounted = __lookup_mnt(path->mnt, path->dentry, 1); mounted = __lookup_mnt(path->mnt, path->dentry, 1);
if (!mounted) if (!mounted)
...@@ -1065,7 +1064,7 @@ static int follow_dotdot_rcu(struct nameidata *nd) ...@@ -1065,7 +1064,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
* Care must be taken as namespace_sem may be held (indicated by mounting_here * Care must be taken as namespace_sem may be held (indicated by mounting_here
* being true). * being true).
*/ */
int follow_down(struct path *path, bool mounting_here) int follow_down(struct path *path)
{ {
unsigned managed; unsigned managed;
int ret; int ret;
...@@ -1086,7 +1085,7 @@ int follow_down(struct path *path, bool mounting_here) ...@@ -1086,7 +1085,7 @@ int follow_down(struct path *path, bool mounting_here)
BUG_ON(!path->dentry->d_op); BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage); BUG_ON(!path->dentry->d_op->d_manage);
ret = path->dentry->d_op->d_manage( ret = path->dentry->d_op->d_manage(
path->dentry, mounting_here, false); path->dentry, false);
if (ret < 0) if (ret < 0)
return ret == -EISDIR ? 0 : ret; return ret == -EISDIR ? 0 : ret;
} }
......
...@@ -196,7 +196,7 @@ unsigned int mnt_get_count(struct vfsmount *mnt) ...@@ -196,7 +196,7 @@ unsigned int mnt_get_count(struct vfsmount *mnt)
#endif #endif
} }
struct vfsmount *alloc_vfsmnt(const char *name) static 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) {
...@@ -466,15 +466,7 @@ static void __mnt_unmake_readonly(struct vfsmount *mnt) ...@@ -466,15 +466,7 @@ static void __mnt_unmake_readonly(struct vfsmount *mnt)
br_write_unlock(vfsmount_lock); br_write_unlock(vfsmount_lock);
} }
void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb) static void free_vfsmnt(struct vfsmount *mnt)
{
mnt->mnt_sb = sb;
mnt->mnt_root = dget(sb->s_root);
}
EXPORT_SYMBOL(simple_set_mnt);
void free_vfsmnt(struct vfsmount *mnt)
{ {
kfree(mnt->mnt_devname); kfree(mnt->mnt_devname);
mnt_free_id(mnt); mnt_free_id(mnt);
...@@ -678,6 +670,36 @@ static struct vfsmount *skip_mnt_tree(struct vfsmount *p) ...@@ -678,6 +670,36 @@ static struct vfsmount *skip_mnt_tree(struct vfsmount *p)
return p; return p;
} }
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
struct vfsmount *mnt;
struct dentry *root;
if (!type)
return ERR_PTR(-ENODEV);
mnt = alloc_vfsmnt(name);
if (!mnt)
return ERR_PTR(-ENOMEM);
if (flags & MS_KERNMOUNT)
mnt->mnt_flags = MNT_INTERNAL;
root = mount_fs(type, flags, name, data);
if (IS_ERR(root)) {
free_vfsmnt(mnt);
return ERR_CAST(root);
}
mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
mnt->mnt_mountpoint = mnt->mnt_root;
mnt->mnt_parent = mnt;
return mnt;
}
EXPORT_SYMBOL_GPL(vfs_kern_mount);
static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,
int flag) int flag)
{ {
...@@ -1641,9 +1663,35 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, ...@@ -1641,9 +1663,35 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt,
return err; return err;
} }
static int lock_mount(struct path *path)
{
struct vfsmount *mnt;
retry:
mutex_lock(&path->dentry->d_inode->i_mutex);
if (unlikely(cant_mount(path->dentry))) {
mutex_unlock(&path->dentry->d_inode->i_mutex);
return -ENOENT;
}
down_write(&namespace_sem);
mnt = lookup_mnt(path);
if (likely(!mnt))
return 0;
up_write(&namespace_sem);
mutex_unlock(&path->dentry->d_inode->i_mutex);
path_put(path);
path->mnt = mnt;
path->dentry = dget(mnt->mnt_root);
goto retry;
}
static void unlock_mount(struct path *path)
{
up_write(&namespace_sem);
mutex_unlock(&path->dentry->d_inode->i_mutex);
}
static int graft_tree(struct vfsmount *mnt, struct path *path) static int graft_tree(struct vfsmount *mnt, struct path *path)
{ {
int err;
if (mnt->mnt_sb->s_flags & MS_NOUSER) if (mnt->mnt_sb->s_flags & MS_NOUSER)
return -EINVAL; return -EINVAL;
...@@ -1651,16 +1699,10 @@ static int graft_tree(struct vfsmount *mnt, struct path *path) ...@@ -1651,16 +1699,10 @@ static int graft_tree(struct vfsmount *mnt, struct path *path)
S_ISDIR(mnt->mnt_root->d_inode->i_mode)) S_ISDIR(mnt->mnt_root->d_inode->i_mode))
return -ENOTDIR; return -ENOTDIR;
err = -ENOENT; if (d_unlinked(path->dentry))
mutex_lock(&path->dentry->d_inode->i_mutex); return -ENOENT;
if (cant_mount(path->dentry))
goto out_unlock;
if (!d_unlinked(path->dentry)) return attach_recursive_mnt(mnt, path, NULL);
err = attach_recursive_mnt(mnt, path, NULL);
out_unlock:
mutex_unlock(&path->dentry->d_inode->i_mutex);
return err;
} }
/* /*
...@@ -1723,6 +1765,7 @@ static int do_change_type(struct path *path, int flag) ...@@ -1723,6 +1765,7 @@ static int do_change_type(struct path *path, int flag)
static int do_loopback(struct path *path, char *old_name, static int do_loopback(struct path *path, char *old_name,
int recurse) int recurse)
{ {
LIST_HEAD(umount_list);
struct path old_path; struct path old_path;
struct vfsmount *mnt = NULL; struct vfsmount *mnt = NULL;
int err = mount_is_safe(path); int err = mount_is_safe(path);
...@@ -1734,13 +1777,16 @@ static int do_loopback(struct path *path, char *old_name, ...@@ -1734,13 +1777,16 @@ static int do_loopback(struct path *path, char *old_name,
if (err) if (err)
return err; return err;
down_write(&namespace_sem); err = lock_mount(path);
if (err)
goto out;
err = -EINVAL; err = -EINVAL;
if (IS_MNT_UNBINDABLE(old_path.mnt)) if (IS_MNT_UNBINDABLE(old_path.mnt))
goto out; goto out2;
if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt))
goto out; goto out2;
err = -ENOMEM; err = -ENOMEM;
if (recurse) if (recurse)
...@@ -1749,20 +1795,18 @@ static int do_loopback(struct path *path, char *old_name, ...@@ -1749,20 +1795,18 @@ static int do_loopback(struct path *path, char *old_name,
mnt = clone_mnt(old_path.mnt, old_path.dentry, 0); mnt = clone_mnt(old_path.mnt, old_path.dentry, 0);
if (!mnt) if (!mnt)
goto out; goto out2;
err = graft_tree(mnt, path); err = graft_tree(mnt, path);
if (err) { if (err) {
LIST_HEAD(umount_list);
br_write_lock(vfsmount_lock); br_write_lock(vfsmount_lock);
umount_tree(mnt, 0, &umount_list); umount_tree(mnt, 0, &umount_list);
br_write_unlock(vfsmount_lock); br_write_unlock(vfsmount_lock);
release_mounts(&umount_list);
} }
out2:
unlock_mount(path);
release_mounts(&umount_list);
out: out:
up_write(&namespace_sem);
path_put(&old_path); path_put(&old_path);
return err; return err;
} }
...@@ -1851,18 +1895,12 @@ static int do_move_mount(struct path *path, char *old_name) ...@@ -1851,18 +1895,12 @@ static int do_move_mount(struct path *path, char *old_name)
if (err) if (err)
return err; return err;
down_write(&namespace_sem); err = lock_mount(path);
err = follow_down(path, true);
if (err < 0) if (err < 0)
goto out; goto out;
err = -EINVAL; err = -EINVAL;
if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt))
goto out;
err = -ENOENT;
mutex_lock(&path->dentry->d_inode->i_mutex);
if (cant_mount(path->dentry))
goto out1; goto out1;
if (d_unlinked(path->dentry)) if (d_unlinked(path->dentry))
...@@ -1904,16 +1942,87 @@ static int do_move_mount(struct path *path, char *old_name) ...@@ -1904,16 +1942,87 @@ static int do_move_mount(struct path *path, char *old_name)
* automatically */ * automatically */
list_del_init(&old_path.mnt->mnt_expire); list_del_init(&old_path.mnt->mnt_expire);
out1: out1:
mutex_unlock(&path->dentry->d_inode->i_mutex); unlock_mount(path);
out: out:
up_write(&namespace_sem);
if (!err) if (!err)
path_put(&parent_path); path_put(&parent_path);
path_put(&old_path); path_put(&old_path);
return err; return err;
} }
static int do_add_mount(struct vfsmount *, struct path *, int); static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
{
int err;
const char *subtype = strchr(fstype, '.');
if (subtype) {
subtype++;
err = -EINVAL;
if (!subtype[0])
goto err;
} else
subtype = "";
mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL);
err = -ENOMEM;
if (!mnt->mnt_sb->s_subtype)
goto err;
return mnt;
err:
mntput(mnt);
return ERR_PTR(err);
}
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
struct file_system_type *type = get_fs_type(fstype);
struct vfsmount *mnt;
if (!type)
return ERR_PTR(-ENODEV);
mnt = vfs_kern_mount(type, flags, name, data);
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
!mnt->mnt_sb->s_subtype)
mnt = fs_set_subtype(mnt, fstype);
put_filesystem(type);
return mnt;
}
EXPORT_SYMBOL_GPL(do_kern_mount);
/*
* add a mount into a namespace's mount tree
*/
static int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
{
int err;
mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
err = lock_mount(path);
if (err)
return err;
err = -EINVAL;
if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))
goto unlock;
/* Refuse the same filesystem on the same mount point */
err = -EBUSY;
if (path->mnt->mnt_sb == newmnt->mnt_sb &&
path->mnt->mnt_root == path->dentry)
goto unlock;
err = -EINVAL;
if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
goto unlock;
newmnt->mnt_flags = mnt_flags;
err = graft_tree(newmnt, path);
unlock:
unlock_mount(path);
return err;
}
/* /*
* create a new mount for userspace and request it to be added into the * create a new mount for userspace and request it to be added into the
...@@ -1973,43 +2082,6 @@ int finish_automount(struct vfsmount *m, struct path *path) ...@@ -1973,43 +2082,6 @@ int finish_automount(struct vfsmount *m, struct path *path)
return err; return err;
} }
/*
* add a mount into a namespace's mount tree
*/
static int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
{
int err;
mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
down_write(&namespace_sem);
/* Something was mounted here while we slept */
err = follow_down(path, true);
if (err < 0)
goto unlock;
err = -EINVAL;
if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))
goto unlock;
/* Refuse the same filesystem on the same mount point */
err = -EBUSY;
if (path->mnt->mnt_sb == newmnt->mnt_sb &&
path->mnt->mnt_root == path->dentry)
goto unlock;
err = -EINVAL;
if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
goto unlock;
newmnt->mnt_flags = mnt_flags;
err = graft_tree(newmnt, path);
unlock:
up_write(&namespace_sem);
return err;
}
/** /**
* mnt_set_expiry - Put a mount on an expiration list * mnt_set_expiry - Put a mount on an expiration list
* @mnt: The mount to list. * @mnt: The mount to list.
...@@ -2510,65 +2582,60 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, ...@@ -2510,65 +2582,60 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
error = user_path_dir(new_root, &new); error = user_path_dir(new_root, &new);
if (error) if (error)
goto out0; goto out0;
error = -EINVAL;
if (!check_mnt(new.mnt))
goto out1;
error = user_path_dir(put_old, &old); error = user_path_dir(put_old, &old);
if (error) if (error)
goto out1; goto out1;
error = security_sb_pivotroot(&old, &new); error = security_sb_pivotroot(&old, &new);
if (error) { if (error)
path_put(&old); goto out2;
goto out1;
}
get_fs_root(current->fs, &root); get_fs_root(current->fs, &root);
down_write(&namespace_sem); error = lock_mount(&old);
mutex_lock(&old.dentry->d_inode->i_mutex); if (error)
goto out3;
error = -EINVAL; error = -EINVAL;
if (IS_MNT_SHARED(old.mnt) || if (IS_MNT_SHARED(old.mnt) ||
IS_MNT_SHARED(new.mnt->mnt_parent) || IS_MNT_SHARED(new.mnt->mnt_parent) ||
IS_MNT_SHARED(root.mnt->mnt_parent)) IS_MNT_SHARED(root.mnt->mnt_parent))
goto out2; goto out4;
if (!check_mnt(root.mnt)) if (!check_mnt(root.mnt) || !check_mnt(new.mnt))
goto out2; goto out4;
error = -ENOENT; error = -ENOENT;
if (cant_mount(old.dentry))
goto out2;
if (d_unlinked(new.dentry)) if (d_unlinked(new.dentry))
goto out2; goto out4;
if (d_unlinked(old.dentry)) if (d_unlinked(old.dentry))
goto out2; goto out4;
error = -EBUSY; error = -EBUSY;
if (new.mnt == root.mnt || if (new.mnt == root.mnt ||
old.mnt == root.mnt) old.mnt == root.mnt)
goto out2; /* loop, on the same file system */ goto out4; /* loop, on the same file system */
error = -EINVAL; error = -EINVAL;
if (root.mnt->mnt_root != root.dentry) if (root.mnt->mnt_root != root.dentry)
goto out2; /* not a mountpoint */ goto out4; /* not a mountpoint */
if (root.mnt->mnt_parent == root.mnt) if (root.mnt->mnt_parent == root.mnt)
goto out2; /* not attached */ goto out4; /* not attached */
if (new.mnt->mnt_root != new.dentry) if (new.mnt->mnt_root != new.dentry)
goto out2; /* not a mountpoint */ goto out4; /* not a mountpoint */
if (new.mnt->mnt_parent == new.mnt) if (new.mnt->mnt_parent == new.mnt)
goto out2; /* not attached */ goto out4; /* not attached */
/* make sure we can reach put_old from new_root */ /* make sure we can reach put_old from new_root */
tmp = old.mnt; tmp = old.mnt;
br_write_lock(vfsmount_lock);
if (tmp != new.mnt) { if (tmp != new.mnt) {
for (;;) { for (;;) {
if (tmp->mnt_parent == tmp) if (tmp->mnt_parent == tmp)
goto out3; /* already mounted on put_old */ goto out4; /* already mounted on put_old */
if (tmp->mnt_parent == new.mnt) if (tmp->mnt_parent == new.mnt)
break; break;
tmp = tmp->mnt_parent; tmp = tmp->mnt_parent;
} }
if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) if (!is_subdir(tmp->mnt_mountpoint, new.dentry))
goto out3; goto out4;
} else if (!is_subdir(old.dentry, new.dentry)) } else if (!is_subdir(old.dentry, new.dentry))
goto out3; goto out4;
br_write_lock(vfsmount_lock);
detach_mnt(new.mnt, &parent_path); detach_mnt(new.mnt, &parent_path);
detach_mnt(root.mnt, &root_parent); detach_mnt(root.mnt, &root_parent);
/* mount old root on put_old */ /* mount old root on put_old */
...@@ -2578,22 +2645,21 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, ...@@ -2578,22 +2645,21 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
touch_mnt_namespace(current->nsproxy->mnt_ns); touch_mnt_namespace(current->nsproxy->mnt_ns);
br_write_unlock(vfsmount_lock); br_write_unlock(vfsmount_lock);
chroot_fs_refs(&root, &new); chroot_fs_refs(&root, &new);
error = 0; error = 0;
out4:
unlock_mount(&old);
if (!error) {
path_put(&root_parent); path_put(&root_parent);
path_put(&parent_path); path_put(&parent_path);
out2: }
mutex_unlock(&old.dentry->d_inode->i_mutex); out3:
up_write(&namespace_sem);
path_put(&root); path_put(&root);
out2:
path_put(&old); path_put(&old);
out1: out1:
path_put(&new); path_put(&new);
out0: out0:
return error; return error;
out3:
br_write_unlock(vfsmount_lock);
goto out2;
} }
static void __init init_mount_tree(void) static void __init init_mount_tree(void)
...@@ -2668,3 +2734,9 @@ void put_mnt_ns(struct mnt_namespace *ns) ...@@ -2668,3 +2734,9 @@ void put_mnt_ns(struct mnt_namespace *ns)
kfree(ns); kfree(ns);
} }
EXPORT_SYMBOL(put_mnt_ns); EXPORT_SYMBOL(put_mnt_ns);
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
{
return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
}
EXPORT_SYMBOL_GPL(kern_mount_data);
...@@ -87,7 +87,7 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, ...@@ -87,7 +87,7 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
.dentry = dget(dentry)}; .dentry = dget(dentry)};
int err = 0; int err = 0;
err = follow_down(&path, false); err = follow_down(&path);
if (err < 0) if (err < 0)
goto out; goto out;
......
...@@ -40,9 +40,29 @@ ...@@ -40,9 +40,29 @@
struct pstore_private { struct pstore_private {
u64 id; u64 id;
int (*erase)(u64); int (*erase)(u64);
ssize_t size;
char data[];
}; };
#define pstore_get_inode ramfs_get_inode static int pstore_file_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct pstore_private *ps = file->private_data;
return simple_read_from_buffer(userbuf, count, ppos, ps->data, ps->size);
}
static const struct file_operations pstore_file_operations = {
.open = pstore_file_open,
.read = pstore_file_read,
.llseek = default_llseek,
};
/* /*
* When a file is unlinked from our file system we call the * When a file is unlinked from our file system we call the
...@@ -63,6 +83,30 @@ static const struct inode_operations pstore_dir_inode_operations = { ...@@ -63,6 +83,30 @@ static const struct inode_operations pstore_dir_inode_operations = {
.unlink = pstore_unlink, .unlink = pstore_unlink,
}; };
static struct inode *pstore_get_inode(struct super_block *sb,
const struct inode *dir, int mode, dev_t dev)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_ino = get_next_ino();
inode->i_uid = inode->i_gid = 0;
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
case S_IFREG:
inode->i_fop = &pstore_file_operations;
break;
case S_IFDIR:
inode->i_op = &pstore_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inc_nlink(inode);
break;
}
}
return inode;
}
static const struct super_operations pstore_ops = { static const struct super_operations pstore_ops = {
.statfs = simple_statfs, .statfs = simple_statfs,
.drop_inode = generic_delete_inode, .drop_inode = generic_delete_inode,
...@@ -70,37 +114,10 @@ static const struct super_operations pstore_ops = { ...@@ -70,37 +114,10 @@ static const struct super_operations pstore_ops = {
}; };
static struct super_block *pstore_sb; static struct super_block *pstore_sb;
static struct vfsmount *pstore_mnt;
int pstore_is_mounted(void) int pstore_is_mounted(void)
{ {
return pstore_mnt != NULL; return pstore_sb != NULL;
}
/*
* Set up a file structure as if we had opened this file and
* write our data to it.
*/
static int pstore_writefile(struct inode *inode, struct dentry *dentry,
char *data, size_t size)
{
struct file f;
ssize_t n;
mm_segment_t old_fs = get_fs();
memset(&f, '0', sizeof f);
f.f_mapping = inode->i_mapping;
f.f_path.dentry = dentry;
f.f_path.mnt = pstore_mnt;
f.f_pos = 0;
f.f_op = inode->i_fop;
set_fs(KERNEL_DS);
n = do_sync_write(&f, data, size, &f.f_pos);
set_fs(old_fs);
fsnotify_modify(&f);
return n == size;
} }
/* /*
...@@ -123,8 +140,7 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, ...@@ -123,8 +140,7 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
inode = pstore_get_inode(pstore_sb, root->d_inode, S_IFREG | 0444, 0); inode = pstore_get_inode(pstore_sb, root->d_inode, S_IFREG | 0444, 0);
if (!inode) if (!inode)
goto fail; goto fail;
inode->i_uid = inode->i_gid = 0; private = kmalloc(sizeof *private + size, GFP_KERNEL);
private = kmalloc(sizeof *private, GFP_KERNEL);
if (!private) if (!private)
goto fail_alloc; goto fail_alloc;
private->id = id; private->id = id;
...@@ -152,28 +168,19 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, ...@@ -152,28 +168,19 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto fail_lockedalloc; goto fail_lockedalloc;
d_add(dentry, inode); memcpy(private->data, data, size);
inode->i_size = private->size = size;
mutex_unlock(&root->d_inode->i_mutex);
if (!pstore_writefile(inode, dentry, data, size))
goto fail_write;
inode->i_private = private; inode->i_private = private;
if (time.tv_sec) if (time.tv_sec)
inode->i_mtime = inode->i_ctime = time; inode->i_mtime = inode->i_ctime = time;
return 0; d_add(dentry, inode);
fail_write:
kfree(private);
inode->i_nlink--;
mutex_lock(&root->d_inode->i_mutex);
d_delete(dentry);
dput(dentry);
mutex_unlock(&root->d_inode->i_mutex); mutex_unlock(&root->d_inode->i_mutex);
goto fail;
return 0;
fail_lockedalloc: fail_lockedalloc:
mutex_unlock(&root->d_inode->i_mutex); mutex_unlock(&root->d_inode->i_mutex);
...@@ -225,32 +232,21 @@ int pstore_fill_super(struct super_block *sb, void *data, int silent) ...@@ -225,32 +232,21 @@ int pstore_fill_super(struct super_block *sb, void *data, int silent)
return err; return err;
} }
static int pstore_get_sb(struct file_system_type *fs_type, static struct dentry *pstore_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt) int flags, const char *dev_name, void *data)
{ {
struct dentry *root; return mount_single(fs_type, flags, data, pstore_fill_super);
root = mount_nodev(fs_type, flags, data, pstore_fill_super);
if (IS_ERR(root))
return -ENOMEM;
mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
pstore_mnt = mnt;
return 0;
} }
static void pstore_kill_sb(struct super_block *sb) static void pstore_kill_sb(struct super_block *sb)
{ {
kill_litter_super(sb); kill_litter_super(sb);
pstore_sb = NULL; pstore_sb = NULL;
pstore_mnt = NULL;
} }
static struct file_system_type pstore_fs_type = { static struct file_system_type pstore_fs_type = {
.name = "pstore", .name = "pstore",
.get_sb = pstore_get_sb, .mount = pstore_mount,
.kill_sb = pstore_kill_sb, .kill_sb = pstore_kill_sb,
}; };
......
...@@ -910,29 +910,18 @@ struct dentry *mount_single(struct file_system_type *fs_type, ...@@ -910,29 +910,18 @@ struct dentry *mount_single(struct file_system_type *fs_type,
} }
EXPORT_SYMBOL(mount_single); EXPORT_SYMBOL(mount_single);
struct vfsmount * struct dentry *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{ {
struct vfsmount *mnt;
struct dentry *root; struct dentry *root;
struct super_block *sb;
char *secdata = NULL; char *secdata = NULL;
int error; int error = -ENOMEM;
if (!type)
return ERR_PTR(-ENODEV);
error = -ENOMEM;
mnt = alloc_vfsmnt(name);
if (!mnt)
goto out;
if (flags & MS_KERNMOUNT)
mnt->mnt_flags = MNT_INTERNAL;
if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) { if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
secdata = alloc_secdata(); secdata = alloc_secdata();
if (!secdata) if (!secdata)
goto out_mnt; goto out;
error = security_sb_copy_data(data, secdata); error = security_sb_copy_data(data, secdata);
if (error) if (error)
...@@ -944,13 +933,12 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void ...@@ -944,13 +933,12 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
error = PTR_ERR(root); error = PTR_ERR(root);
goto out_free_secdata; goto out_free_secdata;
} }
mnt->mnt_root = root; sb = root->d_sb;
mnt->mnt_sb = root->d_sb; BUG_ON(!sb);
BUG_ON(!mnt->mnt_sb); WARN_ON(!sb->s_bdi);
WARN_ON(!mnt->mnt_sb->s_bdi); sb->s_flags |= MS_BORN;
mnt->mnt_sb->s_flags |= MS_BORN;
error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata); error = security_sb_kern_mount(sb, flags, secdata);
if (error) if (error)
goto out_sb; goto out_sb;
...@@ -961,27 +949,21 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void ...@@ -961,27 +949,21 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
* violate this rule. This warning should be either removed or * violate this rule. This warning should be either removed or
* converted to a BUG() in 2.6.34. * converted to a BUG() in 2.6.34.
*/ */
WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to " WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
"negative value (%lld)\n", type->name, mnt->mnt_sb->s_maxbytes); "negative value (%lld)\n", type->name, sb->s_maxbytes);
mnt->mnt_mountpoint = mnt->mnt_root; up_write(&sb->s_umount);
mnt->mnt_parent = mnt;
up_write(&mnt->mnt_sb->s_umount);
free_secdata(secdata); free_secdata(secdata);
return mnt; return root;
out_sb: out_sb:
dput(mnt->mnt_root); dput(root);
deactivate_locked_super(mnt->mnt_sb); deactivate_locked_super(sb);
out_free_secdata: out_free_secdata:
free_secdata(secdata); free_secdata(secdata);
out_mnt:
free_vfsmnt(mnt);
out: out:
return ERR_PTR(error); return ERR_PTR(error);
} }
EXPORT_SYMBOL_GPL(vfs_kern_mount);
/** /**
* freeze_super - lock the filesystem and force it into a consistent state * freeze_super - lock the filesystem and force it into a consistent state
* @sb: the super to lock * @sb: the super to lock
...@@ -1071,49 +1053,3 @@ int thaw_super(struct super_block *sb) ...@@ -1071,49 +1053,3 @@ int thaw_super(struct super_block *sb)
return 0; return 0;
} }
EXPORT_SYMBOL(thaw_super); EXPORT_SYMBOL(thaw_super);
static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
{
int err;
const char *subtype = strchr(fstype, '.');
if (subtype) {
subtype++;
err = -EINVAL;
if (!subtype[0])
goto err;
} else
subtype = "";
mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL);
err = -ENOMEM;
if (!mnt->mnt_sb->s_subtype)
goto err;
return mnt;
err:
mntput(mnt);
return ERR_PTR(err);
}
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
struct file_system_type *type = get_fs_type(fstype);
struct vfsmount *mnt;
if (!type)
return ERR_PTR(-ENODEV);
mnt = vfs_kern_mount(type, flags, name, data);
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
!mnt->mnt_sb->s_subtype)
mnt = fs_set_subtype(mnt, fstype);
put_filesystem(type);
return mnt;
}
EXPORT_SYMBOL_GPL(do_kern_mount);
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
{
return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
}
EXPORT_SYMBOL_GPL(kern_mount_data);
...@@ -168,7 +168,7 @@ struct dentry_operations { ...@@ -168,7 +168,7 @@ struct dentry_operations {
void (*d_iput)(struct dentry *, struct inode *); void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int); char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *); struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool, bool); int (*d_manage)(struct dentry *, bool);
} ____cacheline_aligned; } ____cacheline_aligned;
/* /*
......
...@@ -1839,7 +1839,6 @@ extern struct dentry *mount_pseudo(struct file_system_type *, char *, ...@@ -1839,7 +1839,6 @@ extern struct dentry *mount_pseudo(struct file_system_type *, char *,
const struct super_operations *ops, const struct super_operations *ops,
const struct dentry_operations *dops, const struct dentry_operations *dops,
unsigned long); unsigned long);
extern void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb);
static inline void sb_mark_dirty(struct super_block *sb) static inline void sb_mark_dirty(struct super_block *sb)
{ {
......
...@@ -85,7 +85,7 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry ...@@ -85,7 +85,7 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry
extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
extern int follow_down_one(struct path *); extern int follow_down_one(struct path *);
extern int follow_down(struct path *, bool); extern int follow_down(struct path *);
extern int follow_up(struct path *); extern int follow_up(struct path *);
extern struct dentry *lock_rename(struct dentry *, struct dentry *); extern struct dentry *lock_rename(struct dentry *, struct dentry *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment