You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openwrt/target/linux/generic/patches-2.6.37/102-overlayfs_fix_readdir_d...

125 lines
3.3 KiB
Diff

--- a/fs/overlayfs/overlayfs.c
+++ b/fs/overlayfs/overlayfs.c
@@ -248,8 +248,7 @@ static struct ovl_cache_entry *ovl_cache
}
static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len,
- u64 ino, unsigned int d_type,
- bool is_whiteout)
+ u64 ino, unsigned int d_type)
{
struct ovl_cache_entry *p;
@@ -262,7 +261,7 @@ static struct ovl_cache_entry *ovl_cache
p->len = len;
p->type = d_type;
p->ino = ino;
- p->is_whiteout = is_whiteout;
+ p->is_whiteout = false;
}
return p;
@@ -270,7 +269,7 @@ static struct ovl_cache_entry *ovl_cache
static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
const char *name, int len, u64 ino,
- unsigned int d_type, bool is_whiteout)
+ unsigned int d_type)
{
struct rb_node **newp = &rdd->root->rb_node;
struct rb_node *parent = NULL;
@@ -291,11 +290,18 @@ static int ovl_cache_entry_add_rb(struct
return 0;
}
- p = ovl_cache_entry_new(name, len, ino, d_type, is_whiteout);
+ p = ovl_cache_entry_new(name, len, ino, d_type);
if (p == NULL)
return -ENOMEM;
- list_add_tail(&p->l_node, rdd->list);
+ /*
+ * Add links before other types to be able to quicky mark
+ * any whiteout entries
+ */
+ if (d_type == DT_LNK)
+ list_add(&p->l_node, rdd->list);
+ else
+ list_add_tail(&p->l_node, rdd->list);
rb_link_node(&p->node, parent, newp);
rb_insert_color(&p->node, rdd->root);
@@ -313,7 +319,7 @@ static int ovl_fill_lower(void *buf, con
if (p) {
list_move_tail(&p->l_node, rdd->middle);
} else {
- p = ovl_cache_entry_new(name, namelen, ino, d_type, false);
+ p = ovl_cache_entry_new(name, namelen, ino, d_type);
if (p == NULL)
rdd->err = -ENOMEM;
else
@@ -338,26 +344,9 @@ static int ovl_fill_upper(void *buf, con
loff_t offset, u64 ino, unsigned int d_type)
{
struct ovl_readdir_data *rdd = buf;
- bool is_whiteout = false;
rdd->count++;
- if (d_type == DT_LNK) {
- struct dentry *dentry;
-
- dentry = lookup_one_len(name, rdd->dir, namelen);
- if (IS_ERR(dentry)) {
- rdd->err = PTR_ERR(dentry);
- goto out;
- }
- is_whiteout = ovl_is_whiteout(dentry);
- dput(dentry);
- }
-
- rdd->err = ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type,
- is_whiteout);
-
-out:
- return rdd->err;
+ return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type);
}
static int ovl_dir_read(struct path *realpath, struct ovl_readdir_data *rdd,
@@ -423,6 +412,26 @@ static void ovl_dir_reset(struct file *f
}
}
+static void ovl_dir_mark_whiteouts(struct ovl_readdir_data *rdd)
+{
+ struct ovl_cache_entry *p;
+ struct dentry *dentry;
+
+ mutex_lock(&rdd->dir->d_inode->i_mutex);
+ list_for_each_entry(p, rdd->list, l_node) {
+ if (p->type != DT_LNK)
+ break;
+
+ dentry = lookup_one_len(p->name, rdd->dir, p->len);
+ if (IS_ERR(dentry))
+ continue;
+
+ p->is_whiteout = ovl_is_whiteout(dentry);
+ dput(dentry);
+ }
+ mutex_unlock(&rdd->dir->d_inode->i_mutex);
+}
+
static int ovl_dir_read_merged(struct path *upperpath, struct path *lowerpath,
struct ovl_readdir_data *rdd)
{
@@ -436,6 +445,8 @@ static int ovl_dir_read_merged(struct pa
err = ovl_dir_read(upperpath, rdd, ovl_fill_upper);
if (err)
goto out;
+
+ ovl_dir_mark_whiteouts(rdd);
}
/*
* Insert lowerpath entries before upperpath ones, this allows