|
@@ -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;
|
|
|
+}
|