diff options
Diffstat (limited to 'fs/overlayfs/export.c')
-rw-r--r-- | fs/overlayfs/export.c | 43 |
1 files changed, 26 insertions, 17 deletions
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 7a4b6a0fd527..361174810ce8 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -215,6 +215,23 @@ nomem: return ERR_PTR(-ENOMEM); } +/* Get the upper or lower dentry in stach whose on layer @idx */ +static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int idx) +{ + struct ovl_entry *oe = dentry->d_fsdata; + int i; + + if (!idx) + return ovl_dentry_upper(dentry); + + for (i = 0; i < oe->numlower; i++) { + if (oe->lowerstack[i].layer->idx == idx) + return oe->lowerstack[i].dentry; + } + + return NULL; +} + /* * Lookup a child overlay dentry to get a connected overlay dentry whose real * dentry is @real. If @real is on upper layer, we lookup a child overlay @@ -230,10 +247,6 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected, struct name_snapshot name; int err; - /* TODO: lookup by lower real dentry */ - if (layer->idx) - return ERR_PTR(-EACCES); - /* * Lookup child overlay dentry by real name. The dir mutex protects us * from racing with overlay rename. If the overlay dentry that is above @@ -244,7 +257,7 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected, inode_lock_nested(dir, I_MUTEX_PARENT); err = -ECHILD; parent = dget_parent(real); - if (ovl_dentry_upper(connected) != parent) + if (ovl_dentry_real_at(connected, layer->idx) != parent) goto fail; /* @@ -262,7 +275,7 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected, dput(this); err = -ENOENT; goto fail; - } else if (ovl_dentry_upper(this) != real) { + } else if (ovl_dentry_real_at(this, layer->idx) != real) { dput(this); err = -ESTALE; goto fail; @@ -294,14 +307,13 @@ static struct dentry *ovl_lookup_real(struct super_block *sb, int err = 0; /* TODO: use index when looking up by lower real dentry */ - if (layer->idx) - return ERR_PTR(-EACCES); connected = dget(sb->s_root); while (!err) { struct dentry *next, *this; struct dentry *parent = NULL; - struct dentry *real_connected = ovl_dentry_upper(connected); + struct dentry *real_connected = ovl_dentry_real_at(connected, + layer->idx); if (real_connected == real) break; @@ -391,6 +403,7 @@ static struct dentry *ovl_get_dentry(struct super_block *sb, { struct ovl_fs *ofs = sb->s_fs_info; struct ovl_layer upper_layer = { .mnt = ofs->upper_mnt }; + struct ovl_layer *layer = upper ? &upper_layer : lowerpath->layer; struct dentry *real = upper ?: (index ?: lowerpath->dentry); /* @@ -400,19 +413,15 @@ static struct dentry *ovl_get_dentry(struct super_block *sb, if (!d_is_dir(real)) return ovl_obtain_alias(sb, upper, lowerpath, index); - /* TODO: lookup connected dir from real lower dir */ - if (!upper) - return ERR_PTR(-EACCES); - /* Removed empty directory? */ - if ((upper->d_flags & DCACHE_DISCONNECTED) || d_unhashed(upper)) + if ((real->d_flags & DCACHE_DISCONNECTED) || d_unhashed(real)) return ERR_PTR(-ENOENT); /* - * If real upper dentry is connected and hashed, get a connected - * overlay dentry with the same path as the real upper dentry. + * If real dentry is connected and hashed, get a connected overlay + * dentry whose real dentry is @real. */ - return ovl_lookup_real(sb, upper, &upper_layer); + return ovl_lookup_real(sb, real, layer); } static struct dentry *ovl_upper_fh_to_d(struct super_block *sb, |