]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'userns/for-next'
authorThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 13:02:30 +0000 (15:02 +0200)
committerThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 13:02:30 +0000 (15:02 +0200)
Conflicts:
fs/fuse/dir.c

fs/afs/dir.c
fs/dcache.c
fs/fuse/dir.c
fs/gfs2/dentry.c
fs/mount.h
fs/namei.c
fs/namespace.c
fs/nfs/dir.c
fs/sysfs/dir.c
include/linux/dcache.h

index 529300327f4574d2dc36345be3c7398442548e08..3756d4fe129fe13bf98995ce37e2321eff0b2fbf 100644 (file)
@@ -683,8 +683,7 @@ not_found:
 
 out_bad:
        /* don't unhash if we have submounts */
-       if (check_submounts_and_drop(dentry) != 0)
-               goto out_skip;
+       shrink_submounts_and_drop(dentry);
 
        _debug("dropping dentry %s/%s",
               parent->d_name.name, dentry->d_name.name);
index d70df2e0e0da8a6b41d9bfeba798fb4cc9481a80..093a68d5fec5d2129d271066b1f4e4cbc83286f3 100644 (file)
@@ -1372,7 +1372,7 @@ int d_set_mounted(struct dentry *dentry)
        int ret = -ENOENT;
        write_seqlock(&rename_lock);
        for (p = dentry->d_parent; !IS_ROOT(p); p = p->d_parent) {
-               /* Need exclusion wrt. check_submounts_and_drop() */
+               /* Need exclusion wrt. shrink_submounts_and_drop() */
                spin_lock(&p->d_lock);
                if (unlikely(d_unhashed(p))) {
                        spin_unlock(&p->d_lock);
@@ -1477,70 +1477,56 @@ void shrink_dcache_parent(struct dentry *parent)
 }
 EXPORT_SYMBOL(shrink_dcache_parent);
 
-static enum d_walk_ret check_and_collect(void *_data, struct dentry *dentry)
+struct detach_data {
+       struct dentry *found;
+};
+static enum d_walk_ret do_detach_submounts(void *ptr, struct dentry *dentry)
 {
-       struct select_data *data = _data;
-
-       if (d_mountpoint(dentry)) {
-               data->found = -EBUSY;
-               return D_WALK_QUIT;
-       }
-
-       return select_collect(_data, dentry);
-}
+       struct detach_data *data = ptr;
 
-static void check_and_drop(void *_data)
-{
-       struct select_data *data = _data;
+       if (d_mountpoint(dentry))
+               data->found = dentry;
 
-       if (d_mountpoint(data->start))
-               data->found = -EBUSY;
-       if (!data->found)
-               __d_drop(data->start);
+       return data->found ? D_WALK_QUIT : D_WALK_CONTINUE;
 }
 
 /**
- * check_submounts_and_drop - prune dcache, check for submounts and drop
+ * detach_submounts - check for submounts and detach them.
  *
- * All done as a single atomic operation relative to has_unlinked_ancestor().
- * Returns 0 if successfully unhashed @parent.  If there were submounts then
- * return -EBUSY.
+ * @dentry: dentry to find mount points under.
  *
- * @dentry: dentry to prune and drop
+ * If dentry or any of it's children is a mount point detach those mounts.
  */
-int check_submounts_and_drop(struct dentry *dentry)
+void detach_submounts(struct dentry *dentry)
 {
-       int ret = 0;
-
-       /* Negative dentries can be dropped without further checks */
-       if (!dentry->d_inode) {
-               d_drop(dentry);
-               goto out;
-       }
-
+       struct detach_data data;
        for (;;) {
-               struct select_data data;
-
-               INIT_LIST_HEAD(&data.dispose);
-               data.start = dentry;
-               data.found = 0;
+               data.found = NULL;
+               d_walk(dentry, &data, do_detach_submounts, NULL);
 
-               d_walk(dentry, &data, check_and_collect, check_and_drop);
-               ret = data.found;
-
-               if (!list_empty(&data.dispose))
-                       shrink_dentry_list(&data.dispose);
-
-               if (ret <= 0)
+               if (!data.found)
                        break;
 
+               detach_mounts(data.found);
                cond_resched();
        }
+       detach_mounts(dentry);
+}
 
-out:
-       return ret;
+/**
+ * shrink_submounts_and_drop - detach submounts, prune dcache, and drop
+ *
+ * All done as a single atomic operation reletaive to d_set_mounted().
+ *
+ * @dentry: dentry to detach, prune and drop
+ */
+void shrink_submounts_and_drop(struct dentry *dentry)
+{
+       d_drop(dentry);
+       detach_submounts(dentry);
+       shrink_dcache_parent(dentry);
 }
-EXPORT_SYMBOL(check_submounts_and_drop);
+EXPORT_SYMBOL(shrink_submounts_and_drop);
 
 /**
  * __d_alloc   -       allocate a dcache entry
index 0747f6eed59836954a23743cddde06fef4d8c05c..3959aa981f17a3a23da34d3cc23645b57b22b321 100644 (file)
@@ -264,8 +264,9 @@ out:
 invalid:
        ret = 0;
 
-       if (!(flags & LOOKUP_RCU) && check_submounts_and_drop(entry) != 0)
-               ret = 1;
+       if (!(flags & LOOKUP_RCU))
+               shrink_submounts_and_drop(entry);
+
        goto out;
 }
 
index d3a5d4e29ba5f37b10f4f0fd0043c7e9847b9923..2ecc2b873829efc8b2eca4d1161f8fa991a8e753 100644 (file)
@@ -93,9 +93,7 @@ invalid_gunlock:
        if (!had_lock)
                gfs2_glock_dq_uninit(&d_gh);
 invalid:
-       if (check_submounts_and_drop(dentry) != 0)
-               goto valid;
-
+       shrink_submounts_and_drop(dentry);
        dput(parent);
        return 0;
 
index 64a858143ff923ab80499d5a865bde9de2257314..7a6a2bb3f290c036c0c30eefdbf0698d523a381e 100644 (file)
@@ -21,6 +21,7 @@ struct mnt_pcp {
 struct mountpoint {
        struct list_head m_hash;
        struct dentry *m_dentry;
+       struct list_head m_list;
        int m_count;
 };
 
@@ -47,6 +48,7 @@ struct mount {
        struct mount *mnt_master;       /* slave is on master->mnt_slave_list */
        struct mnt_namespace *mnt_ns;   /* containing namespace */
        struct mountpoint *mnt_mp;      /* where is it mounted */
+       struct list_head mnt_mp_list;   /* list mounts with the same mountpoint */
 #ifdef CONFIG_FSNOTIFY
        struct hlist_head mnt_fsnotify_marks;
        __u32 mnt_fsnotify_mask;
@@ -77,6 +79,7 @@ static inline int is_mounted(struct vfsmount *mnt)
 }
 
 extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *, int);
+extern void detach_mounts(struct dentry *dentry);
 
 static inline void get_mnt_ns(struct mnt_namespace *ns)
 {
index caa28051e197e898e3c2bc52afce37bcbb284853..f892ef9b3ead08b6ffea75833349d58d889e42c6 100644 (file)
@@ -3548,6 +3548,20 @@ void dentry_unhash(struct dentry *dentry)
        spin_unlock(&dentry->d_lock);
 }
 
+static bool covered(struct vfsmount *mnt, struct dentry *dentry)
+{
+       /* test to see if a dentry is covered with a mount in
+        * the current mount namespace.
+        */
+       bool is_covered;
+
+       rcu_read_lock();
+       is_covered = d_mountpoint(dentry) && __lookup_mnt(mnt, dentry, 1);
+       rcu_read_unlock();
+
+       return is_covered;
+}
+
 int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
        int error = may_delete(dir, dentry, 1);
@@ -3561,10 +3575,6 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        dget(dentry);
        mutex_lock(&dentry->d_inode->i_mutex);
 
-       error = -EBUSY;
-       if (d_mountpoint(dentry))
-               goto out;
-
        error = security_inode_rmdir(dir, dentry);
        if (error)
                goto out;
@@ -3576,6 +3586,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 
        dentry->d_inode->i_flags |= S_DEAD;
        dont_mount(dentry);
+       detach_mounts(dentry);
 
 out:
        mutex_unlock(&dentry->d_inode->i_mutex);
@@ -3623,6 +3634,9 @@ retry:
                error = -ENOENT;
                goto exit3;
        }
+       error = -EBUSY;
+       if (covered(nd.path.mnt, dentry))
+               goto exit3;
        error = security_path_rmdir(&nd.path, dentry);
        if (error)
                goto exit3;
@@ -3658,14 +3672,12 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
                return -EPERM;
 
        mutex_lock(&dentry->d_inode->i_mutex);
-       if (d_mountpoint(dentry))
-               error = -EBUSY;
-       else {
-               error = security_inode_unlink(dir, dentry);
+       error = security_inode_unlink(dir, dentry);
+       if (!error) {
+               error = dir->i_op->unlink(dir, dentry);
                if (!error) {
-                       error = dir->i_op->unlink(dir, dentry);
-                       if (!error)
-                               dont_mount(dentry);
+                       dont_mount(dentry);
+                       detach_mounts(dentry);
                }
        }
        mutex_unlock(&dentry->d_inode->i_mutex);
@@ -3717,6 +3729,9 @@ retry:
                inode = dentry->d_inode;
                if (!inode)
                        goto slashes;
+               error = -EBUSY;
+               if (covered(nd.path.mnt, dentry))
+                       goto exit2;
                ihold(inode);
                error = security_path_unlink(&nd.path, dentry);
                if (error)
@@ -3989,10 +4004,6 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
        if (target)
                mutex_lock(&target->i_mutex);
 
-       error = -EBUSY;
-       if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
-               goto out;
-
        error = -EMLINK;
        if (max_links && !target && new_dir != old_dir &&
            new_dir->i_nlink >= max_links)
@@ -4007,6 +4018,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
        if (target) {
                target->i_flags |= S_DEAD;
                dont_mount(new_dentry);
+               detach_mounts(new_dentry);
        }
 out:
        if (target)
@@ -4032,16 +4044,14 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
        if (target)
                mutex_lock(&target->i_mutex);
 
-       error = -EBUSY;
-       if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
-               goto out;
-
        error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
        if (error)
                goto out;
 
-       if (target)
+       if (target) {
                dont_mount(new_dentry);
+               detach_mounts(new_dentry);
+       }
        if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
                d_move(old_dentry, new_dentry);
 out:
@@ -4165,6 +4175,11 @@ retry:
        error = -ENOTEMPTY;
        if (new_dentry == trap)
                goto exit5;
+       error = -EBUSY;
+       if (covered(oldnd.path.mnt, old_dentry))
+               goto exit5;
+       if (covered(newnd.path.mnt, new_dentry))
+               goto exit5;
 
        error = security_path_rename(&oldnd.path, old_dentry,
                                     &newnd.path, new_dentry);
index 3ee6e59ead55a9ca2398a30b86f347315532a8b5..ca81c46ea649831ea3767ba114ba9a5c360df1a9 100644 (file)
@@ -197,6 +197,7 @@ static struct mount *alloc_vfsmnt(const char *name)
                INIT_LIST_HEAD(&mnt->mnt_share);
                INIT_LIST_HEAD(&mnt->mnt_slave_list);
                INIT_LIST_HEAD(&mnt->mnt_slave);
+               INIT_LIST_HEAD(&mnt->mnt_mp_list);
 #ifdef CONFIG_FSNOTIFY
                INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
 #endif
@@ -636,6 +637,7 @@ static struct mountpoint *new_mountpoint(struct dentry *dentry)
        mp->m_dentry = dentry;
        mp->m_count = 1;
        list_add(&mp->m_hash, chain);
+       INIT_LIST_HEAD(&mp->m_list);
        return mp;
 }
 
@@ -643,6 +645,7 @@ static void put_mountpoint(struct mountpoint *mp)
 {
        if (!--mp->m_count) {
                struct dentry *dentry = mp->m_dentry;
+               BUG_ON(!list_empty(&mp->m_list));
                spin_lock(&dentry->d_lock);
                dentry->d_flags &= ~DCACHE_MOUNTED;
                spin_unlock(&dentry->d_lock);
@@ -689,6 +692,7 @@ static void detach_mnt(struct mount *mnt, struct path *old_path)
        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
        list_del_init(&mnt->mnt_child);
        list_del_init(&mnt->mnt_hash);
+       list_del_init(&mnt->mnt_mp_list);
        put_mountpoint(mnt->mnt_mp);
        mnt->mnt_mp = NULL;
 }
@@ -705,6 +709,7 @@ void mnt_set_mountpoint(struct mount *mnt,
        child_mnt->mnt_mountpoint = dget(mp->m_dentry);
        child_mnt->mnt_parent = mnt;
        child_mnt->mnt_mp = mp;
+       list_add_tail(&child_mnt->mnt_mp_list, &mp->m_list);
 }
 
 /*
@@ -1191,6 +1196,7 @@ void umount_tree(struct mount *mnt, int propagate)
                list_del_init(&p->mnt_child);
                if (mnt_has_parent(p)) {
                        p->mnt_parent->mnt_ghosts++;
+                       list_del_init(&p->mnt_mp_list);
                        put_mountpoint(p->mnt_mp);
                        p->mnt_mp = NULL;
                }
@@ -1289,6 +1295,30 @@ static int do_umount(struct mount *mnt, int flags)
        return retval;
 }
 
+void detach_mounts(struct dentry *dentry)
+{
+       struct mountpoint *mp;
+       struct mount *mnt;
+
+       namespace_lock();
+       if (!d_mountpoint(dentry))
+               goto out_unlock;
+
+       mp = new_mountpoint(dentry);
+       if (IS_ERR(mp))
+               goto out_unlock;
+
+       br_write_lock(&vfsmount_lock);
+       while (!list_empty(&mp->m_list)) {
+               mnt = list_first_entry(&mp->m_list, struct mount, mnt_mp_list);
+               umount_tree(mnt, 1);
+       }
+       br_write_unlock(&vfsmount_lock);
+       put_mountpoint(mp);
+out_unlock:
+       namespace_unlock();
+}
+
 /* 
  * Is the caller allowed to modify his namespace?
  */
index 76548d81f926c8843a291350a080f09938c0d0fd..c20fffac621e0f4032ae066c4cc884f800b5f684 100644 (file)
@@ -1128,10 +1128,7 @@ out_zap_parent:
                if (dentry->d_flags & DCACHE_DISCONNECTED)
                        goto out_valid;
        }
-       /* If we have submounts, don't unhash ! */
-       if (check_submounts_and_drop(dentry) != 0)
-               goto out_valid;
-
+       shrink_submounts_and_drop(dentry);
        dput(parent);
        dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is invalid\n",
                        __func__, dentry);
index eab59de47556568e522c66e83bd0844de4cd5146..11837b73abbfac18fdc733b41808dac2800d6af9 100644 (file)
@@ -315,7 +315,6 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
                goto out_bad;
 
        mutex_unlock(&sysfs_mutex);
-out_valid:
        return 1;
 out_bad:
        /* Remove the dentry from the dcache hashes.
@@ -329,13 +328,7 @@ out_bad:
         * to the dcache hashes.
         */
        mutex_unlock(&sysfs_mutex);
-
-       /* If we have submounts we must allow the vfs caches
-        * to lie about the state of the filesystem to prevent
-        * leaks and other nasty things.
-        */
-       if (check_submounts_and_drop(dentry) != 0)
-               goto out_valid;
+       shrink_submounts_and_drop(dentry);
 
        return 0;
 }
index 716c3760ee3970908577ccdb4ea32fa0275d7e33..ae57848f92d924255e417ede6c38ceb258fc9d1c 100644 (file)
@@ -255,7 +255,8 @@ extern void d_prune_aliases(struct inode *);
 
 /* test whether we have any submounts in a subdir tree */
 extern int have_submounts(struct dentry *);
-extern int check_submounts_and_drop(struct dentry *);
+extern void detach_submounts(struct dentry *dentry);
+extern void shrink_submounts_and_drop(struct dentry *);
 
 /*
  * This adds the entry to the hash queues.