ovl: multi-layer readdir
authorMiklos Szeredi <mszeredi@suse.cz>
Fri, 12 Dec 2014 23:59:44 +0000 (00:59 +0100)
committerMiklos Szeredi <mszeredi@suse.cz>
Fri, 12 Dec 2014 23:59:44 +0000 (00:59 +0100)
If multiple lower layers exist, merge them as well in readdir according to
the same rules as merging upper with lower.  I.e. take whiteouts and opaque
directories into account on all but the lowers layer.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
fs/overlayfs/readdir.c
fs/overlayfs/super.c

index 481e44873b65e8e23a246d2ff078ad049e5a1dd9..dfef6ca53dfe1210e692f4b5c5ce1ac171921245 100644 (file)
@@ -261,35 +261,34 @@ static void ovl_dir_reset(struct file *file)
 static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
 {
        int err;
-       struct path lowerpath;
-       struct path upperpath;
+       struct path realpath;
        struct ovl_readdir_data rdd = {
                .ctx.actor = ovl_fill_merge,
                .list = list,
                .root = RB_ROOT,
                .is_merge = false,
        };
-
-       ovl_path_lower(dentry, &lowerpath);
-       ovl_path_upper(dentry, &upperpath);
-
-       if (upperpath.dentry) {
-               rdd.dir = upperpath.dentry;
-               err = ovl_dir_read(&upperpath, &rdd);
-               if (err)
-                       goto out;
-       }
-       if (lowerpath.dentry) {
-               /*
-                * Insert lowerpath entries before upperpath ones, this allows
-                * offsets to be reasonably constant
-                */
-               list_add(&rdd.middle, rdd.list);
-               rdd.is_merge = true;
-               err = ovl_dir_read(&lowerpath, &rdd);
-               list_del(&rdd.middle);
+       int idx, next;
+
+       for (idx = 0; idx != -1; idx = next) {
+               next = ovl_path_next(idx, dentry, &realpath);
+
+               if (next != -1) {
+                       rdd.dir = realpath.dentry;
+                       err = ovl_dir_read(&realpath, &rdd);
+                       if (err)
+                               break;
+               } else {
+                       /*
+                        * Insert lowest layer entries before upper ones, this
+                        * allows offsets to be reasonably constant
+                        */
+                       list_add(&rdd.middle, rdd.list);
+                       rdd.is_merge = true;
+                       err = ovl_dir_read(&realpath, &rdd);
+                       list_del(&rdd.middle);
+               }
        }
-out:
        return err;
 }
 
index 07e4c576e93ed8e9891555af0c0bc5997e8317f5..c245043aa1b951bb31153ccba67ca31b21ced725 100644 (file)
@@ -81,6 +81,9 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)
                } else if (!oe->opaque) {
                        type |= __OVL_PATH_PURE;
                }
+       } else {
+               if (oe->numlower > 1)
+                       type |= __OVL_PATH_MERGE;
        }
        return type;
 }