|
@@ -703,25 +703,39 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
|
|
struct ovl_path *stack = &origin;
|
|
struct ovl_path *stack = &origin;
|
|
struct dentry *dentry = NULL;
|
|
struct dentry *dentry = NULL;
|
|
struct dentry *index = NULL;
|
|
struct dentry *index = NULL;
|
|
- struct inode *inode = NULL;
|
|
|
|
- bool is_deleted = false;
|
|
|
|
|
|
+ struct inode *inode;
|
|
int err;
|
|
int err;
|
|
|
|
|
|
- /* First lookup indexed upper by fh */
|
|
|
|
|
|
+ /* First lookup overlay inode in inode cache by origin fh */
|
|
|
|
+ err = ovl_check_origin_fh(ofs, fh, false, NULL, &stack);
|
|
|
|
+ if (err)
|
|
|
|
+ return ERR_PTR(err);
|
|
|
|
+
|
|
|
|
+ if (!d_is_dir(origin.dentry) ||
|
|
|
|
+ !(origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
|
|
|
|
+ inode = ovl_lookup_inode(sb, origin.dentry, false);
|
|
|
|
+ err = PTR_ERR(inode);
|
|
|
|
+ if (IS_ERR(inode))
|
|
|
|
+ goto out_err;
|
|
|
|
+ if (inode) {
|
|
|
|
+ dentry = d_find_any_alias(inode);
|
|
|
|
+ iput(inode);
|
|
|
|
+ if (dentry)
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Then lookup indexed upper/whiteout by origin fh */
|
|
if (ofs->indexdir) {
|
|
if (ofs->indexdir) {
|
|
index = ovl_get_index_fh(ofs, fh);
|
|
index = ovl_get_index_fh(ofs, fh);
|
|
err = PTR_ERR(index);
|
|
err = PTR_ERR(index);
|
|
if (IS_ERR(index)) {
|
|
if (IS_ERR(index)) {
|
|
- if (err != -ESTALE)
|
|
|
|
- return ERR_PTR(err);
|
|
|
|
-
|
|
|
|
- /* Found a whiteout index - treat as deleted inode */
|
|
|
|
- is_deleted = true;
|
|
|
|
index = NULL;
|
|
index = NULL;
|
|
|
|
+ goto out_err;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /* Then try to get upper dir by index */
|
|
|
|
|
|
+ /* Then try to get a connected upper dir by index */
|
|
if (index && d_is_dir(index)) {
|
|
if (index && d_is_dir(index)) {
|
|
struct dentry *upper = ovl_index_upper(ofs, index);
|
|
struct dentry *upper = ovl_index_upper(ofs, index);
|
|
|
|
|
|
@@ -734,24 +748,19 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- /* Then lookup origin by fh */
|
|
|
|
- err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
|
|
|
|
- if (err) {
|
|
|
|
- goto out_err;
|
|
|
|
- } else if (index) {
|
|
|
|
- err = ovl_verify_origin(index, origin.dentry, false);
|
|
|
|
|
|
+ /* Otherwise, get a connected non-upper dir or disconnected non-dir */
|
|
|
|
+ if (d_is_dir(origin.dentry) &&
|
|
|
|
+ (origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
|
|
|
|
+ dput(origin.dentry);
|
|
|
|
+ origin.dentry = NULL;
|
|
|
|
+ err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
|
|
if (err)
|
|
if (err)
|
|
goto out_err;
|
|
goto out_err;
|
|
- } else if (is_deleted) {
|
|
|
|
- /* Lookup deleted non-dir by origin inode */
|
|
|
|
- if (!d_is_dir(origin.dentry))
|
|
|
|
- inode = ovl_lookup_inode(sb, origin.dentry, false);
|
|
|
|
- err = -ESTALE;
|
|
|
|
- if (!inode || atomic_read(&inode->i_count) == 1)
|
|
|
|
|
|
+ }
|
|
|
|
+ if (index) {
|
|
|
|
+ err = ovl_verify_origin(index, origin.dentry, false);
|
|
|
|
+ if (err)
|
|
goto out_err;
|
|
goto out_err;
|
|
-
|
|
|
|
- /* Deleted but still open? */
|
|
|
|
- index = dget(ovl_i_dentry_upper(inode));
|
|
|
|
}
|
|
}
|
|
|
|
|
|
dentry = ovl_get_dentry(sb, NULL, &origin, index);
|
|
dentry = ovl_get_dentry(sb, NULL, &origin, index);
|
|
@@ -759,7 +768,6 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
|
|
out:
|
|
out:
|
|
dput(origin.dentry);
|
|
dput(origin.dentry);
|
|
dput(index);
|
|
dput(index);
|
|
- iput(inode);
|
|
|
|
return dentry;
|
|
return dentry;
|
|
|
|
|
|
out_err:
|
|
out_err:
|