Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszer...
[sfrench/cifs-2.6.git] / fs / overlayfs / readdir.c
index c310e3ff7f3f7d55979d60ef776c0740243f39b6..0daa4354fec4ae967da4ae43e81f7f833cd1095c 100644 (file)
@@ -26,6 +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 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
        /* 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 @@ static inline int ovl_dir_read(struct path *realpath,
        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,
@@ -816,7 +834,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
                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 @@ const struct file_operations ovl_dir_operations = {
 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);
@@ -844,18 +862,29 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
 
        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 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
        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 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
 }
 
 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;
@@ -1020,7 +1049,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
                        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);