diff options
| -rw-r--r-- | fs/namei.c | 37 | ||||
| -rw-r--r-- | include/linux/errno.h | 1 | 
2 files changed, 36 insertions, 2 deletions
diff --git a/fs/namei.c b/fs/namei.c index 998d5316921..7d694194024 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -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: diff --git a/include/linux/errno.h b/include/linux/errno.h index 2d09bfa5c26..e0de516374d 100644 --- a/include/linux/errno.h +++ b/include/linux/errno.h @@ -17,6 +17,7 @@  #define ENOIOCTLCMD	515	/* No ioctl command */  #define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */  #define EPROBE_DEFER	517	/* Driver requests probe retry */ +#define EOPENSTALE	518	/* open found a stale dentry */  /* Defined for the NFSv3 protocol */  #define EBADHANDLE	521	/* Illegal NFS file handle */  |