summaryrefslogtreecommitdiff
path: root/fs/overlayfs/readdir.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-12-13 00:59:42 +0100
committerMiklos Szeredi <mszeredi@suse.cz>2014-12-13 00:59:42 +0100
commit49c21e1cacd74a8c83407c70ad860c994e606e25 (patch)
tree16c50417ab340185c3a9c69b79e890712e298c8e /fs/overlayfs/readdir.c
parentb2776bf7149bddd1f4161f14f79520f17fc1d71d (diff)
ovl: check whiteout while reading directory
Don't make a separate pass for checking whiteouts, since we can do it while reading the upper directory. This will make it easier to handle multiple layers. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/overlayfs/readdir.c')
-rw-r--r--fs/overlayfs/readdir.c77
1 files changed, 28 insertions, 49 deletions
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index ab1e3dcbed95..3efa44acf98b 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -40,6 +40,7 @@ struct ovl_readdir_data {
struct rb_root root;
struct list_head *list;
struct list_head middle;
+ struct dentry *dir;
int count;
int err;
};
@@ -126,6 +127,32 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
if (p == NULL)
return -ENOMEM;
+ if (d_type == DT_CHR) {
+ struct dentry *dentry;
+ const struct cred *old_cred;
+ struct cred *override_cred;
+
+ override_cred = prepare_creds();
+ if (!override_cred) {
+ kfree(p);
+ return -ENOMEM;
+ }
+
+ /*
+ * CAP_DAC_OVERRIDE for lookup
+ */
+ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
+ old_cred = override_creds(override_cred);
+
+ dentry = lookup_one_len(name, rdd->dir, len);
+ if (!IS_ERR(dentry)) {
+ p->is_whiteout = ovl_is_whiteout(dentry);
+ dput(dentry);
+ }
+ revert_creds(old_cred);
+ put_cred(override_cred);
+ }
+
list_add_tail(&p->l_node, rdd->list);
rb_link_node(&p->node, parent, newp);
rb_insert_color(&p->node, &rdd->root);
@@ -231,49 +258,6 @@ static void ovl_dir_reset(struct file *file)
od->is_real = false;
}
-static int ovl_dir_mark_whiteouts(struct dentry *dir,
- struct ovl_readdir_data *rdd)
-{
- struct ovl_cache_entry *p;
- struct dentry *dentry;
- const struct cred *old_cred;
- struct cred *override_cred;
-
- override_cred = prepare_creds();
- if (!override_cred) {
- ovl_cache_free(rdd->list);
- return -ENOMEM;
- }
-
- /*
- * CAP_DAC_OVERRIDE for lookup
- */
- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
- old_cred = override_creds(override_cred);
-
- mutex_lock(&dir->d_inode->i_mutex);
- list_for_each_entry(p, rdd->list, l_node) {
- if (p->is_cursor)
- continue;
-
- if (p->type != DT_CHR)
- continue;
-
- dentry = lookup_one_len(p->name, dir, p->len);
- if (IS_ERR(dentry))
- continue;
-
- p->is_whiteout = ovl_is_whiteout(dentry);
- dput(dentry);
- }
- mutex_unlock(&dir->d_inode->i_mutex);
-
- revert_creds(old_cred);
- put_cred(override_cred);
-
- return 0;
-}
-
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
{
int err;
@@ -290,15 +274,10 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
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) {
- err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
- if (err)
- goto out;
- }
}
if (lowerpath.dentry) {
/*