Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszer...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 13 Sep 2017 16:11:44 +0000 (09:11 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 13 Sep 2017 16:11:44 +0000 (09:11 -0700)
Pull overlayfs updates from Miklos Szeredi:
 "This fixes d_ino correctness in readdir, which brings overlayfs on par
  with normal filesystems regarding inode number semantics, as long as
  all layers are on the same filesystem.

  There are also some bug fixes, one in particular (random ioctl's
  shouldn't be able to modify lower layers) that touches some vfs code,
  but of course no-op for non-overlay fs"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: fix false positive ESTALE on lookup
  ovl: don't allow writing ioctl on lower layer
  ovl: fix relatime for directories
  vfs: add flags to d_real()
  ovl: cleanup d_real for negative
  ovl: constant d_ino for non-merge dirs
  ovl: constant d_ino across copy up
  ovl: fix readdir error value
  ovl: check snprintf return

15 files changed:
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
fs/inode.c
fs/internal.h
fs/namespace.c
fs/open.c
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
fs/overlayfs/readdir.c
fs/overlayfs/super.c
fs/overlayfs/util.c
fs/xattr.c
include/linux/dcache.h
include/linux/fs.h

index fe25787ff6d49748c3e1ecc494f17e401a60916a..75d2d57e2c4421122434aa855444e8a344c38f21 100644 (file)
@@ -22,7 +22,7 @@ prototypes:
        struct vfsmount *(*d_automount)(struct path *path);
        int (*d_manage)(const struct path *, bool);
        struct dentry *(*d_real)(struct dentry *, const struct inode *,
-                                unsigned int);
+                                unsigned int, unsigned int);
 
 locking rules:
                rename_lock     ->d_lock        may block       rcu-walk
index 405a3df759b33938dc9b6cc4e819673538f973a1..5fd325df59e2233df60cbf6da6c92230aa0c26ef 100644 (file)
@@ -988,7 +988,7 @@ struct dentry_operations {
        struct vfsmount *(*d_automount)(struct path *);
        int (*d_manage)(const struct path *, bool);
        struct dentry *(*d_real)(struct dentry *, const struct inode *,
-                                unsigned int);
+                                unsigned int, unsigned int);
 };
 
   d_revalidate: called when the VFS needs to revalidate a dentry. This
index 210054157a4998b901c366e5d27a211b39f23315..d1e35b53bb23b80db7077500f63eeec9bce6bb28 100644 (file)
@@ -1570,11 +1570,24 @@ EXPORT_SYMBOL(bmap);
 static void update_ovl_inode_times(struct dentry *dentry, struct inode *inode,
                               bool rcu)
 {
-       if (!rcu) {
-               struct inode *realinode = d_real_inode(dentry);
+       struct dentry *upperdentry;
 
-               if (unlikely(inode != realinode) &&
-                   (!timespec_equal(&inode->i_mtime, &realinode->i_mtime) ||
+       /*
+        * Nothing to do if in rcu or if non-overlayfs
+        */
+       if (rcu || likely(!(dentry->d_flags & DCACHE_OP_REAL)))
+               return;
+
+       upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER);
+
+       /*
+        * If file is on lower then we can't update atime, so no worries about
+        * stale mtime/ctime.
+        */
+       if (upperdentry) {
+               struct inode *realinode = d_inode(upperdentry);
+
+               if ((!timespec_equal(&inode->i_mtime, &realinode->i_mtime) ||
                     !timespec_equal(&inode->i_ctime, &realinode->i_ctime))) {
                        inode->i_mtime = realinode->i_mtime;
                        inode->i_ctime = realinode->i_ctime;
index fedfe94d84ba56a45ee9ab24948824ecc999b388..48cee21b4f146f5da86b3e485a1f9c155c33a5fe 100644 (file)
@@ -71,8 +71,10 @@ extern void __init mnt_init(void);
 
 extern int __mnt_want_write(struct vfsmount *);
 extern int __mnt_want_write_file(struct file *);
+extern int mnt_want_write_file_path(struct file *);
 extern void __mnt_drop_write(struct vfsmount *);
 extern void __mnt_drop_write_file(struct file *);
+extern void mnt_drop_write_file_path(struct file *);
 
 /*
  * fs_struct.c
index f8893dc6a989dc6c9b0f4e8daba2e985a64976e6..df0f7521979a211eee70ae6a0e848d88bb1c65c9 100644 (file)
@@ -431,13 +431,18 @@ int __mnt_want_write_file(struct file *file)
 }
 
 /**
- * mnt_want_write_file - get write access to a file's mount
+ * mnt_want_write_file_path - get write access to a file's mount
  * @file: the file who's mount on which to take a write
  *
  * This is like mnt_want_write, but it takes a file and can
  * do some optimisations if the file is open for write already
+ *
+ * Called by the vfs for cases when we have an open file at hand, but will do an
+ * inode operation on it (important distinction for files opened on overlayfs,
+ * since the file operations will come from the real underlying file, while
+ * inode operations come from the overlay).
  */
-int mnt_want_write_file(struct file *file)
+int mnt_want_write_file_path(struct file *file)
 {
        int ret;
 
@@ -447,6 +452,53 @@ int mnt_want_write_file(struct file *file)
                sb_end_write(file->f_path.mnt->mnt_sb);
        return ret;
 }
+
+static inline int may_write_real(struct file *file)
+{
+       struct dentry *dentry = file->f_path.dentry;
+       struct dentry *upperdentry;
+
+       /* Writable file? */
+       if (file->f_mode & FMODE_WRITER)
+               return 0;
+
+       /* Not overlayfs? */
+       if (likely(!(dentry->d_flags & DCACHE_OP_REAL)))
+               return 0;
+
+       /* File refers to upper, writable layer? */
+       upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER);
+       if (upperdentry && file_inode(file) == d_inode(upperdentry))
+               return 0;
+
+       /* Lower layer: can't write to real file, sorry... */
+       return -EPERM;
+}
+
+/**
+ * mnt_want_write_file - get write access to a file's mount
+ * @file: the file who's mount on which to take a write
+ *
+ * This is like mnt_want_write, but it takes a file and can
+ * do some optimisations if the file is open for write already
+ *
+ * Mostly called by filesystems from their ioctl operation before performing
+ * modification.  On overlayfs this needs to check if the file is on a read-only
+ * lower layer and deny access in that case.
+ */
+int mnt_want_write_file(struct file *file)
+{
+       int ret;
+
+       ret = may_write_real(file);
+       if (!ret) {
+               sb_start_write(file_inode(file)->i_sb);
+               ret = __mnt_want_write_file(file);
+               if (ret)
+                       sb_end_write(file_inode(file)->i_sb);
+       }
+       return ret;
+}
 EXPORT_SYMBOL_GPL(mnt_want_write_file);
 
 /**
@@ -484,10 +536,16 @@ void __mnt_drop_write_file(struct file *file)
        __mnt_drop_write(file->f_path.mnt);
 }
 
-void mnt_drop_write_file(struct file *file)
+void mnt_drop_write_file_path(struct file *file)
 {
        mnt_drop_write(file->f_path.mnt);
 }
+
+void mnt_drop_write_file(struct file *file)
+{
+       __mnt_drop_write(file->f_path.mnt);
+       sb_end_write(file_inode(file)->i_sb);
+}
 EXPORT_SYMBOL(mnt_drop_write_file);
 
 static int mnt_make_readonly(struct mount *mnt)
index 35bb784763a4fde35558b10d8538cb9617a7c728..7ea118471dce59996ae3d3d9b9e3b9d156baffba 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -96,7 +96,7 @@ long vfs_truncate(const struct path *path, loff_t length)
         * write access on the upper inode, not on the overlay inode.  For
         * non-overlay filesystems d_real() is an identity function.
         */
-       upperdentry = d_real(path->dentry, NULL, O_WRONLY);
+       upperdentry = d_real(path->dentry, NULL, O_WRONLY, 0);
        error = PTR_ERR(upperdentry);
        if (IS_ERR(upperdentry))
                goto mnt_drop_write_and_out;
@@ -670,12 +670,12 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group)
        if (!f.file)
                goto out;
 
-       error = mnt_want_write_file(f.file);
+       error = mnt_want_write_file_path(f.file);
        if (error)
                goto out_fput;
        audit_file(f.file);
        error = chown_common(&f.file->f_path, user, group);
-       mnt_drop_write_file(f.file);
+       mnt_drop_write_file_path(f.file);
 out_fput:
        fdput(f);
 out:
@@ -857,7 +857,7 @@ EXPORT_SYMBOL(file_path);
 int vfs_open(const struct path *path, struct file *file,
             const struct cred *cred)
 {
-       struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags);
+       struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags, 0);
 
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
index 48b70e6490f32e9c7f9c34f8dcb20735279b3946..9cb0c80e5967e5b9fec893e7645d77c41db457f6 100644 (file)
@@ -155,7 +155,7 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
 static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
                            struct dentry *newdentry, bool hardlink)
 {
-       ovl_dentry_version_inc(dentry->d_parent);
+       ovl_dentry_version_inc(dentry->d_parent, false);
        ovl_dentry_set_upper_alias(dentry);
        if (!hardlink) {
                ovl_inode_update(inode, newdentry);
@@ -692,7 +692,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
        if (flags)
                ovl_cleanup(wdir, upper);
 
-       ovl_dentry_version_inc(dentry->d_parent);
+       ovl_dentry_version_inc(dentry->d_parent, true);
 out_d_drop:
        d_drop(dentry);
        dput(whiteout);
@@ -742,7 +742,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
                err = vfs_rmdir(dir, upper);
        else
                err = vfs_unlink(dir, upper, NULL);
-       ovl_dentry_version_inc(dentry->d_parent);
+       ovl_dentry_version_inc(dentry->d_parent, ovl_type_origin(dentry));
 
        /*
         * Keeping this dentry hashed would mean having to release
@@ -1089,8 +1089,9 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
                        drop_nlink(d_inode(new));
        }
 
-       ovl_dentry_version_inc(old->d_parent);
-       ovl_dentry_version_inc(new->d_parent);
+       ovl_dentry_version_inc(old->d_parent,
+                              !overwrite && ovl_type_origin(new));
+       ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old));
 
 out_dput:
        dput(newdentry);
index 5bc71642b22605367ee319abdf8e45a0ec9adcde..a619addecafcf05713b3a41e7e05a9e0866847a1 100644 (file)
@@ -498,6 +498,9 @@ static int ovl_set_nlink_common(struct dentry *dentry,
        len = snprintf(buf, sizeof(buf), format,
                       (int) (inode->i_nlink - realinode->i_nlink));
 
+       if (WARN_ON(len >= sizeof(buf)))
+               return -EIO;
+
        return ovl_do_setxattr(ovl_dentry_upper(dentry),
                               OVL_XATTR_NLINK, buf, len, 0);
 }
@@ -576,10 +579,13 @@ static int ovl_inode_set(struct inode *inode, void *data)
 static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
                             struct dentry *upperdentry)
 {
-       struct inode *lowerinode = lowerdentry ? d_inode(lowerdentry) : NULL;
-
-       /* Lower (origin) inode must match, even if NULL */
-       if (ovl_inode_lower(inode) != lowerinode)
+       /*
+        * Allow non-NULL lower inode in ovl_inode even if lowerdentry is NULL.
+        * This happens when finding a copied up overlay inode for a renamed
+        * or hardlinked overlay dentry and lower dentry cannot be followed
+        * by origin because lower fs does not support file handles.
+        */
+       if (lowerdentry && ovl_inode_lower(inode) != d_inode(lowerdentry))
                return false;
 
        /*
index e927a62c97ae3c92070dbb440712c819cc8e827f..d4e8c1a08fb0f5d7b4c79de7aa88a5211a4996a4 100644 (file)
@@ -204,8 +204,8 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode);
 struct inode *ovl_inode_upper(struct inode *inode);
 struct inode *ovl_inode_lower(struct inode *inode);
 struct inode *ovl_inode_real(struct inode *inode);
-struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
-void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
+struct ovl_dir_cache *ovl_dir_cache(struct inode *inode);
+void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache);
 bool ovl_dentry_is_opaque(struct dentry *dentry);
 bool ovl_dentry_is_whiteout(struct dentry *dentry);
 void ovl_dentry_set_opaque(struct dentry *dentry);
@@ -217,7 +217,7 @@ void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect);
 void ovl_inode_init(struct inode *inode, struct dentry *upperdentry,
                    struct dentry *lowerdentry);
 void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
-void ovl_dentry_version_inc(struct dentry *dentry);
+void ovl_dentry_version_inc(struct dentry *dentry, bool impurity);
 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);
@@ -229,6 +229,7 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
                       int xerr);
 int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry);
 void ovl_set_flag(unsigned long flag, struct inode *inode);
+void ovl_clear_flag(unsigned long flag, struct inode *inode);
 bool ovl_test_flag(unsigned long flag, struct inode *inode);
 bool ovl_inuse_trylock(struct dentry *dentry);
 void ovl_inuse_unlock(struct dentry *dentry);
@@ -256,6 +257,7 @@ extern const struct file_operations ovl_dir_operations;
 int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list);
 void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list);
 void ovl_cache_free(struct list_head *list);
+void ovl_dir_cache_free(struct inode *inode);
 int ovl_check_d_type_supported(struct path *realpath);
 void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
                         struct dentry *dentry, int level);
index f0fd3adb169398ec8483876dece90fe2bc435d78..62e9b22a2077ac2ac9acb382dd5617a34e9e7636 100644 (file)
 #include <linux/rbtree.h>
 #include <linux/security.h>
 #include <linux/cred.h>
+#include <linux/ratelimit.h>
 #include "overlayfs.h"
 
 struct ovl_cache_entry {
        unsigned int len;
        unsigned int type;
+       u64 real_ino;
        u64 ino;
        struct list_head l_node;
        struct rb_node node;
@@ -32,18 +34,20 @@ struct ovl_dir_cache {
        long refcount;
        u64 version;
        struct list_head entries;
+       struct rb_root root;
 };
 
 struct ovl_readdir_data {
        struct dir_context ctx;
        struct dentry *dentry;
        bool is_lowest;
-       struct rb_root root;
+       struct rb_root *root;
        struct list_head *list;
        struct list_head middle;
        struct ovl_cache_entry *first_maybe_whiteout;
        int count;
        int err;
+       bool is_upper;
        bool d_type_supported;
 };
 
@@ -58,7 +62,33 @@ struct ovl_dir_file {
 
 static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n)
 {
-       return container_of(n, struct ovl_cache_entry, node);
+       return rb_entry(n, struct ovl_cache_entry, node);
+}
+
+static bool ovl_cache_entry_find_link(const char *name, int len,
+                                     struct rb_node ***link,
+                                     struct rb_node **parent)
+{
+       bool found = false;
+       struct rb_node **newp = *link;
+
+       while (!found && *newp) {
+               int cmp;
+               struct ovl_cache_entry *tmp;
+
+               *parent = *newp;
+               tmp = ovl_cache_entry_from_node(*newp);
+               cmp = strncmp(name, tmp->name, len);
+               if (cmp > 0)
+                       newp = &tmp->node.rb_right;
+               else if (cmp < 0 || len < tmp->len)
+                       newp = &tmp->node.rb_left;
+               else
+                       found = true;
+       }
+       *link = newp;
+
+       return found;
 }
 
 static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
@@ -82,6 +112,32 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
        return NULL;
 }
 
+static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
+                          struct ovl_cache_entry *p)
+{
+       /* Don't care if not doing ovl_iter() */
+       if (!rdd->dentry)
+               return false;
+
+       /* Always recalc d_ino for parent */
+       if (strcmp(p->name, "..") == 0)
+               return true;
+
+       /* If this is lower, then native d_ino will do */
+       if (!rdd->is_upper)
+               return false;
+
+       /*
+        * Recalc d_ino for '.' and for all entries if dir is impure (contains
+        * copied up entries)
+        */
+       if ((p->name[0] == '.' && p->len == 1) ||
+           ovl_test_flag(OVL_IMPURE, d_inode(rdd->dentry)))
+               return true;
+
+       return false;
+}
+
 static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
                                                   const char *name, int len,
                                                   u64 ino, unsigned int d_type)
@@ -97,7 +153,11 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
        p->name[len] = '\0';
        p->len = len;
        p->type = d_type;
+       p->real_ino = ino;
        p->ino = ino;
+       /* Defer setting d_ino for upper entry to ovl_iterate() */
+       if (ovl_calc_d_ino(rdd, p))
+               p->ino = 0;
        p->is_whiteout = false;
 
        if (d_type == DT_CHR) {
@@ -111,32 +171,22 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
                                  const char *name, int len, u64 ino,
                                  unsigned int d_type)
 {
-       struct rb_node **newp = &rdd->root.rb_node;
+       struct rb_node **newp = &rdd->root->rb_node;
        struct rb_node *parent = NULL;
        struct ovl_cache_entry *p;
 
-       while (*newp) {
-               int cmp;
-               struct ovl_cache_entry *tmp;
-
-               parent = *newp;
-               tmp = ovl_cache_entry_from_node(*newp);
-               cmp = strncmp(name, tmp->name, len);
-               if (cmp > 0)
-                       newp = &tmp->node.rb_right;
-               else if (cmp < 0 || len < tmp->len)
-                       newp = &tmp->node.rb_left;
-               else
-                       return 0;
-       }
+       if (ovl_cache_entry_find_link(name, len, &newp, &parent))
+               return 0;
 
        p = ovl_cache_entry_new(rdd, name, len, ino, d_type);
-       if (p == NULL)
+       if (p == NULL) {
+               rdd->err = -ENOMEM;
                return -ENOMEM;
+       }
 
        list_add_tail(&p->l_node, rdd->list);
        rb_link_node(&p->node, parent, newp);
-       rb_insert_color(&p->node, &rdd->root);
+       rb_insert_color(&p->node, rdd->root);
 
        return 0;
 }
@@ -147,7 +197,7 @@ static int ovl_fill_lowest(struct ovl_readdir_data *rdd,
 {
        struct ovl_cache_entry *p;
 
-       p = ovl_cache_entry_find(&rdd->root, name, namelen);
+       p = ovl_cache_entry_find(rdd->root, name, namelen);
        if (p) {
                list_move_tail(&p->l_node, &rdd->middle);
        } else {
@@ -172,6 +222,16 @@ void ovl_cache_free(struct list_head *list)
        INIT_LIST_HEAD(list);
 }
 
+void ovl_dir_cache_free(struct inode *inode)
+{
+       struct ovl_dir_cache *cache = ovl_dir_cache(inode);
+
+       if (cache) {
+               ovl_cache_free(&cache->entries);
+               kfree(cache);
+       }
+}
+
 static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry)
 {
        struct ovl_dir_cache *cache = od->cache;
@@ -179,8 +239,8 @@ static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry)
        WARN_ON(cache->refcount <= 0);
        cache->refcount--;
        if (!cache->refcount) {
-               if (ovl_dir_cache(dentry) == cache)
-                       ovl_set_dir_cache(dentry, NULL);
+               if (ovl_dir_cache(d_inode(dentry)) == cache)
+                       ovl_set_dir_cache(d_inode(dentry), NULL);
 
                ovl_cache_free(&cache->entries);
                kfree(cache);
@@ -273,7 +333,8 @@ static void ovl_dir_reset(struct file *file)
                od->is_real = false;
 }
 
-static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
+static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list,
+       struct rb_root *root)
 {
        int err;
        struct path realpath;
@@ -281,13 +342,14 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
                .ctx.actor = ovl_fill_merge,
                .dentry = dentry,
                .list = list,
-               .root = RB_ROOT,
+               .root = root,
                .is_lowest = false,
        };
        int idx, next;
 
        for (idx = 0; idx != -1; idx = next) {
                next = ovl_path_next(idx, dentry, &realpath);
+               rdd.is_upper = ovl_dentry_upper(dentry) == realpath.dentry;
 
                if (next != -1) {
                        err = ovl_dir_read(&realpath, &rdd);
@@ -326,12 +388,13 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
        int res;
        struct ovl_dir_cache *cache;
 
-       cache = ovl_dir_cache(dentry);
+       cache = ovl_dir_cache(d_inode(dentry));
        if (cache && ovl_dentry_version_get(dentry) == cache->version) {
+               WARN_ON(!cache->refcount);
                cache->refcount++;
                return cache;
        }
-       ovl_set_dir_cache(dentry, NULL);
+       ovl_set_dir_cache(d_inode(dentry), NULL);
 
        cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL);
        if (!cache)
@@ -339,8 +402,9 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
 
        cache->refcount = 1;
        INIT_LIST_HEAD(&cache->entries);
+       cache->root = RB_ROOT;
 
-       res = ovl_dir_read_merged(dentry, &cache->entries);
+       res = ovl_dir_read_merged(dentry, &cache->entries, &cache->root);
        if (res) {
                ovl_cache_free(&cache->entries);
                kfree(cache);
@@ -348,22 +412,266 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
        }
 
        cache->version = ovl_dentry_version_get(dentry);
-       ovl_set_dir_cache(dentry, cache);
+       ovl_set_dir_cache(d_inode(dentry), cache);
 
        return cache;
 }
 
+/*
+ * Set d_ino for upper entries. Non-upper entries should always report
+ * the uppermost real inode ino and should not call this function.
+ *
+ * When not all layer are on same fs, report real ino also for upper.
+ *
+ * When all layers are on the same fs, and upper has a reference to
+ * copy up origin, call vfs_getattr() on the overlay entry to make
+ * sure that d_ino will be consistent with st_ino from stat(2).
+ */
+static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p)
+
+{
+       struct dentry *dir = path->dentry;
+       struct dentry *this = NULL;
+       enum ovl_path_type type;
+       u64 ino = p->real_ino;
+       int err = 0;
+
+       if (!ovl_same_sb(dir->d_sb))
+               goto out;
+
+       if (p->name[0] == '.') {
+               if (p->len == 1) {
+                       this = dget(dir);
+                       goto get;
+               }
+               if (p->len == 2 && p->name[1] == '.') {
+                       /* we shall not be moved */
+                       this = dget(dir->d_parent);
+                       goto get;
+               }
+       }
+       this = lookup_one_len(p->name, dir, p->len);
+       if (IS_ERR_OR_NULL(this) || !this->d_inode) {
+               if (IS_ERR(this)) {
+                       err = PTR_ERR(this);
+                       this = NULL;
+                       goto fail;
+               }
+               goto out;
+       }
+
+get:
+       type = ovl_path_type(this);
+       if (OVL_TYPE_ORIGIN(type)) {
+               struct kstat stat;
+               struct path statpath = *path;
+
+               statpath.dentry = this;
+               err = vfs_getattr(&statpath, &stat, STATX_INO, 0);
+               if (err)
+                       goto fail;
+
+               WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev);
+               ino = stat.ino;
+       }
+
+out:
+       p->ino = ino;
+       dput(this);
+       return err;
+
+fail:
+       pr_warn_ratelimited("overlay: failed to look up (%s) for ino (%i)\n",
+                           p->name, err);
+       goto out;
+}
+
+static int ovl_fill_plain(struct dir_context *ctx, const char *name,
+                         int namelen, loff_t offset, u64 ino,
+                         unsigned int d_type)
+{
+       struct ovl_cache_entry *p;
+       struct ovl_readdir_data *rdd =
+               container_of(ctx, struct ovl_readdir_data, ctx);
+
+       rdd->count++;
+       p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type);
+       if (p == NULL) {
+               rdd->err = -ENOMEM;
+               return -ENOMEM;
+       }
+       list_add_tail(&p->l_node, rdd->list);
+
+       return 0;
+}
+
+static int ovl_dir_read_impure(struct path *path,  struct list_head *list,
+                              struct rb_root *root)
+{
+       int err;
+       struct path realpath;
+       struct ovl_cache_entry *p, *n;
+       struct ovl_readdir_data rdd = {
+               .ctx.actor = ovl_fill_plain,
+               .list = list,
+               .root = root,
+       };
+
+       INIT_LIST_HEAD(list);
+       *root = RB_ROOT;
+       ovl_path_upper(path->dentry, &realpath);
+
+       err = ovl_dir_read(&realpath, &rdd);
+       if (err)
+               return err;
+
+       list_for_each_entry_safe(p, n, list, l_node) {
+               if (strcmp(p->name, ".") != 0 &&
+                   strcmp(p->name, "..") != 0) {
+                       err = ovl_cache_update_ino(path, p);
+                       if (err)
+                               return err;
+               }
+               if (p->ino == p->real_ino) {
+                       list_del(&p->l_node);
+                       kfree(p);
+               } else {
+                       struct rb_node **newp = &root->rb_node;
+                       struct rb_node *parent = NULL;
+
+                       if (WARN_ON(ovl_cache_entry_find_link(p->name, p->len,
+                                                             &newp, &parent)))
+                               return -EIO;
+
+                       rb_link_node(&p->node, parent, newp);
+                       rb_insert_color(&p->node, root);
+               }
+       }
+       return 0;
+}
+
+static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path)
+{
+       int res;
+       struct dentry *dentry = path->dentry;
+       struct ovl_dir_cache *cache;
+
+       cache = ovl_dir_cache(d_inode(dentry));
+       if (cache && ovl_dentry_version_get(dentry) == cache->version)
+               return cache;
+
+       /* Impure cache is not refcounted, free it here */
+       ovl_dir_cache_free(d_inode(dentry));
+       ovl_set_dir_cache(d_inode(dentry), NULL);
+
+       cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL);
+       if (!cache)
+               return ERR_PTR(-ENOMEM);
+
+       res = ovl_dir_read_impure(path, &cache->entries, &cache->root);
+       if (res) {
+               ovl_cache_free(&cache->entries);
+               kfree(cache);
+               return ERR_PTR(res);
+       }
+       if (list_empty(&cache->entries)) {
+               /* Good oportunity to get rid of an unnecessary "impure" flag */
+               ovl_do_removexattr(ovl_dentry_upper(dentry), OVL_XATTR_IMPURE);
+               ovl_clear_flag(OVL_IMPURE, d_inode(dentry));
+               kfree(cache);
+               return NULL;
+       }
+
+       cache->version = ovl_dentry_version_get(dentry);
+       ovl_set_dir_cache(d_inode(dentry), cache);
+
+       return cache;
+}
+
+struct ovl_readdir_translate {
+       struct dir_context *orig_ctx;
+       struct ovl_dir_cache *cache;
+       struct dir_context ctx;
+       u64 parent_ino;
+};
+
+static int ovl_fill_real(struct dir_context *ctx, const char *name,
+                          int namelen, loff_t offset, u64 ino,
+                          unsigned int d_type)
+{
+       struct ovl_readdir_translate *rdt =
+               container_of(ctx, struct ovl_readdir_translate, ctx);
+       struct dir_context *orig_ctx = rdt->orig_ctx;
+
+       if (rdt->parent_ino && strcmp(name, "..") == 0)
+               ino = rdt->parent_ino;
+       else if (rdt->cache) {
+               struct ovl_cache_entry *p;
+
+               p = ovl_cache_entry_find(&rdt->cache->root, name, namelen);
+               if (p)
+                       ino = p->ino;
+       }
+
+       return orig_ctx->actor(orig_ctx, name, namelen, offset, ino, d_type);
+}
+
+static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
+{
+       int err;
+       struct ovl_dir_file *od = file->private_data;
+       struct dentry *dir = file->f_path.dentry;
+       struct ovl_readdir_translate rdt = {
+               .ctx.actor = ovl_fill_real,
+               .orig_ctx = ctx,
+       };
+
+       if (OVL_TYPE_MERGE(ovl_path_type(dir->d_parent))) {
+               struct kstat stat;
+               struct path statpath = file->f_path;
+
+               statpath.dentry = dir->d_parent;
+               err = vfs_getattr(&statpath, &stat, STATX_INO, 0);
+               if (err)
+                       return err;
+
+               WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev);
+               rdt.parent_ino = stat.ino;
+       }
+
+       if (ovl_test_flag(OVL_IMPURE, d_inode(dir))) {
+               rdt.cache = ovl_cache_get_impure(&file->f_path);
+               if (IS_ERR(rdt.cache))
+                       return PTR_ERR(rdt.cache);
+       }
+
+       return iterate_dir(od->realfile, &rdt.ctx);
+}
+
+
 static int ovl_iterate(struct file *file, struct dir_context *ctx)
 {
        struct ovl_dir_file *od = file->private_data;
        struct dentry *dentry = file->f_path.dentry;
        struct ovl_cache_entry *p;
+       int err;
 
        if (!ctx->pos)
                ovl_dir_reset(file);
 
-       if (od->is_real)
+       if (od->is_real) {
+               /*
+                * If parent is merge, then need to adjust d_ino for '..', if
+                * dir is impure then need to adjust d_ino for copied up
+                * entries.
+                */
+               if (ovl_same_sb(dentry->d_sb) &&
+                   (ovl_test_flag(OVL_IMPURE, d_inode(dentry)) ||
+                    OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent)))) {
+                       return ovl_iterate_real(file, ctx);
+               }
                return iterate_dir(od->realfile, ctx);
+       }
 
        if (!od->cache) {
                struct ovl_dir_cache *cache;
@@ -378,9 +686,15 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
 
        while (od->cursor != &od->cache->entries) {
                p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
-               if (!p->is_whiteout)
+               if (!p->is_whiteout) {
+                       if (!p->ino) {
+                               err = ovl_cache_update_ino(&file->f_path, p);
+                               if (err)
+                                       return err;
+                       }
                        if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
                                break;
+               }
                od->cursor = p->l_node.next;
                ctx->pos++;
        }
@@ -522,8 +836,9 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
 {
        int err;
        struct ovl_cache_entry *p;
+       struct rb_root root = RB_ROOT;
 
-       err = ovl_dir_read_merged(dentry, list);
+       err = ovl_dir_read_merged(dentry, list, &root);
        if (err)
                return err;
 
@@ -612,12 +927,13 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
        int err;
        struct inode *dir = path->dentry->d_inode;
        LIST_HEAD(list);
+       struct rb_root root = RB_ROOT;
        struct ovl_cache_entry *p;
        struct ovl_readdir_data rdd = {
                .ctx.actor = ovl_fill_merge,
                .dentry = NULL,
                .list = &list,
-               .root = RB_ROOT,
+               .root = &root,
                .is_lowest = false,
        };
 
@@ -675,12 +991,13 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
        struct inode *dir = dentry->d_inode;
        struct path path = { .mnt = mnt, .dentry = dentry };
        LIST_HEAD(list);
+       struct rb_root root = RB_ROOT;
        struct ovl_cache_entry *p;
        struct ovl_readdir_data rdd = {
                .ctx.actor = ovl_fill_merge,
                .dentry = NULL,
                .list = &list,
-               .root = RB_ROOT,
+               .root = &root,
                .is_lowest = false,
        };
 
index d86e89f972016b4046b20a7a45c2f3f436c50f02..cd49c0298ddf9c69b4331e578b97f26c89c72caf 100644 (file)
@@ -70,20 +70,20 @@ static int ovl_check_append_only(struct inode *inode, int flag)
 
 static struct dentry *ovl_d_real(struct dentry *dentry,
                                 const struct inode *inode,
-                                unsigned int open_flags)
+                                unsigned int open_flags, unsigned int flags)
 {
        struct dentry *real;
        int err;
 
+       if (flags & D_REAL_UPPER)
+               return ovl_dentry_upper(dentry);
+
        if (!d_is_reg(dentry)) {
                if (!inode || inode == d_inode(dentry))
                        return dentry;
                goto bug;
        }
 
-       if (d_is_negative(dentry))
-               return dentry;
-
        if (open_flags) {
                err = ovl_open_maybe_copy_up(dentry, open_flags);
                if (err)
@@ -105,7 +105,7 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
                goto bug;
 
        /* Handle recursion */
-       real = d_real(real, inode, open_flags);
+       real = d_real(real, inode, open_flags, 0);
 
        if (!inode || inode == d_inode(real))
                return real;
@@ -198,6 +198,7 @@ static void ovl_destroy_inode(struct inode *inode)
 
        dput(oi->__upperdentry);
        kfree(oi->redirect);
+       ovl_dir_cache_free(inode);
        mutex_destroy(&oi->lock);
 
        call_rcu(&inode->i_rcu, ovl_i_callback);
index f46ad75dc96af187ea22990a09e63d051aa4bbca..117794582f9fa597d34d8843aa8bdfb7a99cbe76 100644 (file)
@@ -180,14 +180,14 @@ struct inode *ovl_inode_real(struct inode *inode)
 }
 
 
-struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
+struct ovl_dir_cache *ovl_dir_cache(struct inode *inode)
 {
-       return OVL_I(d_inode(dentry))->cache;
+       return OVL_I(inode)->cache;
 }
 
-void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
+void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache)
 {
-       OVL_I(d_inode(dentry))->cache = cache;
+       OVL_I(inode)->cache = cache;
 }
 
 bool ovl_dentry_is_opaque(struct dentry *dentry)
@@ -275,12 +275,19 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
        }
 }
 
-void ovl_dentry_version_inc(struct dentry *dentry)
+void ovl_dentry_version_inc(struct dentry *dentry, bool impurity)
 {
        struct inode *inode = d_inode(dentry);
 
        WARN_ON(!inode_is_locked(inode));
-       OVL_I(inode)->version++;
+       /*
+        * Version is used by readdir code to keep cache consistent.  For merge
+        * dirs all changes need to be noted.  For non-merge dirs, cache only
+        * contains impure (ones which have been copied up and have origins)
+        * entries, so only need to note changes to impure entries.
+        */
+       if (OVL_TYPE_MERGE(ovl_path_type(dentry)) || impurity)
+               OVL_I(inode)->version++;
 }
 
 u64 ovl_dentry_version_get(struct dentry *dentry)
@@ -382,6 +389,11 @@ void ovl_set_flag(unsigned long flag, struct inode *inode)
        set_bit(flag, &OVL_I(inode)->flags);
 }
 
+void ovl_clear_flag(unsigned long flag, struct inode *inode)
+{
+       clear_bit(flag, &OVL_I(inode)->flags);
+}
+
 bool ovl_test_flag(unsigned long flag, struct inode *inode)
 {
        return test_bit(flag, &OVL_I(inode)->flags);
index 7b03df6b8be26fbc7483f139063b99e1a0d2b451..4424f7fecf14549b65c62d0cac4b8b692718f426 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/posix_acl_xattr.h>
 
 #include <linux/uaccess.h>
+#include "internal.h"
 
 static const char *
 strcmp_prefix(const char *a, const char *a_prefix)
@@ -502,10 +503,10 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
        if (!f.file)
                return error;
        audit_file(f.file);
-       error = mnt_want_write_file(f.file);
+       error = mnt_want_write_file_path(f.file);
        if (!error) {
                error = setxattr(f.file->f_path.dentry, name, value, size, flags);
-               mnt_drop_write_file(f.file);
+               mnt_drop_write_file_path(f.file);
        }
        fdput(f);
        return error;
@@ -734,10 +735,10 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
        if (!f.file)
                return error;
        audit_file(f.file);
-       error = mnt_want_write_file(f.file);
+       error = mnt_want_write_file_path(f.file);
        if (!error) {
                error = removexattr(f.file->f_path.dentry, name);
-               mnt_drop_write_file(f.file);
+               mnt_drop_write_file_path(f.file);
        }
        fdput(f);
        return error;
index aae1cdb76851308dd825e1558cf007a3722dad73..ed1a7cf6923af31a3c08a591ca74444b6771e71d 100644 (file)
@@ -147,7 +147,7 @@ struct dentry_operations {
        struct vfsmount *(*d_automount)(struct path *);
        int (*d_manage)(const struct path *, bool);
        struct dentry *(*d_real)(struct dentry *, const struct inode *,
-                                unsigned int);
+                                unsigned int, unsigned int);
 } ____cacheline_aligned;
 
 /*
@@ -562,11 +562,15 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper)
        return upper;
 }
 
+/* d_real() flags */
+#define D_REAL_UPPER   0x2     /* return upper dentry or NULL if non-upper */
+
 /**
  * d_real - Return the real dentry
  * @dentry: the dentry to query
  * @inode: inode to select the dentry from multiple layers (can be NULL)
- * @flags: open flags to control copy-up behavior
+ * @open_flags: open flags to control copy-up behavior
+ * @flags: flags to control what is returned by this function
  *
  * If dentry is on a union/overlay, then return the underlying, real dentry.
  * Otherwise return the dentry itself.
@@ -575,10 +579,10 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper)
  */
 static inline struct dentry *d_real(struct dentry *dentry,
                                    const struct inode *inode,
-                                   unsigned int flags)
+                                   unsigned int open_flags, unsigned int flags)
 {
        if (unlikely(dentry->d_flags & DCACHE_OP_REAL))
-               return dentry->d_op->d_real(dentry, inode, flags);
+               return dentry->d_op->d_real(dentry, inode, open_flags, flags);
        else
                return dentry;
 }
@@ -593,7 +597,7 @@ static inline struct dentry *d_real(struct dentry *dentry,
 static inline struct inode *d_real_inode(const struct dentry *dentry)
 {
        /* This usage of d_real() results in const dentry */
-       return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0));
+       return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0, 0));
 }
 
 struct name_snapshot {
index 2d0e6748e46e1f6707b0de2b127cf5ee59553c3c..33d8e45cd874b7b25d9fc570b26c6df4dfea83e2 100644 (file)
@@ -1235,7 +1235,7 @@ static inline struct inode *file_inode(const struct file *f)
 
 static inline struct dentry *file_dentry(const struct file *file)
 {
-       return d_real(file->f_path.dentry, file_inode(file), 0);
+       return d_real(file->f_path.dentry, file_inode(file), 0, 0);
 }
 
 static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)