]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 10 Sep 2013 19:44:24 +0000 (12:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 10 Sep 2013 19:44:24 +0000 (12:44 -0700)
Pull vfs pile 3 (of many) from Al Viro:
 "Waiman's conversion of d_path() and bits related to it,
  kern_path_mountpoint(), several cleanups and fixes (exportfs
  one is -stable fodder, IMO).

  There definitely will be more...  ;-/"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  split read_seqretry_or_unlock(), convert d_walk() to resulting primitives
  dcache: Translating dentry into pathname without taking rename_lock
  autofs4 - fix device ioctl mount lookup
  introduce kern_path_mountpoint()
  rename user_path_umountat() to user_path_mountpoint_at()
  take unlazy_walk() into umount_lookup_last()
  Kill indirect include of file.h from eventfd.h, use fdget() in cgroup.c
  prune_super(): sb->s_op is never NULL
  exportfs: don't assume that ->iterate() won't feed us too long entries
  afs: get rid of redundant ->d_name.len checks

fs/afs/dir.c
fs/autofs4/dev-ioctl.c
fs/dcache.c
fs/exportfs/expfs.c
fs/internal.h
fs/namei.c
fs/namespace.c
fs/super.c
include/linux/eventfd.h
include/linux/namei.h
kernel/cgroup.c

index 0b74d3176ab7c9293c0583e1b2cff730e7f4df0d..646337dc5201e702227309cc17db99f96af99173 100644 (file)
@@ -751,10 +751,6 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        _enter("{%x:%u},{%s},%ho",
               dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode);
 
-       ret = -ENAMETOOLONG;
-       if (dentry->d_name.len >= AFSNAMEMAX)
-               goto error;
-
        key = afs_request_key(dvnode->volume->cell);
        if (IS_ERR(key)) {
                ret = PTR_ERR(key);
@@ -816,10 +812,6 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
        _enter("{%x:%u},{%s}",
               dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name);
 
-       ret = -ENAMETOOLONG;
-       if (dentry->d_name.len >= AFSNAMEMAX)
-               goto error;
-
        key = afs_request_key(dvnode->volume->cell);
        if (IS_ERR(key)) {
                ret = PTR_ERR(key);
@@ -936,10 +928,6 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
        _enter("{%x:%u},{%s},%ho,",
               dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode);
 
-       ret = -ENAMETOOLONG;
-       if (dentry->d_name.len >= AFSNAMEMAX)
-               goto error;
-
        key = afs_request_key(dvnode->volume->cell);
        if (IS_ERR(key)) {
                ret = PTR_ERR(key);
@@ -1005,10 +993,6 @@ static int afs_link(struct dentry *from, struct inode *dir,
               dvnode->fid.vid, dvnode->fid.vnode,
               dentry->d_name.name);
 
-       ret = -ENAMETOOLONG;
-       if (dentry->d_name.len >= AFSNAMEMAX)
-               goto error;
-
        key = afs_request_key(dvnode->volume->cell);
        if (IS_ERR(key)) {
                ret = PTR_ERR(key);
@@ -1053,10 +1037,6 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
               dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name,
               content);
 
-       ret = -ENAMETOOLONG;
-       if (dentry->d_name.len >= AFSNAMEMAX)
-               goto error;
-
        ret = -EINVAL;
        if (strlen(content) >= AFSPATHMAX)
                goto error;
@@ -1127,10 +1107,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
               new_dvnode->fid.vid, new_dvnode->fid.vnode,
               new_dentry->d_name.name);
 
-       ret = -ENAMETOOLONG;
-       if (new_dentry->d_name.len >= AFSNAMEMAX)
-               goto error;
-
        key = afs_request_key(orig_dvnode->volume->cell);
        if (IS_ERR(key)) {
                ret = PTR_ERR(key);
index 743c7c2c949d2571c1c0ae0571c604d516c914e4..0f00da329e718ab465ddb1488dda626db71cca23 100644 (file)
@@ -183,13 +183,14 @@ static int autofs_dev_ioctl_protosubver(struct file *fp,
        return 0;
 }
 
+/* Find the topmost mount satisfying test() */
 static int find_autofs_mount(const char *pathname,
                             struct path *res,
                             int test(struct path *path, void *data),
                             void *data)
 {
        struct path path;
-       int err = kern_path(pathname, 0, &path);
+       int err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0);
        if (err)
                return err;
        err = -ENOENT;
@@ -197,10 +198,9 @@ static int find_autofs_mount(const char *pathname,
                if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) {
                        if (test(&path, data)) {
                                path_get(&path);
-                               if (!err) /* already found some */
-                                       path_put(res);
                                *res = path;
                                err = 0;
+                               break;
                        }
                }
                if (!follow_up(&path))
@@ -486,12 +486,11 @@ static int autofs_dev_ioctl_askumount(struct file *fp,
  * mount if there is one or 0 if it isn't a mountpoint.
  *
  * If we aren't supplied with a file descriptor then we
- * lookup the nameidata of the path and check if it is the
- * root of a mount. If a type is given we are looking for
- * a particular autofs mount and if we don't find a match
- * we return fail. If the located nameidata path is the
- * root of a mount we return 1 along with the super magic
- * of the mount or 0 otherwise.
+ * lookup the path and check if it is the root of a mount.
+ * If a type is given we are looking for a particular autofs
+ * mount and if we don't find a match we return fail. If the
+ * located path is the root of a mount we return 1 along with
+ * the super magic of the mount or 0 otherwise.
  *
  * In both cases the the device number (as returned by
  * new_encode_dev()) is also returned.
@@ -519,9 +518,11 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
 
        if (!fp || param->ioctlfd == -1) {
                if (autofs_type_any(type))
-                       err = kern_path(name, LOOKUP_FOLLOW, &path);
+                       err = kern_path_mountpoint(AT_FDCWD,
+                                                  name, &path, LOOKUP_FOLLOW);
                else
-                       err = find_autofs_mount(name, &path, test_by_type, &type);
+                       err = find_autofs_mount(name, &path,
+                                               test_by_type, &type);
                if (err)
                        goto out;
                devid = new_encode_dev(path.dentry->d_sb->s_dev);
index ca8e9cd60f87ec1770af68bba09731c0227f42de..4d9df3c940e697603ca41aec60f7fc97c3361ada 100644 (file)
@@ -88,6 +88,35 @@ EXPORT_SYMBOL(rename_lock);
 
 static struct kmem_cache *dentry_cache __read_mostly;
 
+/**
+ * read_seqbegin_or_lock - begin a sequence number check or locking block
+ * lock: sequence lock
+ * seq : sequence number to be checked
+ *
+ * First try it once optimistically without taking the lock. If that fails,
+ * take the lock. The sequence number is also used as a marker for deciding
+ * whether to be a reader (even) or writer (odd).
+ * N.B. seq must be initialized to an even number to begin with.
+ */
+static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq)
+{
+       if (!(*seq & 1))        /* Even */
+               *seq = read_seqbegin(lock);
+       else                    /* Odd */
+               write_seqlock(lock);
+}
+
+static inline int need_seqretry(seqlock_t *lock, int seq)
+{
+       return !(seq & 1) && read_seqretry(lock, seq);
+}
+
+static inline void done_seqretry(seqlock_t *lock, int seq)
+{
+       if (seq & 1)
+               write_sequnlock(lock);
+}
+
 /*
  * This is the single most critical data structure when it comes
  * to the dcache: the hashtable for lookups. Somebody should try
@@ -1012,7 +1041,7 @@ void shrink_dcache_for_umount(struct super_block *sb)
  * the parenthood after dropping the lock and check
  * that the sequence number still matches.
  */
-static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq)
+static struct dentry *try_to_ascend(struct dentry *old, unsigned seq)
 {
        struct dentry *new = old->d_parent;
 
@@ -1026,7 +1055,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq
         */
        if (new != old->d_parent ||
                 (old->d_flags & DCACHE_DENTRY_KILLED) ||
-                (!locked && read_seqretry(&rename_lock, seq))) {
+                need_seqretry(&rename_lock, seq)) {
                spin_unlock(&new->d_lock);
                new = NULL;
        }
@@ -1063,13 +1092,12 @@ static void d_walk(struct dentry *parent, void *data,
 {
        struct dentry *this_parent;
        struct list_head *next;
-       unsigned seq;
-       int locked = 0;
+       unsigned seq = 0;
        enum d_walk_ret ret;
        bool retry = true;
 
-       seq = read_seqbegin(&rename_lock);
 again:
+       read_seqbegin_or_lock(&rename_lock, &seq);
        this_parent = parent;
        spin_lock(&this_parent->d_lock);
 
@@ -1123,13 +1151,13 @@ resume:
         */
        if (this_parent != parent) {
                struct dentry *child = this_parent;
-               this_parent = try_to_ascend(this_parent, locked, seq);
+               this_parent = try_to_ascend(this_parent, seq);
                if (!this_parent)
                        goto rename_retry;
                next = child->d_u.d_child.next;
                goto resume;
        }
-       if (!locked && read_seqretry(&rename_lock, seq)) {
+       if (need_seqretry(&rename_lock, seq)) {
                spin_unlock(&this_parent->d_lock);
                goto rename_retry;
        }
@@ -1138,17 +1166,13 @@ resume:
 
 out_unlock:
        spin_unlock(&this_parent->d_lock);
-       if (locked)
-               write_sequnlock(&rename_lock);
+       done_seqretry(&rename_lock, seq);
        return;
 
 rename_retry:
        if (!retry)
                return;
-       if (locked)
-               goto again;
-       locked = 1;
-       write_seqlock(&rename_lock);
+       seq = 1;
        goto again;
 }
 
@@ -2647,9 +2671,39 @@ static int prepend(char **buffer, int *buflen, const char *str, int namelen)
        return 0;
 }
 
+/**
+ * prepend_name - prepend a pathname in front of current buffer pointer
+ * buffer: buffer pointer
+ * buflen: allocated length of the buffer
+ * name:   name string and length qstr structure
+ *
+ * With RCU path tracing, it may race with d_move(). Use ACCESS_ONCE() to
+ * make sure that either the old or the new name pointer and length are
+ * fetched. However, there may be mismatch between length and pointer.
+ * The length cannot be trusted, we need to copy it byte-by-byte until
+ * the length is reached or a null byte is found. It also prepends "/" at
+ * the beginning of the name. The sequence number check at the caller will
+ * retry it again when a d_move() does happen. So any garbage in the buffer
+ * due to mismatched pointer and length will be discarded.
+ */
 static int prepend_name(char **buffer, int *buflen, struct qstr *name)
 {
-       return prepend(buffer, buflen, name->name, name->len);
+       const char *dname = ACCESS_ONCE(name->name);
+       u32 dlen = ACCESS_ONCE(name->len);
+       char *p;
+
+       if (*buflen < dlen + 1)
+               return -ENAMETOOLONG;
+       *buflen -= dlen + 1;
+       p = *buffer -= dlen + 1;
+       *p++ = '/';
+       while (dlen--) {
+               char c = *dname++;
+               if (!c)
+                       break;
+               *p++ = c;
+       }
+       return 0;
 }
 
 /**
@@ -2659,7 +2713,14 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
  * @buffer: pointer to the end of the buffer
  * @buflen: pointer to buffer length
  *
- * Caller holds the rename_lock.
+ * The function tries to write out the pathname without taking any lock other
+ * than the RCU read lock to make sure that dentries won't go away. It only
+ * checks the sequence number of the global rename_lock as any change in the
+ * dentry's d_seq will be preceded by changes in the rename_lock sequence
+ * number. If the sequence number had been change, it will restart the whole
+ * pathname back-tracing sequence again. It performs a total of 3 trials of
+ * lockless back-tracing sequences before falling back to take the
+ * rename_lock.
  */
 static int prepend_path(const struct path *path,
                        const struct path *root,
@@ -2668,54 +2729,66 @@ static int prepend_path(const struct path *path,
        struct dentry *dentry = path->dentry;
        struct vfsmount *vfsmnt = path->mnt;
        struct mount *mnt = real_mount(vfsmnt);
-       bool slash = false;
        int error = 0;
+       unsigned seq = 0;
+       char *bptr;
+       int blen;
 
+       rcu_read_lock();
+restart:
+       bptr = *buffer;
+       blen = *buflen;
+       read_seqbegin_or_lock(&rename_lock, &seq);
        while (dentry != root->dentry || vfsmnt != root->mnt) {
                struct dentry * parent;
 
                if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
                        /* Global root? */
-                       if (!mnt_has_parent(mnt))
-                               goto global_root;
-                       dentry = mnt->mnt_mountpoint;
-                       mnt = mnt->mnt_parent;
-                       vfsmnt = &mnt->mnt;
-                       continue;
+                       if (mnt_has_parent(mnt)) {
+                               dentry = mnt->mnt_mountpoint;
+                               mnt = mnt->mnt_parent;
+                               vfsmnt = &mnt->mnt;
+                               continue;
+                       }
+                       /*
+                        * Filesystems needing to implement special "root names"
+                        * should do so with ->d_dname()
+                        */
+                       if (IS_ROOT(dentry) &&
+                          (dentry->d_name.len != 1 ||
+                           dentry->d_name.name[0] != '/')) {
+                               WARN(1, "Root dentry has weird name <%.*s>\n",
+                                    (int) dentry->d_name.len,
+                                    dentry->d_name.name);
+                       }
+                       if (!error)
+                               error = is_mounted(vfsmnt) ? 1 : 2;
+                       break;
                }
                parent = dentry->d_parent;
                prefetch(parent);
-               spin_lock(&dentry->d_lock);
-               error = prepend_name(buffer, buflen, &dentry->d_name);
-               spin_unlock(&dentry->d_lock);
-               if (!error)
-                       error = prepend(buffer, buflen, "/", 1);
+               error = prepend_name(&bptr, &blen, &dentry->d_name);
                if (error)
                        break;
 
-               slash = true;
                dentry = parent;
        }
+       if (!(seq & 1))
+               rcu_read_unlock();
+       if (need_seqretry(&rename_lock, seq)) {
+               seq = 1;
+               goto restart;
+       }
+       done_seqretry(&rename_lock, seq);
 
-       if (!error && !slash)
-               error = prepend(buffer, buflen, "/", 1);
-
-       return error;
-
-global_root:
-       /*
-        * Filesystems needing to implement special "root names"
-        * should do so with ->d_dname()
-        */
-       if (IS_ROOT(dentry) &&
-           (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) {
-               WARN(1, "Root dentry has weird name <%.*s>\n",
-                    (int) dentry->d_name.len, dentry->d_name.name);
-       }
-       if (!slash)
-               error = prepend(buffer, buflen, "/", 1);
-       if (!error)
-               error = is_mounted(vfsmnt) ? 1 : 2;
+       if (error >= 0 && bptr == *buffer) {
+               if (--blen < 0)
+                       error = -ENAMETOOLONG;
+               else
+                       *--bptr = '/';
+       }
+       *buffer = bptr;
+       *buflen = blen;
        return error;
 }
 
@@ -2744,9 +2817,7 @@ char *__d_path(const struct path *path,
 
        prepend(&res, &buflen, "\0", 1);
        br_read_lock(&vfsmount_lock);
-       write_seqlock(&rename_lock);
        error = prepend_path(path, root, &res, &buflen);
-       write_sequnlock(&rename_lock);
        br_read_unlock(&vfsmount_lock);
 
        if (error < 0)
@@ -2765,9 +2836,7 @@ char *d_absolute_path(const struct path *path,
 
        prepend(&res, &buflen, "\0", 1);
        br_read_lock(&vfsmount_lock);
-       write_seqlock(&rename_lock);
        error = prepend_path(path, &root, &res, &buflen);
-       write_sequnlock(&rename_lock);
        br_read_unlock(&vfsmount_lock);
 
        if (error > 1)
@@ -2833,9 +2902,7 @@ char *d_path(const struct path *path, char *buf, int buflen)
 
        get_fs_root(current->fs, &root);
        br_read_lock(&vfsmount_lock);
-       write_seqlock(&rename_lock);
        error = path_with_deleted(path, &root, &res, &buflen);
-       write_sequnlock(&rename_lock);
        br_read_unlock(&vfsmount_lock);
        if (error < 0)
                res = ERR_PTR(error);
@@ -2870,10 +2937,10 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
        char *end = buffer + buflen;
        /* these dentries are never renamed, so d_lock is not needed */
        if (prepend(&end, &buflen, " (deleted)", 11) ||
-           prepend_name(&end, &buflen, &dentry->d_name) ||
+           prepend(&end, &buflen, dentry->d_name.name, dentry->d_name.len) ||
            prepend(&end, &buflen, "/", 1))  
                end = ERR_PTR(-ENAMETOOLONG);
-       return end;  
+       return end;
 }
 
 /*
@@ -2881,30 +2948,42 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
  */
 static char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
 {
-       char *end = buf + buflen;
-       char *retval;
+       char *end, *retval;
+       int len, seq = 0;
+       int error = 0;
 
-       prepend(&end, &buflen, "\0", 1);
+       rcu_read_lock();
+restart:
+       end = buf + buflen;
+       len = buflen;
+       prepend(&end, &len, "\0", 1);
        if (buflen < 1)
                goto Elong;
        /* Get '/' right */
        retval = end-1;
        *retval = '/';
-
+       read_seqbegin_or_lock(&rename_lock, &seq);
        while (!IS_ROOT(dentry)) {
                struct dentry *parent = dentry->d_parent;
                int error;
 
                prefetch(parent);
-               spin_lock(&dentry->d_lock);
-               error = prepend_name(&end, &buflen, &dentry->d_name);
-               spin_unlock(&dentry->d_lock);
-               if (error != 0 || prepend(&end, &buflen, "/", 1) != 0)
-                       goto Elong;
+               error = prepend_name(&end, &len, &dentry->d_name);
+               if (error)
+                       break;
 
                retval = end;
                dentry = parent;
        }
+       if (!(seq & 1))
+               rcu_read_unlock();
+       if (need_seqretry(&rename_lock, seq)) {
+               seq = 1;
+               goto restart;
+       }
+       done_seqretry(&rename_lock, seq);
+       if (error)
+               goto Elong;
        return retval;
 Elong:
        return ERR_PTR(-ENAMETOOLONG);
@@ -2912,13 +2991,7 @@ Elong:
 
 char *dentry_path_raw(struct dentry *dentry, char *buf, int buflen)
 {
-       char *retval;
-
-       write_seqlock(&rename_lock);
-       retval = __dentry_path(dentry, buf, buflen);
-       write_sequnlock(&rename_lock);
-
-       return retval;
+       return __dentry_path(dentry, buf, buflen);
 }
 EXPORT_SYMBOL(dentry_path_raw);
 
@@ -2927,7 +3000,6 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen)
        char *p = NULL;
        char *retval;
 
-       write_seqlock(&rename_lock);
        if (d_unlinked(dentry)) {
                p = buf + buflen;
                if (prepend(&p, &buflen, "//deleted", 10) != 0)
@@ -2935,7 +3007,6 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen)
                buflen++;
        }
        retval = __dentry_path(dentry, buf, buflen);
-       write_sequnlock(&rename_lock);
        if (!IS_ERR(retval) && p)
                *p = '/';       /* restore '/' overriden with '\0' */
        return retval;
@@ -2974,7 +3045,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
 
        error = -ENOENT;
        br_read_lock(&vfsmount_lock);
-       write_seqlock(&rename_lock);
        if (!d_unlinked(pwd.dentry)) {
                unsigned long len;
                char *cwd = page + PAGE_SIZE;
@@ -2982,7 +3052,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
 
                prepend(&cwd, &buflen, "\0", 1);
                error = prepend_path(&pwd, &root, &cwd, &buflen);
-               write_sequnlock(&rename_lock);
                br_read_unlock(&vfsmount_lock);
 
                if (error < 0)
@@ -3003,7 +3072,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
                                error = -EFAULT;
                }
        } else {
-               write_sequnlock(&rename_lock);
                br_read_unlock(&vfsmount_lock);
        }
 
index 293bc2e47a735807a75eaad424764315172367b6..a235f0016889b557e06d6273f0f3dca7d873a608 100644 (file)
@@ -231,7 +231,7 @@ static int filldir_one(void * __buf, const char * name, int len,
        int result = 0;
 
        buf->sequence++;
-       if (buf->ino == ino) {
+       if (buf->ino == ino && len <= NAME_MAX) {
                memcpy(buf->name, name, len);
                buf->name[len] = '\0';
                buf->found = 1;
index d208937955265998fd4e02ff1bfeaaa0807dfd3e..2be46ea5dd0bf27af5002057f1b20399afee6e69 100644 (file)
@@ -45,6 +45,9 @@ extern void __init chrdev_init(void);
  * namei.c
  */
 extern int __inode_permission(struct inode *, int);
+extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *);
+extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
+                          const char *, unsigned int, struct path *);
 
 /*
  * namespace.c
index 5e6aaadc1dcd994df52a7fcfdc322d0f508cba6f..409a441ba2ae1c3b52f2b6d87563a9b0e80125b4 100644 (file)
@@ -2209,7 +2209,7 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
 }
 
 /**
- * umount_lookup_last - look up last component for umount
+ * mountpoint_last - look up last component for umount
  * @nd:   pathwalk nameidata - currently pointing at parent directory of "last"
  * @path: pointer to container for result
  *
@@ -2236,25 +2236,28 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
  *         to the link, and nd->path will *not* be put.
  */
 static int
-umount_lookup_last(struct nameidata *nd, struct path *path)
+mountpoint_last(struct nameidata *nd, struct path *path)
 {
        int error = 0;
        struct dentry *dentry;
        struct dentry *dir = nd->path.dentry;
 
-       if (unlikely(nd->flags & LOOKUP_RCU)) {
-               WARN_ON_ONCE(1);
-               error = -ECHILD;
-               goto error_check;
+       /* If we're in rcuwalk, drop out of it to handle last component */
+       if (nd->flags & LOOKUP_RCU) {
+               if (unlazy_walk(nd, NULL)) {
+                       error = -ECHILD;
+                       goto out;
+               }
        }
 
        nd->flags &= ~LOOKUP_PARENT;
 
        if (unlikely(nd->last_type != LAST_NORM)) {
                error = handle_dots(nd, nd->last_type);
-               if (!error)
-                       dentry = dget(nd->path.dentry);
-               goto error_check;
+               if (error)
+                       goto out;
+               dentry = dget(nd->path.dentry);
+               goto done;
        }
 
        mutex_lock(&dir->d_inode->i_mutex);
@@ -2268,44 +2271,43 @@ umount_lookup_last(struct nameidata *nd, struct path *path)
                dentry = d_alloc(dir, &nd->last);
                if (!dentry) {
                        error = -ENOMEM;
-               } else {
-                       dentry = lookup_real(dir->d_inode, dentry, nd->flags);
-                       if (IS_ERR(dentry))
-                               error = PTR_ERR(dentry);
+                       goto out;
                }
+               dentry = lookup_real(dir->d_inode, dentry, nd->flags);
+               error = PTR_ERR(dentry);
+               if (IS_ERR(dentry))
+                       goto out;
        }
        mutex_unlock(&dir->d_inode->i_mutex);
 
-error_check:
-       if (!error) {
-               if (!dentry->d_inode) {
-                       error = -ENOENT;
-                       dput(dentry);
-               } else {
-                       path->dentry = dentry;
-                       path->mnt = mntget(nd->path.mnt);
-                       if (should_follow_link(dentry->d_inode,
-                                               nd->flags & LOOKUP_FOLLOW))
-                               return 1;
-                       follow_mount(path);
-               }
+done:
+       if (!dentry->d_inode) {
+               error = -ENOENT;
+               dput(dentry);
+               goto out;
        }
+       path->dentry = dentry;
+       path->mnt = mntget(nd->path.mnt);
+       if (should_follow_link(dentry->d_inode, nd->flags & LOOKUP_FOLLOW))
+               return 1;
+       follow_mount(path);
+       error = 0;
+out:
        terminate_walk(nd);
        return error;
 }
 
 /**
- * path_umountat - look up a path to be umounted
+ * path_mountpoint - look up a path to be umounted
  * @dfd:       directory file descriptor to start walk from
  * @name:      full pathname to walk
  * @flags:     lookup flags
- * @nd:                pathwalk nameidata
  *
  * Look up the given name, but don't attempt to revalidate the last component.
  * Returns 0 and "path" will be valid on success; Retuns error otherwise.
  */
 static int
-path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
+path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags)
 {
        struct file *base = NULL;
        struct nameidata nd;
@@ -2320,16 +2322,7 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
        if (err)
                goto out;
 
-       /* If we're in rcuwalk, drop out of it to handle last component */
-       if (nd.flags & LOOKUP_RCU) {
-               err = unlazy_walk(&nd, NULL);
-               if (err) {
-                       terminate_walk(&nd);
-                       goto out;
-               }
-       }
-
-       err = umount_lookup_last(&nd, path);
+       err = mountpoint_last(&nd, path);
        while (err > 0) {
                void *cookie;
                struct path link = *path;
@@ -2340,7 +2333,7 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
                err = follow_link(&link, &nd, &cookie);
                if (err)
                        break;
-               err = umount_lookup_last(&nd, path);
+               err = mountpoint_last(&nd, path);
                put_link(&nd, &link, cookie);
        }
 out:
@@ -2353,8 +2346,22 @@ out:
        return err;
 }
 
+static int
+filename_mountpoint(int dfd, struct filename *s, struct path *path,
+                       unsigned int flags)
+{
+       int error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU);
+       if (unlikely(error == -ECHILD))
+               error = path_mountpoint(dfd, s->name, path, flags);
+       if (unlikely(error == -ESTALE))
+               error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_REVAL);
+       if (likely(!error))
+               audit_inode(s, path->dentry, 0);
+       return error;
+}
+
 /**
- * user_path_umountat - lookup a path from userland in order to umount it
+ * user_path_mountpoint_at - lookup a path from userland in order to umount it
  * @dfd:       directory file descriptor
  * @name:      pathname from userland
  * @flags:     lookup flags
@@ -2368,28 +2375,27 @@ out:
  * Returns 0 and populates "path" on success.
  */
 int
-user_path_umountat(int dfd, const char __user *name, unsigned int flags,
+user_path_mountpoint_at(int dfd, const char __user *name, unsigned int flags,
                        struct path *path)
 {
        struct filename *s = getname(name);
        int error;
-
        if (IS_ERR(s))
                return PTR_ERR(s);
-
-       error = path_umountat(dfd, s->name, path, flags | LOOKUP_RCU);
-       if (unlikely(error == -ECHILD))
-               error = path_umountat(dfd, s->name, path, flags);
-       if (unlikely(error == -ESTALE))
-               error = path_umountat(dfd, s->name, path, flags | LOOKUP_REVAL);
-
-       if (likely(!error))
-               audit_inode(s, path->dentry, 0);
-
+       error = filename_mountpoint(dfd, s, path, flags);
        putname(s);
        return error;
 }
 
+int
+kern_path_mountpoint(int dfd, const char *name, struct path *path,
+                       unsigned int flags)
+{
+       struct filename s = {.name = name};
+       return filename_mountpoint(dfd, &s, path, flags);
+}
+EXPORT_SYMBOL(kern_path_mountpoint);
+
 /*
  * It's inline, so penalty for filesystems that don't use sticky bit is
  * minimal.
index fc2b5226278dbc4d8c0daaa1a2be987fbc7eca5e..25845d1b300b33d84cdd1eb19edc173298c7f168 100644 (file)
@@ -1321,7 +1321,7 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
        if (!(flags & UMOUNT_NOFOLLOW))
                lookup_flags |= LOOKUP_FOLLOW;
 
-       retval = user_path_umountat(AT_FDCWD, name, lookup_flags, &path);
+       retval = user_path_mountpoint_at(AT_FDCWD, name, lookup_flags, &path);
        if (retval)
                goto out;
        mnt = real_mount(path.mnt);
index 5536a95186e28cc60d27676714cc5bb8a1ec350b..f6961ea84c56c7dfa3963626d041d0004d509cbf 100644 (file)
@@ -71,7 +71,7 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc)
        if (!grab_super_passive(sb))
                return -1;
 
-       if (sb->s_op && sb->s_op->nr_cached_objects)
+       if (sb->s_op->nr_cached_objects)
                fs_objects = sb->s_op->nr_cached_objects(sb);
 
        total_objects = sb->s_nr_dentry_unused +
index cf5d2af61b8105ba34818d4f668fad10c6759c02..ff0b981f078e238122113da027d96698a5cbe91d 100644 (file)
@@ -9,7 +9,6 @@
 #define _LINUX_EVENTFD_H
 
 #include <linux/fcntl.h>
-#include <linux/file.h>
 #include <linux/wait.h>
 
 /*
@@ -26,6 +25,8 @@
 #define EFD_SHARED_FCNTL_FLAGS (O_CLOEXEC | O_NONBLOCK)
 #define EFD_FLAGS_SET (EFD_SHARED_FCNTL_FLAGS | EFD_SEMAPHORE)
 
+struct file;
+
 #ifdef CONFIG_EVENTFD
 
 struct file *eventfd_file_create(unsigned int count, int flags);
index cd09751c71a0474cfddc0afc90cfdbfb48d5d4e0..8e47bc7a1665b8b7e6ca06392584328aab702d82 100644 (file)
@@ -58,7 +58,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 
 extern int user_path_at(int, const char __user *, unsigned, struct path *);
 extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
-extern int user_path_umountat(int, const char __user *, unsigned int, struct path *);
 
 #define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
 #define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path)
@@ -71,8 +70,7 @@ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigne
 extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
 extern void done_path_create(struct path *, struct dentry *);
 extern struct dentry *kern_path_locked(const char *, struct path *);
-extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
-                          const char *, unsigned int, struct path *);
+extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
 
 extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
 
index e0aeb32415fff53a032803edbcf96ea30398b61d..2418b6e71a854e187573ec12746481ce863e721a 100644 (file)
@@ -60,6 +60,7 @@
 #include <linux/poll.h>
 #include <linux/flex_array.h> /* used in cgroup_attach_task */
 #include <linux/kthread.h>
+#include <linux/file.h>
 
 #include <linux/atomic.h>
 
@@ -4034,8 +4035,8 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css,
        struct cgroup_event *event;
        struct cgroup_subsys_state *cfile_css;
        unsigned int efd, cfd;
-       struct file *efile;
-       struct file *cfile;
+       struct fefile;
+       struct fcfile;
        char *endp;
        int ret;
 
@@ -4058,31 +4059,31 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css,
        init_waitqueue_func_entry(&event->wait, cgroup_event_wake);
        INIT_WORK(&event->remove, cgroup_event_remove);
 
-       efile = eventfd_fget(efd);
-       if (IS_ERR(efile)) {
-               ret = PTR_ERR(efile);
+       efile = fdget(efd);
+       if (!efile.file) {
+               ret = -EBADF;
                goto out_kfree;
        }
 
-       event->eventfd = eventfd_ctx_fileget(efile);
+       event->eventfd = eventfd_ctx_fileget(efile.file);
        if (IS_ERR(event->eventfd)) {
                ret = PTR_ERR(event->eventfd);
                goto out_put_efile;
        }
 
-       cfile = fget(cfd);
-       if (!cfile) {
+       cfile = fdget(cfd);
+       if (!cfile.file) {
                ret = -EBADF;
                goto out_put_eventfd;
        }
 
        /* the process need read permission on control file */
        /* AV: shouldn't we check that it's been opened for read instead? */
-       ret = inode_permission(file_inode(cfile), MAY_READ);
+       ret = inode_permission(file_inode(cfile.file), MAY_READ);
        if (ret < 0)
                goto out_put_cfile;
 
-       event->cft = __file_cft(cfile);
+       event->cft = __file_cft(cfile.file);
        if (IS_ERR(event->cft)) {
                ret = PTR_ERR(event->cft);
                goto out_put_cfile;
@@ -4103,7 +4104,7 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css,
 
        ret = -EINVAL;
        event->css = cgroup_css(cgrp, event->cft->ss);
-       cfile_css = css_from_dir(cfile->f_dentry->d_parent, event->cft->ss);
+       cfile_css = css_from_dir(cfile.file->f_dentry->d_parent, event->cft->ss);
        if (event->css && event->css == cfile_css && css_tryget(event->css))
                ret = 0;
 
@@ -4121,25 +4122,25 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css,
        if (ret)
                goto out_put_css;
 
-       efile->f_op->poll(efile, &event->pt);
+       efile.file->f_op->poll(efile.file, &event->pt);
 
        spin_lock(&cgrp->event_list_lock);
        list_add(&event->list, &cgrp->event_list);
        spin_unlock(&cgrp->event_list_lock);
 
-       fput(cfile);
-       fput(efile);
+       fdput(cfile);
+       fdput(efile);
 
        return 0;
 
 out_put_css:
        css_put(event->css);
 out_put_cfile:
-       fput(cfile);
+       fdput(cfile);
 out_put_eventfd:
        eventfd_ctx_put(event->eventfd);
 out_put_efile:
-       fput(efile);
+       fdput(efile);
 out_kfree:
        kfree(event);