Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszer...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 17 Nov 2017 21:36:59 +0000 (13:36 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 17 Nov 2017 21:36:59 +0000 (13:36 -0800)
Pull overlayfs updates from Miklos Szeredi:

 - Report constant st_ino values across copy-up even if underlying
   layers are on different filesystems, but using different st_dev
   values for each layer.

   Ideally we'd report the same st_dev across the overlay, and it's
   possible to do for filesystems that use only 32bits for st_ino by
   unifying the inum space. It would be nice if it wasn't a choice of 32
   or 64, rather filesystems could report their current maximum (that
   could change on resize, so it wouldn't be set in stone).

 - miscellaneus fixes and a cleanup of ovl_fill_super(), that was long
   overdue.

 - created a path_put_init() helper that clears out the pointers after
   putting the ref.

   I think this could be useful elsewhere, so added it to <linux/path.h>

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: (30 commits)
  ovl: remove unneeded arg from ovl_verify_origin()
  ovl: Put upperdentry if ovl_check_origin() fails
  ovl: rename ufs to ofs
  ovl: clean up getting lower layers
  ovl: clean up workdir creation
  ovl: clean up getting upper layer
  ovl: move ovl_get_workdir() and ovl_get_lower_layers()
  ovl: reduce the number of arguments for ovl_workdir_create()
  ovl: change order of setup in ovl_fill_super()
  ovl: factor out ovl_free_fs() helper
  ovl: grab reference to workbasedir early
  ovl: split out ovl_get_indexdir() from ovl_fill_super()
  ovl: split out ovl_get_lower_layers() from ovl_fill_super()
  ovl: split out ovl_get_workdir() from ovl_fill_super()
  ovl: split out ovl_get_upper() from ovl_fill_super()
  ovl: split out ovl_get_lowerstack() from ovl_fill_super()
  ovl: split out ovl_get_workpath() from ovl_fill_super()
  ovl: split out ovl_get_upperpath() from ovl_fill_super()
  ovl: use path_put_init() in error paths for ovl_fill_super()
  vfs: add path_put_init()
  ...

1  2 
fs/overlayfs/ovl_entry.h
fs/overlayfs/readdir.c

diff --combined fs/overlayfs/ovl_entry.h
index 36b49bd09264a5bd92df902a1de7d10771d4b770,93eb6a044dd24e1230acf63a1ca82151983e9469..752bab645879e5fce43e86d45e835d94d3c44b22
@@@ -17,11 -17,21 +17,21 @@@ struct ovl_config 
        bool index;
  };
  
+ struct ovl_layer {
+       struct vfsmount *mnt;
+       dev_t pseudo_dev;
+ };
+ struct ovl_path {
+       struct ovl_layer *layer;
+       struct dentry *dentry;
+ };
  /* private information held for overlayfs's superblock */
  struct ovl_fs {
        struct vfsmount *upper_mnt;
        unsigned numlower;
-       struct vfsmount **lower_mnt;
+       struct ovl_layer *lower_layers;
        /* workbasedir is the path at workdir= mount option */
        struct dentry *workbasedir;
        /* workdir is the 'work' directory under workbasedir */
@@@ -52,7 -62,7 +62,7 @@@ struct ovl_entry 
                struct rcu_head rcu;
        };
        unsigned numlower;
-       struct path lowerstack[];
+       struct ovl_path lowerstack[];
  };
  
  struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
@@@ -77,5 -87,5 +87,5 @@@ static inline struct ovl_inode *OVL_I(s
  
  static inline struct dentry *ovl_upperdentry_dereference(struct ovl_inode *oi)
  {
 -      return lockless_dereference(oi->__upperdentry);
 +      return READ_ONCE(oi->__upperdentry);
  }
diff --combined fs/overlayfs/readdir.c
index c310e3ff7f3f7d55979d60ef776c0740243f39b6,914e77e10f0f3e6ef0867cb6f593c91d07a42c11..0daa4354fec4ae967da4ae43e81f7f833cd1095c
@@@ -26,6 -26,7 +26,7 @@@ struct ovl_cache_entry 
        struct list_head l_node;
        struct rb_node node;
        struct ovl_cache_entry *next_maybe_whiteout;
+       bool is_upper;
        bool is_whiteout;
        char name[];
  };
@@@ -158,6 -159,7 +159,7 @@@ static struct ovl_cache_entry *ovl_cach
        /* Defer setting d_ino for upper entry to ovl_iterate() */
        if (ovl_calc_d_ino(rdd, p))
                p->ino = 0;
+       p->is_upper = rdd->is_upper;
        p->is_whiteout = false;
  
        if (d_type == DT_CHR) {
@@@ -316,21 -318,37 +318,37 @@@ static inline int ovl_dir_read(struct p
        return err;
  }
  
+ /*
+  * Can we iterate real dir directly?
+  *
+  * Non-merge dir may contain whiteouts from a time it was a merge upper, before
+  * lower dir was removed under it and possibly before it was rotated from upper
+  * to lower layer.
+  */
+ static bool ovl_dir_is_real(struct dentry *dir)
+ {
+       return !ovl_test_flag(OVL_WHITEOUTS, d_inode(dir));
+ }
  static void ovl_dir_reset(struct file *file)
  {
        struct ovl_dir_file *od = file->private_data;
        struct ovl_dir_cache *cache = od->cache;
        struct dentry *dentry = file->f_path.dentry;
-       enum ovl_path_type type = ovl_path_type(dentry);
+       bool is_real;
  
        if (cache && ovl_dentry_version_get(dentry) != cache->version) {
                ovl_cache_put(od, dentry);
                od->cache = NULL;
                od->cursor = NULL;
        }
-       WARN_ON(!od->is_real && !OVL_TYPE_MERGE(type));
-       if (od->is_real && OVL_TYPE_MERGE(type))
+       is_real = ovl_dir_is_real(dentry);
+       if (od->is_real != is_real) {
+               /* is_real can only become false when dir is copied up */
+               if (WARN_ON(is_real))
+                       return;
                od->is_real = false;
+       }
  }
  
  static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list,
@@@ -754,7 -772,7 +772,7 @@@ static int ovl_dir_fsync(struct file *f
        if (!od->is_upper && OVL_TYPE_UPPER(ovl_path_type(dentry))) {
                struct inode *inode = file_inode(file);
  
 -              realfile = lockless_dereference(od->upperfile);
 +              realfile = READ_ONCE(od->upperfile);
                if (!realfile) {
                        struct path upperpath;
  
@@@ -816,7 -834,7 +834,7 @@@ static int ovl_dir_open(struct inode *i
                return PTR_ERR(realfile);
        }
        od->realfile = realfile;
-       od->is_real = !OVL_TYPE_MERGE(type);
+       od->is_real = ovl_dir_is_real(file->f_path.dentry);
        od->is_upper = OVL_TYPE_UPPER(type);
        file->private_data = od;
  
@@@ -835,7 -853,7 +853,7 @@@ const struct file_operations ovl_dir_op
  int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
  {
        int err;
-       struct ovl_cache_entry *p;
+       struct ovl_cache_entry *p, *n;
        struct rb_root root = RB_ROOT;
  
        err = ovl_dir_read_merged(dentry, list, &root);
  
        err = 0;
  
-       list_for_each_entry(p, list, l_node) {
-               if (p->is_whiteout)
-                       continue;
+       list_for_each_entry_safe(p, n, list, l_node) {
+               /*
+                * Select whiteouts in upperdir, they should
+                * be cleared when deleting this directory.
+                */
+               if (p->is_whiteout) {
+                       if (p->is_upper)
+                               continue;
+                       goto del_entry;
+               }
  
                if (p->name[0] == '.') {
                        if (p->len == 1)
-                               continue;
+                               goto del_entry;
                        if (p->len == 2 && p->name[1] == '.')
-                               continue;
+                               goto del_entry;
                }
                err = -ENOTEMPTY;
                break;
+ del_entry:
+               list_del(&p->l_node);
+               kfree(p);
        }
  
        return err;
@@@ -869,7 -898,7 +898,7 @@@ void ovl_cleanup_whiteouts(struct dentr
        list_for_each_entry(p, list, l_node) {
                struct dentry *dentry;
  
-               if (!p->is_whiteout)
+               if (WARN_ON(!p->is_whiteout || !p->is_upper))
                        continue;
  
                dentry = lookup_one_len(p->name, upper, p->len);
@@@ -985,7 -1014,7 +1014,7 @@@ void ovl_workdir_cleanup(struct inode *
  }
  
  int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
-                        struct path *lowerstack, unsigned int numlower)
+                        struct ovl_path *lower, unsigned int numlower)
  {
        int err;
        struct dentry *index = NULL;
                        index = NULL;
                        break;
                }
-               err = ovl_verify_index(index, lowerstack, numlower);
+               err = ovl_verify_index(index, lower, numlower);
                /* Cleanup stale and orphan index entries */
                if (err && (err == -ESTALE || err == -ENOENT))
                        err = ovl_cleanup(dir, index);