Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszer...
[linux-2.6-block.git] / fs / overlayfs / readdir.c
index fdaf28f75e12acf1f4dba023c31017c81747744b..6ec1e43a9a54af87d57edf8d1e5a228812f0bd61 100644 (file)
@@ -36,13 +36,14 @@ struct ovl_dir_cache {
 
 struct ovl_readdir_data {
        struct dir_context ctx;
-       bool is_merge;
+       bool is_lowest;
        struct rb_root root;
        struct list_head *list;
        struct list_head middle;
        struct ovl_cache_entry *first_maybe_whiteout;
        int count;
        int err;
+       bool d_type_supported;
 };
 
 struct ovl_dir_file {
@@ -139,9 +140,9 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
        return 0;
 }
 
-static int ovl_fill_lower(struct ovl_readdir_data *rdd,
-                         const char *name, int namelen,
-                         loff_t offset, u64 ino, unsigned int d_type)
+static int ovl_fill_lowest(struct ovl_readdir_data *rdd,
+                          const char *name, int namelen,
+                          loff_t offset, u64 ino, unsigned int d_type)
 {
        struct ovl_cache_entry *p;
 
@@ -193,10 +194,10 @@ static int ovl_fill_merge(struct dir_context *ctx, const char *name,
                container_of(ctx, struct ovl_readdir_data, ctx);
 
        rdd->count++;
-       if (!rdd->is_merge)
+       if (!rdd->is_lowest)
                return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type);
        else
-               return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
+               return ovl_fill_lowest(rdd, name, namelen, offset, ino, d_type);
 }
 
 static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
@@ -289,7 +290,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
                .ctx.actor = ovl_fill_merge,
                .list = list,
                .root = RB_ROOT,
-               .is_merge = false,
+               .is_lowest = false,
        };
        int idx, next;
 
@@ -306,7 +307,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
                         * allows offsets to be reasonably constant
                         */
                        list_add(&rdd.middle, rdd.list);
-                       rdd.is_merge = true;
+                       rdd.is_lowest = true;
                        err = ovl_dir_read(&realpath, &rdd);
                        list_del(&rdd.middle);
                }
@@ -577,3 +578,39 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
        }
        inode_unlock(upper->d_inode);
 }
+
+static int ovl_check_d_type(struct dir_context *ctx, const char *name,
+                         int namelen, loff_t offset, u64 ino,
+                         unsigned int d_type)
+{
+       struct ovl_readdir_data *rdd =
+               container_of(ctx, struct ovl_readdir_data, ctx);
+
+       /* Even if d_type is not supported, DT_DIR is returned for . and .. */
+       if (!strncmp(name, ".", namelen) || !strncmp(name, "..", namelen))
+               return 0;
+
+       if (d_type != DT_UNKNOWN)
+               rdd->d_type_supported = true;
+
+       return 0;
+}
+
+/*
+ * Returns 1 if d_type is supported, 0 not supported/unknown. Negative values
+ * if error is encountered.
+ */
+int ovl_check_d_type_supported(struct path *realpath)
+{
+       int err;
+       struct ovl_readdir_data rdd = {
+               .ctx.actor = ovl_check_d_type,
+               .d_type_supported = false,
+       };
+
+       err = ovl_dir_read(realpath, &rdd);
+       if (err)
+               return err;
+
+       return rdd.d_type_supported;
+}