diff options
Diffstat (limited to 'fs/overlayfs/readdir.c')
-rw-r--r-- | fs/overlayfs/readdir.c | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index fdaf28f75e12..6ec1e43a9a54 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c | |||
@@ -36,13 +36,14 @@ struct ovl_dir_cache { | |||
36 | 36 | ||
37 | struct ovl_readdir_data { | 37 | struct ovl_readdir_data { |
38 | struct dir_context ctx; | 38 | struct dir_context ctx; |
39 | bool is_merge; | 39 | bool is_lowest; |
40 | struct rb_root root; | 40 | struct rb_root root; |
41 | struct list_head *list; | 41 | struct list_head *list; |
42 | struct list_head middle; | 42 | struct list_head middle; |
43 | struct ovl_cache_entry *first_maybe_whiteout; | 43 | struct ovl_cache_entry *first_maybe_whiteout; |
44 | int count; | 44 | int count; |
45 | int err; | 45 | int err; |
46 | bool d_type_supported; | ||
46 | }; | 47 | }; |
47 | 48 | ||
48 | struct ovl_dir_file { | 49 | struct ovl_dir_file { |
@@ -139,9 +140,9 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, | |||
139 | return 0; | 140 | return 0; |
140 | } | 141 | } |
141 | 142 | ||
142 | static int ovl_fill_lower(struct ovl_readdir_data *rdd, | 143 | static int ovl_fill_lowest(struct ovl_readdir_data *rdd, |
143 | const char *name, int namelen, | 144 | const char *name, int namelen, |
144 | loff_t offset, u64 ino, unsigned int d_type) | 145 | loff_t offset, u64 ino, unsigned int d_type) |
145 | { | 146 | { |
146 | struct ovl_cache_entry *p; | 147 | struct ovl_cache_entry *p; |
147 | 148 | ||
@@ -193,10 +194,10 @@ static int ovl_fill_merge(struct dir_context *ctx, const char *name, | |||
193 | container_of(ctx, struct ovl_readdir_data, ctx); | 194 | container_of(ctx, struct ovl_readdir_data, ctx); |
194 | 195 | ||
195 | rdd->count++; | 196 | rdd->count++; |
196 | if (!rdd->is_merge) | 197 | if (!rdd->is_lowest) |
197 | return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type); | 198 | return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type); |
198 | else | 199 | else |
199 | return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type); | 200 | return ovl_fill_lowest(rdd, name, namelen, offset, ino, d_type); |
200 | } | 201 | } |
201 | 202 | ||
202 | static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd) | 203 | 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) | |||
289 | .ctx.actor = ovl_fill_merge, | 290 | .ctx.actor = ovl_fill_merge, |
290 | .list = list, | 291 | .list = list, |
291 | .root = RB_ROOT, | 292 | .root = RB_ROOT, |
292 | .is_merge = false, | 293 | .is_lowest = false, |
293 | }; | 294 | }; |
294 | int idx, next; | 295 | int idx, next; |
295 | 296 | ||
@@ -306,7 +307,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list) | |||
306 | * allows offsets to be reasonably constant | 307 | * allows offsets to be reasonably constant |
307 | */ | 308 | */ |
308 | list_add(&rdd.middle, rdd.list); | 309 | list_add(&rdd.middle, rdd.list); |
309 | rdd.is_merge = true; | 310 | rdd.is_lowest = true; |
310 | err = ovl_dir_read(&realpath, &rdd); | 311 | err = ovl_dir_read(&realpath, &rdd); |
311 | list_del(&rdd.middle); | 312 | list_del(&rdd.middle); |
312 | } | 313 | } |
@@ -577,3 +578,39 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) | |||
577 | } | 578 | } |
578 | inode_unlock(upper->d_inode); | 579 | inode_unlock(upper->d_inode); |
579 | } | 580 | } |
581 | |||
582 | static int ovl_check_d_type(struct dir_context *ctx, const char *name, | ||
583 | int namelen, loff_t offset, u64 ino, | ||
584 | unsigned int d_type) | ||
585 | { | ||
586 | struct ovl_readdir_data *rdd = | ||
587 | container_of(ctx, struct ovl_readdir_data, ctx); | ||
588 | |||
589 | /* Even if d_type is not supported, DT_DIR is returned for . and .. */ | ||
590 | if (!strncmp(name, ".", namelen) || !strncmp(name, "..", namelen)) | ||
591 | return 0; | ||
592 | |||
593 | if (d_type != DT_UNKNOWN) | ||
594 | rdd->d_type_supported = true; | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | /* | ||
600 | * Returns 1 if d_type is supported, 0 not supported/unknown. Negative values | ||
601 | * if error is encountered. | ||
602 | */ | ||
603 | int ovl_check_d_type_supported(struct path *realpath) | ||
604 | { | ||
605 | int err; | ||
606 | struct ovl_readdir_data rdd = { | ||
607 | .ctx.actor = ovl_check_d_type, | ||
608 | .d_type_supported = false, | ||
609 | }; | ||
610 | |||
611 | err = ovl_dir_read(realpath, &rdd); | ||
612 | if (err) | ||
613 | return err; | ||
614 | |||
615 | return rdd.d_type_supported; | ||
616 | } | ||