ovl: split super.c
authorMiklos Szeredi <mszeredi@redhat.com>
Fri, 16 Dec 2016 10:02:56 +0000 (11:02 +0100)
committerMiklos Szeredi <mszeredi@redhat.com>
Fri, 16 Dec 2016 10:02:56 +0000 (11:02 +0100)
fs/overlayfs/super.c is the biggest of the overlayfs source files and it
contains various utility functions as well as the rather complicated lookup
code.  Split these parts out to separate files.

Before:

 1446 fs/overlayfs/super.c

After:

  919 fs/overlayfs/super.c
  267 fs/overlayfs/namei.c
  235 fs/overlayfs/util.c
   51 fs/overlayfs/ovl_entry.h

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/Makefile
fs/overlayfs/namei.c [new file with mode: 0644]
fs/overlayfs/overlayfs.h
fs/overlayfs/ovl_entry.h [new file with mode: 0644]
fs/overlayfs/super.c
fs/overlayfs/util.c [new file with mode: 0644]

index 900daed3e91d28f1901c78cab4e9c2fdddf50043..99373bbc1478d989046e47771598c7f1a658f10d 100644 (file)
@@ -4,4 +4,4 @@
 
 obj-$(CONFIG_OVERLAY_FS) += overlay.o
 
-overlay-objs := super.o inode.o dir.o readdir.o copy_up.o
+overlay-objs := super.o namei.o util.o inode.o dir.o readdir.o copy_up.o
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
new file mode 100644 (file)
index 0000000..f4057fc
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2011 Novell Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/xattr.h>
+#include "overlayfs.h"
+#include "ovl_entry.h"
+
+static struct dentry *ovl_lookup_real(struct dentry *dir,
+                                     const struct qstr *name)
+{
+       struct dentry *dentry;
+
+       dentry = lookup_one_len_unlocked(name->name, dir, name->len);
+       if (IS_ERR(dentry)) {
+               if (PTR_ERR(dentry) == -ENOENT)
+                       dentry = NULL;
+       } else if (!dentry->d_inode) {
+               dput(dentry);
+               dentry = NULL;
+       } else if (ovl_dentry_weird(dentry)) {
+               dput(dentry);
+               /* Don't support traversing automounts and other weirdness */
+               dentry = ERR_PTR(-EREMOTE);
+       }
+       return dentry;
+}
+
+static bool ovl_is_opaquedir(struct dentry *dentry)
+{
+       int res;
+       char val;
+
+       if (!d_is_dir(dentry))
+               return false;
+
+       res = vfs_getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
+       if (res == 1 && val == 'y')
+               return true;
+
+       return false;
+}
+
+/*
+ * Returns next layer in stack starting from top.
+ * Returns -1 if this is the last layer.
+ */
+int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       BUG_ON(idx < 0);
+       if (idx == 0) {
+               ovl_path_upper(dentry, path);
+               if (path->dentry)
+                       return oe->numlower ? 1 : -1;
+               idx++;
+       }
+       BUG_ON(idx > oe->numlower);
+       *path = oe->lowerstack[idx - 1];
+
+       return (idx < oe->numlower) ? idx + 1 : -1;
+}
+
+struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
+                         unsigned int flags)
+{
+       struct ovl_entry *oe;
+       const struct cred *old_cred;
+       struct ovl_entry *poe = dentry->d_parent->d_fsdata;
+       struct path *stack = NULL;
+       struct dentry *upperdir, *upperdentry = NULL;
+       unsigned int ctr = 0;
+       struct inode *inode = NULL;
+       bool upperopaque = false;
+       bool stop = false;
+       bool isdir = false;
+       struct dentry *this;
+       unsigned int i;
+       int err;
+
+       old_cred = ovl_override_creds(dentry->d_sb);
+       upperdir = ovl_upperdentry_dereference(poe);
+       if (upperdir) {
+               this = ovl_lookup_real(upperdir, &dentry->d_name);
+               err = PTR_ERR(this);
+               if (IS_ERR(this))
+                       goto out;
+
+               if (this) {
+                       if (unlikely(ovl_dentry_remote(this))) {
+                               dput(this);
+                               err = -EREMOTE;
+                               goto out;
+                       }
+                       if (ovl_is_whiteout(this)) {
+                               dput(this);
+                               this = NULL;
+                               stop = upperopaque = true;
+                       } else if (!d_is_dir(this)) {
+                               stop = true;
+                       } else {
+                               isdir = true;
+                               if (poe->numlower && ovl_is_opaquedir(this))
+                                       stop = upperopaque = true;
+                       }
+               }
+               upperdentry = this;
+       }
+
+       if (!stop && poe->numlower) {
+               err = -ENOMEM;
+               stack = kcalloc(poe->numlower, sizeof(struct path), GFP_KERNEL);
+               if (!stack)
+                       goto out_put_upper;
+       }
+
+       for (i = 0; !stop && i < poe->numlower; i++) {
+               struct path lowerpath = poe->lowerstack[i];
+
+               this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name);
+               err = PTR_ERR(this);
+               if (IS_ERR(this)) {
+                       /*
+                        * If it's positive, then treat ENAMETOOLONG as ENOENT.
+                        */
+                       if (err == -ENAMETOOLONG && (upperdentry || ctr))
+                               continue;
+                       goto out_put;
+               }
+               if (!this)
+                       continue;
+               if (ovl_is_whiteout(this)) {
+                       dput(this);
+                       break;
+               }
+               /*
+                * If this is a non-directory then stop here.
+                */
+               if (!d_is_dir(this)) {
+                       if (isdir) {
+                               dput(this);
+                               break;
+                       }
+                       stop = true;
+               } else {
+                       /*
+                        * Only makes sense to check opaque dir if this is not
+                        * the lowermost layer.
+                        */
+                       if (i < poe->numlower - 1 && ovl_is_opaquedir(this))
+                               stop = true;
+               }
+
+               stack[ctr].dentry = this;
+               stack[ctr].mnt = lowerpath.mnt;
+               ctr++;
+       }
+
+       oe = ovl_alloc_entry(ctr);
+       err = -ENOMEM;
+       if (!oe)
+               goto out_put;
+
+       if (upperdentry || ctr) {
+               struct dentry *realdentry;
+               struct inode *realinode;
+
+               realdentry = upperdentry ? upperdentry : stack[0].dentry;
+               realinode = d_inode(realdentry);
+
+               err = -ENOMEM;
+               if (upperdentry && !d_is_dir(upperdentry)) {
+                       inode = ovl_get_inode(dentry->d_sb, realinode);
+               } else {
+                       inode = ovl_new_inode(dentry->d_sb, realinode->i_mode,
+                                             realinode->i_rdev);
+                       if (inode)
+                               ovl_inode_init(inode, realinode, !!upperdentry);
+               }
+               if (!inode)
+                       goto out_free_oe;
+               ovl_copyattr(realdentry->d_inode, inode);
+       }
+
+       revert_creds(old_cred);
+       oe->opaque = upperopaque;
+       oe->__upperdentry = upperdentry;
+       memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
+       kfree(stack);
+       dentry->d_fsdata = oe;
+       d_add(dentry, inode);
+
+       return NULL;
+
+out_free_oe:
+       kfree(oe);
+out_put:
+       for (i = 0; i < ctr; i++)
+               dput(stack[i].dentry);
+       kfree(stack);
+out_put_upper:
+       dput(upperdentry);
+out:
+       revert_creds(old_cred);
+       return ERR_PTR(err);
+}
+
+bool ovl_lower_positive(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+       struct ovl_entry *poe = dentry->d_parent->d_fsdata;
+       const struct qstr *name = &dentry->d_name;
+       unsigned int i;
+       bool positive = false;
+       bool done = false;
+
+       /*
+        * If dentry is negative, then lower is positive iff this is a
+        * whiteout.
+        */
+       if (!dentry->d_inode)
+               return oe->opaque;
+
+       /* Negative upper -> positive lower */
+       if (!oe->__upperdentry)
+               return true;
+
+       /* Positive upper -> have to look up lower to see whether it exists */
+       for (i = 0; !done && !positive && i < poe->numlower; i++) {
+               struct dentry *this;
+               struct dentry *lowerdir = poe->lowerstack[i].dentry;
+
+               this = lookup_one_len_unlocked(name->name, lowerdir,
+                                              name->len);
+               if (IS_ERR(this)) {
+                       switch (PTR_ERR(this)) {
+                       case -ENOENT:
+                       case -ENAMETOOLONG:
+                               break;
+
+                       default:
+                               /*
+                                * Assume something is there, we just couldn't
+                                * access it.
+                                */
+                               positive = true;
+                               break;
+                       }
+               } else {
+                       if (this->d_inode) {
+                               positive = !ovl_is_whiteout(this);
+                               done = true;
+                       }
+                       dput(this);
+               }
+       }
+
+       return positive;
+}
index db28512165c55dbab6a2fd36be08680424ae9c93..f6e4d3539a251661c072cf994cb0934732cfeffe 100644 (file)
@@ -9,8 +9,6 @@
 
 #include <linux/kernel.h>
 
-struct ovl_entry;
-
 enum ovl_path_type {
        __OVL_PATH_UPPER        = (1 << 0),
        __OVL_PATH_MERGE        = (1 << 1),
@@ -138,37 +136,39 @@ static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper)
        return (struct inode *) (x & ~OVL_ISUPPER_MASK);
 }
 
+/* util.c */
+int ovl_want_write(struct dentry *dentry);
+void ovl_drop_write(struct dentry *dentry);
+struct dentry *ovl_workdir(struct dentry *dentry);
+const struct cred *ovl_override_creds(struct super_block *sb);
+struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
+bool ovl_dentry_remote(struct dentry *dentry);
+bool ovl_dentry_weird(struct dentry *dentry);
 enum ovl_path_type ovl_path_type(struct dentry *dentry);
-u64 ovl_dentry_version_get(struct dentry *dentry);
-void ovl_dentry_version_inc(struct dentry *dentry);
 void ovl_path_upper(struct dentry *dentry, struct path *path);
 void ovl_path_lower(struct dentry *dentry, struct path *path);
 enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
-int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
 struct dentry *ovl_dentry_upper(struct dentry *dentry);
 struct dentry *ovl_dentry_lower(struct dentry *dentry);
 struct dentry *ovl_dentry_real(struct dentry *dentry);
-struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
-                                   bool is_upper);
 struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
 void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
-struct dentry *ovl_workdir(struct dentry *dentry);
-int ovl_want_write(struct dentry *dentry);
-void ovl_drop_write(struct dentry *dentry);
 bool ovl_dentry_is_opaque(struct dentry *dentry);
-bool ovl_lower_positive(struct dentry *dentry);
 bool ovl_dentry_is_whiteout(struct dentry *dentry);
 void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
-bool ovl_is_whiteout(struct dentry *dentry);
-const struct cred *ovl_override_creds(struct super_block *sb);
 void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
+void ovl_inode_init(struct inode *inode, struct inode *realinode,
+                   bool is_upper);
 void ovl_inode_update(struct inode *inode, struct inode *upperinode);
-struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
-                         unsigned int flags);
+void ovl_dentry_version_inc(struct dentry *dentry);
+u64 ovl_dentry_version_get(struct dentry *dentry);
+bool ovl_is_whiteout(struct dentry *dentry);
 struct file *ovl_path_open(struct path *path, int flags);
 
-struct dentry *ovl_upper_create(struct dentry *upperdir, struct dentry *dentry,
-                               struct kstat *stat, const char *link);
+/* namei.c */
+int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
+struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags);
+bool ovl_lower_positive(struct dentry *dentry);
 
 /* readdir.c */
 extern const struct file_operations ovl_dir_operations;
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
new file mode 100644 (file)
index 0000000..3b7ba59
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright (C) 2011 Novell Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+struct ovl_config {
+       char *lowerdir;
+       char *upperdir;
+       char *workdir;
+       bool default_permissions;
+};
+
+/* private information held for overlayfs's superblock */
+struct ovl_fs {
+       struct vfsmount *upper_mnt;
+       unsigned numlower;
+       struct vfsmount **lower_mnt;
+       struct dentry *workdir;
+       long lower_namelen;
+       /* pathnames of lower and upper dirs, for show_options */
+       struct ovl_config config;
+       /* creds of process who forced instantiation of super block */
+       const struct cred *creator_cred;
+};
+
+/* private information held for every overlayfs dentry */
+struct ovl_entry {
+       struct dentry *__upperdentry;
+       struct ovl_dir_cache *cache;
+       union {
+               struct {
+                       u64 version;
+                       bool opaque;
+               };
+               struct rcu_head rcu;
+       };
+       unsigned numlower;
+       struct path lowerstack[];
+};
+
+struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
+
+static inline struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
+{
+       return lockless_dereference(oe->__upperdentry);
+}
index 212e746320b3315ae9e7aa0d48b2be5bea1a1d91..011482e74096f8849675cfe3e05ec5e38084e34e 100644 (file)
 
 #include <linux/fs.h>
 #include <linux/namei.h>
-#include <linux/pagemap.h>
 #include <linux/xattr.h>
-#include <linux/security.h>
 #include <linux/mount.h>
-#include <linux/slab.h>
 #include <linux/parser.h>
 #include <linux/module.h>
-#include <linux/sched.h>
 #include <linux/statfs.h>
 #include <linux/seq_file.h>
 #include <linux/posix_acl_xattr.h>
 #include "overlayfs.h"
+#include "ovl_entry.h"
 
 MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
 MODULE_DESCRIPTION("Overlay filesystem");
 MODULE_LICENSE("GPL");
 
-struct ovl_config {
-       char *lowerdir;
-       char *upperdir;
-       char *workdir;
-       bool default_permissions;
-};
-
-/* private information held for overlayfs's superblock */
-struct ovl_fs {
-       struct vfsmount *upper_mnt;
-       unsigned numlower;
-       struct vfsmount **lower_mnt;
-       struct dentry *workdir;
-       long lower_namelen;
-       /* pathnames of lower and upper dirs, for show_options */
-       struct ovl_config config;
-       /* creds of process who forced instantiation of super block */
-       const struct cred *creator_cred;
-};
 
 struct ovl_dir_cache;
 
-/* private information held for every overlayfs dentry */
-struct ovl_entry {
-       struct dentry *__upperdentry;
-       struct ovl_dir_cache *cache;
-       union {
-               struct {
-                       u64 version;
-                       bool opaque;
-               };
-               struct rcu_head rcu;
-       };
-       unsigned numlower;
-       struct path lowerstack[];
-};
-
 #define OVL_MAX_STACK 500
 
-static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
-{
-       return oe->numlower ? oe->lowerstack[0].dentry : NULL;
-}
-
-enum ovl_path_type ovl_path_type(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-       enum ovl_path_type type = 0;
-
-       if (oe->__upperdentry) {
-               type = __OVL_PATH_UPPER;
-
-               /*
-                * Non-dir dentry can hold lower dentry from previous
-                * location.
-                */
-               if (oe->numlower && d_is_dir(dentry))
-                       type |= __OVL_PATH_MERGE;
-       } else {
-               if (oe->numlower > 1)
-                       type |= __OVL_PATH_MERGE;
-       }
-       return type;
-}
-
-static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
-{
-       return lockless_dereference(oe->__upperdentry);
-}
-
-void ovl_path_upper(struct dentry *dentry, struct path *path)
-{
-       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       path->mnt = ofs->upper_mnt;
-       path->dentry = ovl_upperdentry_dereference(oe);
-}
-
-enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
-{
-       enum ovl_path_type type = ovl_path_type(dentry);
-
-       if (!OVL_TYPE_UPPER(type))
-               ovl_path_lower(dentry, path);
-       else
-               ovl_path_upper(dentry, path);
-
-       return type;
-}
-
-struct dentry *ovl_dentry_upper(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       return ovl_upperdentry_dereference(oe);
-}
-
-struct dentry *ovl_dentry_lower(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       return __ovl_dentry_lower(oe);
-}
-
-struct dentry *ovl_dentry_real(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-       struct dentry *realdentry;
-
-       realdentry = ovl_upperdentry_dereference(oe);
-       if (!realdentry)
-               realdentry = __ovl_dentry_lower(oe);
-
-       return realdentry;
-}
-
-static void ovl_inode_init(struct inode *inode, struct inode *realinode,
-                          bool is_upper)
-{
-       WRITE_ONCE(inode->i_private, (unsigned long) realinode |
-                  (is_upper ? OVL_ISUPPER_MASK : 0));
-}
-
-struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
-                                   bool is_upper)
-{
-       if (is_upper) {
-               struct ovl_fs *ofs = inode->i_sb->s_fs_info;
-
-               return ofs->upper_mnt;
-       } else {
-               return oe->numlower ? oe->lowerstack[0].mnt : NULL;
-       }
-}
-
-struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       return oe->cache;
-}
-
-void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       oe->cache = cache;
-}
-
-void ovl_path_lower(struct dentry *dentry, struct path *path)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       *path = oe->numlower ? oe->lowerstack[0] : (struct path) { NULL, NULL };
-}
-
-int ovl_want_write(struct dentry *dentry)
-{
-       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
-       return mnt_want_write(ofs->upper_mnt);
-}
-
-void ovl_drop_write(struct dentry *dentry)
-{
-       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
-       mnt_drop_write(ofs->upper_mnt);
-}
-
-struct dentry *ovl_workdir(struct dentry *dentry)
-{
-       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
-       return ofs->workdir;
-}
-
-bool ovl_dentry_is_opaque(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-       return oe->opaque;
-}
-
-bool ovl_dentry_is_whiteout(struct dentry *dentry)
-{
-       return !dentry->d_inode && ovl_dentry_is_opaque(dentry);
-}
-
-void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-       oe->opaque = opaque;
-}
-
-void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode));
-       WARN_ON(oe->__upperdentry);
-       /*
-        * Make sure upperdentry is consistent before making it visible to
-        * ovl_upperdentry_dereference().
-        */
-       smp_wmb();
-       oe->__upperdentry = upperdentry;
-}
-
-void ovl_inode_update(struct inode *inode, struct inode *upperinode)
-{
-       WARN_ON(!upperinode);
-       WARN_ON(!inode_unhashed(inode));
-       WRITE_ONCE(inode->i_private,
-                  (unsigned long) upperinode | OVL_ISUPPER_MASK);
-       if (!S_ISDIR(upperinode->i_mode))
-               __insert_inode_hash(inode, (unsigned long) upperinode);
-}
-
-void ovl_dentry_version_inc(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       WARN_ON(!inode_is_locked(dentry->d_inode));
-       oe->version++;
-}
-
-u64 ovl_dentry_version_get(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       WARN_ON(!inode_is_locked(dentry->d_inode));
-       return oe->version;
-}
-
-bool ovl_is_whiteout(struct dentry *dentry)
-{
-       struct inode *inode = dentry->d_inode;
-
-       return inode && IS_WHITEOUT(inode);
-}
-
-const struct cred *ovl_override_creds(struct super_block *sb)
-{
-       struct ovl_fs *ofs = sb->s_fs_info;
-
-       return override_creds(ofs->creator_cred);
-}
-
-static bool ovl_is_opaquedir(struct dentry *dentry)
-{
-       int res;
-       char val;
-
-       if (!d_is_dir(dentry))
-               return false;
-
-       res = vfs_getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
-       if (res == 1 && val == 'y')
-               return true;
-
-       return false;
-}
 
 static void ovl_dentry_release(struct dentry *dentry)
 {
@@ -395,275 +137,6 @@ static const struct dentry_operations ovl_reval_dentry_operations = {
        .d_weak_revalidate = ovl_dentry_weak_revalidate,
 };
 
-static struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
-{
-       size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);
-       struct ovl_entry *oe = kzalloc(size, GFP_KERNEL);
-
-       if (oe)
-               oe->numlower = numlower;
-
-       return oe;
-}
-
-static bool ovl_dentry_remote(struct dentry *dentry)
-{
-       return dentry->d_flags &
-               (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE |
-                DCACHE_OP_REAL);
-}
-
-static bool ovl_dentry_weird(struct dentry *dentry)
-{
-       return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT |
-                                 DCACHE_MANAGE_TRANSIT |
-                                 DCACHE_OP_HASH |
-                                 DCACHE_OP_COMPARE);
-}
-
-static inline struct dentry *ovl_lookup_real(struct dentry *dir,
-                                            const struct qstr *name)
-{
-       struct dentry *dentry;
-
-       dentry = lookup_one_len_unlocked(name->name, dir, name->len);
-       if (IS_ERR(dentry)) {
-               if (PTR_ERR(dentry) == -ENOENT)
-                       dentry = NULL;
-       } else if (!dentry->d_inode) {
-               dput(dentry);
-               dentry = NULL;
-       } else if (ovl_dentry_weird(dentry)) {
-               dput(dentry);
-               /* Don't support traversing automounts and other weirdness */
-               dentry = ERR_PTR(-EREMOTE);
-       }
-       return dentry;
-}
-
-/*
- * Returns next layer in stack starting from top.
- * Returns -1 if this is the last layer.
- */
-int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-
-       BUG_ON(idx < 0);
-       if (idx == 0) {
-               ovl_path_upper(dentry, path);
-               if (path->dentry)
-                       return oe->numlower ? 1 : -1;
-               idx++;
-       }
-       BUG_ON(idx > oe->numlower);
-       *path = oe->lowerstack[idx - 1];
-
-       return (idx < oe->numlower) ? idx + 1 : -1;
-}
-
-struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
-                         unsigned int flags)
-{
-       struct ovl_entry *oe;
-       const struct cred *old_cred;
-       struct ovl_entry *poe = dentry->d_parent->d_fsdata;
-       struct path *stack = NULL;
-       struct dentry *upperdir, *upperdentry = NULL;
-       unsigned int ctr = 0;
-       struct inode *inode = NULL;
-       bool upperopaque = false;
-       bool stop = false;
-       bool isdir = false;
-       struct dentry *this;
-       unsigned int i;
-       int err;
-
-       old_cred = ovl_override_creds(dentry->d_sb);
-       upperdir = ovl_upperdentry_dereference(poe);
-       if (upperdir) {
-               this = ovl_lookup_real(upperdir, &dentry->d_name);
-               err = PTR_ERR(this);
-               if (IS_ERR(this))
-                       goto out;
-
-               if (this) {
-                       if (unlikely(ovl_dentry_remote(this))) {
-                               dput(this);
-                               err = -EREMOTE;
-                               goto out;
-                       }
-                       if (ovl_is_whiteout(this)) {
-                               dput(this);
-                               this = NULL;
-                               stop = upperopaque = true;
-                       } else if (!d_is_dir(this)) {
-                               stop = true;
-                       } else {
-                               isdir = true;
-                               if (poe->numlower && ovl_is_opaquedir(this))
-                                       stop = upperopaque = true;
-                       }
-               }
-               upperdentry = this;
-       }
-
-       if (!stop && poe->numlower) {
-               err = -ENOMEM;
-               stack = kcalloc(poe->numlower, sizeof(struct path), GFP_KERNEL);
-               if (!stack)
-                       goto out_put_upper;
-       }
-
-       for (i = 0; !stop && i < poe->numlower; i++) {
-               struct path lowerpath = poe->lowerstack[i];
-
-               this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name);
-               err = PTR_ERR(this);
-               if (IS_ERR(this)) {
-                       /*
-                        * If it's positive, then treat ENAMETOOLONG as ENOENT.
-                        */
-                       if (err == -ENAMETOOLONG && (upperdentry || ctr))
-                               continue;
-                       goto out_put;
-               }
-               if (!this)
-                       continue;
-               if (ovl_is_whiteout(this)) {
-                       dput(this);
-                       break;
-               }
-               /*
-                * If this is a non-directory then stop here.
-                */
-               if (!d_is_dir(this)) {
-                       if (isdir) {
-                               dput(this);
-                               break;
-                       }
-                       stop = true;
-               } else {
-                       /*
-                        * Only makes sense to check opaque dir if this is not
-                        * the lowermost layer.
-                        */
-                       if (i < poe->numlower - 1 && ovl_is_opaquedir(this))
-                               stop = true;
-               }
-
-               stack[ctr].dentry = this;
-               stack[ctr].mnt = lowerpath.mnt;
-               ctr++;
-       }
-
-       oe = ovl_alloc_entry(ctr);
-       err = -ENOMEM;
-       if (!oe)
-               goto out_put;
-
-       if (upperdentry || ctr) {
-               struct dentry *realdentry;
-               struct inode *realinode;
-
-               realdentry = upperdentry ? upperdentry : stack[0].dentry;
-               realinode = d_inode(realdentry);
-
-               err = -ENOMEM;
-               if (upperdentry && !d_is_dir(upperdentry)) {
-                       inode = ovl_get_inode(dentry->d_sb, realinode);
-               } else {
-                       inode = ovl_new_inode(dentry->d_sb, realinode->i_mode,
-                                             realinode->i_rdev);
-                       if (inode)
-                               ovl_inode_init(inode, realinode, !!upperdentry);
-               }
-               if (!inode)
-                       goto out_free_oe;
-               ovl_copyattr(realdentry->d_inode, inode);
-       }
-
-       revert_creds(old_cred);
-       oe->opaque = upperopaque;
-       oe->__upperdentry = upperdentry;
-       memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
-       kfree(stack);
-       dentry->d_fsdata = oe;
-       d_add(dentry, inode);
-
-       return NULL;
-
-out_free_oe:
-       kfree(oe);
-out_put:
-       for (i = 0; i < ctr; i++)
-               dput(stack[i].dentry);
-       kfree(stack);
-out_put_upper:
-       dput(upperdentry);
-out:
-       revert_creds(old_cred);
-       return ERR_PTR(err);
-}
-
-bool ovl_lower_positive(struct dentry *dentry)
-{
-       struct ovl_entry *oe = dentry->d_fsdata;
-       struct ovl_entry *poe = dentry->d_parent->d_fsdata;
-       const struct qstr *name = &dentry->d_name;
-       unsigned int i;
-       bool positive = false;
-       bool done = false;
-
-       /*
-        * If dentry is negative, then lower is positive iff this is a
-        * whiteout.
-        */
-       if (!dentry->d_inode)
-               return oe->opaque;
-
-       /* Negative upper -> positive lower */
-       if (!oe->__upperdentry)
-               return true;
-
-       /* Positive upper -> have to look up lower to see whether it exists */
-       for (i = 0; !done && !positive && i < poe->numlower; i++) {
-               struct dentry *this;
-               struct dentry *lowerdir = poe->lowerstack[i].dentry;
-
-               this = lookup_one_len_unlocked(name->name, lowerdir,
-                                              name->len);
-               if (IS_ERR(this)) {
-                       switch (PTR_ERR(this)) {
-                       case -ENOENT:
-                       case -ENAMETOOLONG:
-                               break;
-
-                       default:
-                               /*
-                                * Assume something is there, we just couldn't
-                                * access it.
-                                */
-                               positive = true;
-                               break;
-                       }
-               } else {
-                       if (this->d_inode) {
-                               positive = !ovl_is_whiteout(this);
-                               done = true;
-                       }
-                       dput(this);
-               }
-       }
-
-       return positive;
-}
-
-struct file *ovl_path_open(struct path *path, int flags)
-{
-       return dentry_open(path, flags | O_NOATIME, current_cred());
-}
-
 static void ovl_put_super(struct super_block *sb)
 {
        struct ovl_fs *ufs = sb->s_fs_info;
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
new file mode 100644 (file)
index 0000000..0d45a84
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2011 Novell Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/slab.h>
+#include <linux/xattr.h>
+#include "overlayfs.h"
+#include "ovl_entry.h"
+
+int ovl_want_write(struct dentry *dentry)
+{
+       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
+       return mnt_want_write(ofs->upper_mnt);
+}
+
+void ovl_drop_write(struct dentry *dentry)
+{
+       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
+       mnt_drop_write(ofs->upper_mnt);
+}
+
+struct dentry *ovl_workdir(struct dentry *dentry)
+{
+       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
+       return ofs->workdir;
+}
+
+const struct cred *ovl_override_creds(struct super_block *sb)
+{
+       struct ovl_fs *ofs = sb->s_fs_info;
+
+       return override_creds(ofs->creator_cred);
+}
+
+struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
+{
+       size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);
+       struct ovl_entry *oe = kzalloc(size, GFP_KERNEL);
+
+       if (oe)
+               oe->numlower = numlower;
+
+       return oe;
+}
+
+bool ovl_dentry_remote(struct dentry *dentry)
+{
+       return dentry->d_flags &
+               (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE |
+                DCACHE_OP_REAL);
+}
+
+bool ovl_dentry_weird(struct dentry *dentry)
+{
+       return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT |
+                                 DCACHE_MANAGE_TRANSIT |
+                                 DCACHE_OP_HASH |
+                                 DCACHE_OP_COMPARE);
+}
+
+enum ovl_path_type ovl_path_type(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+       enum ovl_path_type type = 0;
+
+       if (oe->__upperdentry) {
+               type = __OVL_PATH_UPPER;
+
+               /*
+                * Non-dir dentry can hold lower dentry from previous
+                * location.
+                */
+               if (oe->numlower && d_is_dir(dentry))
+                       type |= __OVL_PATH_MERGE;
+       } else {
+               if (oe->numlower > 1)
+                       type |= __OVL_PATH_MERGE;
+       }
+       return type;
+}
+
+void ovl_path_upper(struct dentry *dentry, struct path *path)
+{
+       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       path->mnt = ofs->upper_mnt;
+       path->dentry = ovl_upperdentry_dereference(oe);
+}
+
+void ovl_path_lower(struct dentry *dentry, struct path *path)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       *path = oe->numlower ? oe->lowerstack[0] : (struct path) { NULL, NULL };
+}
+
+enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
+{
+       enum ovl_path_type type = ovl_path_type(dentry);
+
+       if (!OVL_TYPE_UPPER(type))
+               ovl_path_lower(dentry, path);
+       else
+               ovl_path_upper(dentry, path);
+
+       return type;
+}
+
+struct dentry *ovl_dentry_upper(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       return ovl_upperdentry_dereference(oe);
+}
+
+static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
+{
+       return oe->numlower ? oe->lowerstack[0].dentry : NULL;
+}
+
+struct dentry *ovl_dentry_lower(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       return __ovl_dentry_lower(oe);
+}
+
+struct dentry *ovl_dentry_real(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+       struct dentry *realdentry;
+
+       realdentry = ovl_upperdentry_dereference(oe);
+       if (!realdentry)
+               realdentry = __ovl_dentry_lower(oe);
+
+       return realdentry;
+}
+
+struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       return oe->cache;
+}
+
+void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       oe->cache = cache;
+}
+
+bool ovl_dentry_is_opaque(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+       return oe->opaque;
+}
+
+bool ovl_dentry_is_whiteout(struct dentry *dentry)
+{
+       return !dentry->d_inode && ovl_dentry_is_opaque(dentry);
+}
+
+void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+       oe->opaque = opaque;
+}
+
+void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode));
+       WARN_ON(oe->__upperdentry);
+       /*
+        * Make sure upperdentry is consistent before making it visible to
+        * ovl_upperdentry_dereference().
+        */
+       smp_wmb();
+       oe->__upperdentry = upperdentry;
+}
+
+void ovl_inode_init(struct inode *inode, struct inode *realinode, bool is_upper)
+{
+       WRITE_ONCE(inode->i_private, (unsigned long) realinode |
+                  (is_upper ? OVL_ISUPPER_MASK : 0));
+}
+
+void ovl_inode_update(struct inode *inode, struct inode *upperinode)
+{
+       WARN_ON(!upperinode);
+       WARN_ON(!inode_unhashed(inode));
+       WRITE_ONCE(inode->i_private,
+                  (unsigned long) upperinode | OVL_ISUPPER_MASK);
+       if (!S_ISDIR(upperinode->i_mode))
+               __insert_inode_hash(inode, (unsigned long) upperinode);
+}
+
+void ovl_dentry_version_inc(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       WARN_ON(!inode_is_locked(dentry->d_inode));
+       oe->version++;
+}
+
+u64 ovl_dentry_version_get(struct dentry *dentry)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+
+       WARN_ON(!inode_is_locked(dentry->d_inode));
+       return oe->version;
+}
+
+bool ovl_is_whiteout(struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+
+       return inode && IS_WHITEOUT(inode);
+}
+
+struct file *ovl_path_open(struct path *path, int flags)
+{
+       return dentry_open(path, flags | O_NOATIME, current_cred());
+}