diff options
Diffstat (limited to 'fs/open.c')
| -rw-r--r-- | fs/open.c | 97 | 
1 files changed, 66 insertions, 31 deletions
diff --git a/fs/open.c b/fs/open.c index 182d8667b7b..9b33c0cbfac 100644 --- a/fs/open.c +++ b/fs/open.c @@ -61,33 +61,22 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,  	return ret;  } -static long do_sys_truncate(const char __user *pathname, loff_t length) +long vfs_truncate(struct path *path, loff_t length)  { -	struct path path;  	struct inode *inode; -	int error; - -	error = -EINVAL; -	if (length < 0)	/* sorry, but loff_t says... */ -		goto out; +	long error; -	error = user_path(pathname, &path); -	if (error) -		goto out; -	inode = path.dentry->d_inode; +	inode = path->dentry->d_inode;  	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */ -	error = -EISDIR;  	if (S_ISDIR(inode->i_mode)) -		goto dput_and_out; - -	error = -EINVAL; +		return -EISDIR;  	if (!S_ISREG(inode->i_mode)) -		goto dput_and_out; +		return -EINVAL; -	error = mnt_want_write(path.mnt); +	error = mnt_want_write(path->mnt);  	if (error) -		goto dput_and_out; +		goto out;  	error = inode_permission(inode, MAY_WRITE);  	if (error) @@ -111,19 +100,40 @@ static long do_sys_truncate(const char __user *pathname, loff_t length)  	error = locks_verify_truncate(inode, NULL, length);  	if (!error) -		error = security_path_truncate(&path); +		error = security_path_truncate(path);  	if (!error) -		error = do_truncate(path.dentry, length, 0, NULL); +		error = do_truncate(path->dentry, length, 0, NULL);  put_write_and_out:  	put_write_access(inode);  mnt_drop_write_and_out: -	mnt_drop_write(path.mnt); -dput_and_out: -	path_put(&path); +	mnt_drop_write(path->mnt);  out:  	return error;  } +EXPORT_SYMBOL_GPL(vfs_truncate); + +static long do_sys_truncate(const char __user *pathname, loff_t length) +{ +	unsigned int lookup_flags = LOOKUP_FOLLOW; +	struct path path; +	int error; + +	if (length < 0)	/* sorry, but loff_t says... */ +		return -EINVAL; + +retry: +	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); +	if (!error) { +		error = vfs_truncate(&path, length); +		path_put(&path); +	} +	if (retry_estale(error, lookup_flags)) { +		lookup_flags |= LOOKUP_REVAL; +		goto retry; +	} +	return error; +}  SYSCALL_DEFINE2(truncate, const char __user *, path, long, length)  { @@ -306,6 +316,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)  	struct path path;  	struct inode *inode;  	int res; +	unsigned int lookup_flags = LOOKUP_FOLLOW;  	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */  		return -EINVAL; @@ -328,8 +339,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)  	}  	old_cred = override_creds(override_cred); - -	res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path); +retry: +	res = user_path_at(dfd, filename, lookup_flags, &path);  	if (res)  		goto out; @@ -364,6 +375,10 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)  out_path_release:  	path_put(&path); +	if (retry_estale(res, lookup_flags)) { +		lookup_flags |= LOOKUP_REVAL; +		goto retry; +	}  out:  	revert_creds(old_cred);  	put_cred(override_cred); @@ -379,8 +394,9 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename)  {  	struct path path;  	int error; - -	error = user_path_dir(filename, &path); +	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; +retry: +	error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);  	if (error)  		goto out; @@ -392,6 +408,10 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename)  dput_and_out:  	path_put(&path); +	if (retry_estale(error, lookup_flags)) { +		lookup_flags |= LOOKUP_REVAL; +		goto retry; +	}  out:  	return error;  } @@ -425,8 +445,9 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)  {  	struct path path;  	int error; - -	error = user_path_dir(filename, &path); +	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; +retry: +	error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);  	if (error)  		goto out; @@ -445,6 +466,10 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)  	error = 0;  dput_and_out:  	path_put(&path); +	if (retry_estale(error, lookup_flags)) { +		lookup_flags |= LOOKUP_REVAL; +		goto retry; +	}  out:  	return error;  } @@ -489,11 +514,16 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, umode_t, mode  {  	struct path path;  	int error; - -	error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path); +	unsigned int lookup_flags = LOOKUP_FOLLOW; +retry: +	error = user_path_at(dfd, filename, lookup_flags, &path);  	if (!error) {  		error = chmod_common(&path, mode);  		path_put(&path); +		if (retry_estale(error, lookup_flags)) { +			lookup_flags |= LOOKUP_REVAL; +			goto retry; +		}  	}  	return error;  } @@ -552,6 +582,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,  	lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;  	if (flag & AT_EMPTY_PATH)  		lookup_flags |= LOOKUP_EMPTY; +retry:  	error = user_path_at(dfd, filename, lookup_flags, &path);  	if (error)  		goto out; @@ -562,6 +593,10 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,  	mnt_drop_write(path.mnt);  out_release:  	path_put(&path); +	if (retry_estale(error, lookup_flags)) { +		lookup_flags |= LOOKUP_REVAL; +		goto retry; +	}  out:  	return error;  }  |