|
@@ -2202,6 +2202,8 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|
|
struct file *filp;
|
|
|
struct inode *inode;
|
|
|
int symlink_ok = 0;
|
|
|
+ struct path save_parent = { .dentry = NULL, .mnt = NULL };
|
|
|
+ bool retried = false;
|
|
|
int error;
|
|
|
|
|
|
nd->flags &= ~LOOKUP_PARENT;
|
|
@@ -2267,6 +2269,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|
|
if (nd->last.name[nd->last.len])
|
|
|
goto exit;
|
|
|
|
|
|
+retry_lookup:
|
|
|
mutex_lock(&dir->d_inode->i_mutex);
|
|
|
|
|
|
dentry = lookup_hash(nd);
|
|
@@ -2349,12 +2352,21 @@ finish_lookup:
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- path_to_nameidata(path, nd);
|
|
|
+ if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) {
|
|
|
+ path_to_nameidata(path, nd);
|
|
|
+ } else {
|
|
|
+ save_parent.dentry = nd->path.dentry;
|
|
|
+ save_parent.mnt = mntget(path->mnt);
|
|
|
+ nd->path.dentry = path->dentry;
|
|
|
+
|
|
|
+ }
|
|
|
nd->inode = inode;
|
|
|
/* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */
|
|
|
error = complete_walk(nd);
|
|
|
- if (error)
|
|
|
+ if (error) {
|
|
|
+ path_put(&save_parent);
|
|
|
return ERR_PTR(error);
|
|
|
+ }
|
|
|
error = -EISDIR;
|
|
|
if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode))
|
|
|
goto exit;
|
|
@@ -2377,6 +2389,20 @@ common:
|
|
|
if (error)
|
|
|
goto exit;
|
|
|
filp = nameidata_to_filp(nd);
|
|
|
+ if (filp == ERR_PTR(-EOPENSTALE) && save_parent.dentry && !retried) {
|
|
|
+ BUG_ON(save_parent.dentry != dir);
|
|
|
+ path_put(&nd->path);
|
|
|
+ nd->path = save_parent;
|
|
|
+ nd->inode = dir->d_inode;
|
|
|
+ save_parent.mnt = NULL;
|
|
|
+ save_parent.dentry = NULL;
|
|
|
+ if (want_write) {
|
|
|
+ mnt_drop_write(nd->path.mnt);
|
|
|
+ want_write = 0;
|
|
|
+ }
|
|
|
+ retried = true;
|
|
|
+ goto retry_lookup;
|
|
|
+ }
|
|
|
if (!IS_ERR(filp)) {
|
|
|
error = ima_file_check(filp, op->acc_mode);
|
|
|
if (error) {
|
|
@@ -2396,6 +2422,7 @@ common:
|
|
|
out:
|
|
|
if (want_write)
|
|
|
mnt_drop_write(nd->path.mnt);
|
|
|
+ path_put(&save_parent);
|
|
|
terminate_walk(nd);
|
|
|
return filp;
|
|
|
|
|
@@ -2459,6 +2486,12 @@ out:
|
|
|
if (base)
|
|
|
fput(base);
|
|
|
release_open_intent(nd);
|
|
|
+ if (filp == ERR_PTR(-EOPENSTALE)) {
|
|
|
+ if (flags & LOOKUP_RCU)
|
|
|
+ filp = ERR_PTR(-ECHILD);
|
|
|
+ else
|
|
|
+ filp = ERR_PTR(-ESTALE);
|
|
|
+ }
|
|
|
return filp;
|
|
|
|
|
|
out_filp:
|