diff options
Diffstat (limited to 'fs/open.c')
| -rw-r--r-- | fs/open.c | 258 | 
1 files changed, 120 insertions, 138 deletions
diff --git a/fs/open.c b/fs/open.c index e1f2cdb91a4..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)  { @@ -132,27 +142,27 @@ SYSCALL_DEFINE2(truncate, const char __user *, path, long, length)  static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)  { -	struct inode * inode; +	struct inode *inode;  	struct dentry *dentry; -	struct file * file; +	struct fd f;  	int error;  	error = -EINVAL;  	if (length < 0)  		goto out;  	error = -EBADF; -	file = fget(fd); -	if (!file) +	f = fdget(fd); +	if (!f.file)  		goto out;  	/* explicitly opened as large or we are on 64-bit box */ -	if (file->f_flags & O_LARGEFILE) +	if (f.file->f_flags & O_LARGEFILE)  		small = 0; -	dentry = file->f_path.dentry; +	dentry = f.file->f_path.dentry;  	inode = dentry->d_inode;  	error = -EINVAL; -	if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE)) +	if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))  		goto out_putf;  	error = -EINVAL; @@ -165,14 +175,14 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)  		goto out_putf;  	sb_start_write(inode->i_sb); -	error = locks_verify_truncate(inode, file, length); +	error = locks_verify_truncate(inode, f.file, length);  	if (!error) -		error = security_path_truncate(&file->f_path); +		error = security_path_truncate(&f.file->f_path);  	if (!error) -		error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file); +		error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);  	sb_end_write(inode->i_sb);  out_putf: -	fput(file); +	fdput(f);  out:  	return error;  } @@ -276,15 +286,13 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)  SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len)  { -	struct file *file; +	struct fd f = fdget(fd);  	int error = -EBADF; -	file = fget(fd); -	if (file) { -		error = do_fallocate(file, mode, offset, len); -		fput(file); +	if (f.file) { +		error = do_fallocate(f.file, mode, offset, len); +		fdput(f);  	} -  	return error;  } @@ -308,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; @@ -330,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; @@ -366,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); @@ -381,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; @@ -394,22 +408,25 @@ 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;  }  SYSCALL_DEFINE1(fchdir, unsigned int, fd)  { -	struct file *file; +	struct fd f = fdget_raw(fd);  	struct inode *inode; -	int error, fput_needed; +	int error = -EBADF;  	error = -EBADF; -	file = fget_raw_light(fd, &fput_needed); -	if (!file) +	if (!f.file)  		goto out; -	inode = file->f_path.dentry->d_inode; +	inode = f.file->f_path.dentry->d_inode;  	error = -ENOTDIR;  	if (!S_ISDIR(inode->i_mode)) @@ -417,9 +434,9 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)  	error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);  	if (!error) -		set_fs_pwd(current->fs, &file->f_path); +		set_fs_pwd(current->fs, &f.file->f_path);  out_putf: -	fput_light(file, fput_needed); +	fdput(f);  out:  	return error;  } @@ -428,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; @@ -438,7 +456,7 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)  		goto dput_and_out;  	error = -EPERM; -	if (!capable(CAP_SYS_CHROOT)) +	if (!nsown_capable(CAP_SYS_CHROOT))  		goto dput_and_out;  	error = security_path_chroot(&path);  	if (error) @@ -448,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;  } @@ -481,7 +503,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode)  	file = fget(fd);  	if (file) { -		audit_inode(NULL, file->f_path.dentry); +		audit_inode(NULL, file->f_path.dentry, 0);  		err = chmod_common(&file->f_path, mode);  		fput(file);  	} @@ -492,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;  } @@ -534,7 +561,7 @@ static int chown_common(struct path *path, uid_t user, gid_t group)  		newattrs.ia_valid |=  			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;  	mutex_lock(&inode->i_mutex); -	error = security_path_chown(path, user, group); +	error = security_path_chown(path, uid, gid);  	if (!error)  		error = notify_change(path->dentry, &newattrs);  	mutex_unlock(&inode->i_mutex); @@ -555,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; @@ -565,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;  } @@ -582,23 +614,20 @@ SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group  SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group)  { -	struct file * file; +	struct fd f = fdget(fd);  	int error = -EBADF; -	struct dentry * dentry; -	file = fget(fd); -	if (!file) +	if (!f.file)  		goto out; -	error = mnt_want_write_file(file); +	error = mnt_want_write_file(f.file);  	if (error)  		goto out_fput; -	dentry = file->f_path.dentry; -	audit_inode(NULL, dentry); -	error = chown_common(&file->f_path, user, group); -	mnt_drop_write_file(file); +	audit_inode(NULL, f.file->f_path.dentry, 0); +	error = chown_common(&f.file->f_path, user, group); +	mnt_drop_write_file(f.file);  out_fput: -	fput(file); +	fdput(f);  out:  	return error;  } @@ -803,50 +832,6 @@ struct file *dentry_open(const struct path *path, int flags,  }  EXPORT_SYMBOL(dentry_open); -static void __put_unused_fd(struct files_struct *files, unsigned int fd) -{ -	struct fdtable *fdt = files_fdtable(files); -	__clear_open_fd(fd, fdt); -	if (fd < files->next_fd) -		files->next_fd = fd; -} - -void put_unused_fd(unsigned int fd) -{ -	struct files_struct *files = current->files; -	spin_lock(&files->file_lock); -	__put_unused_fd(files, fd); -	spin_unlock(&files->file_lock); -} - -EXPORT_SYMBOL(put_unused_fd); - -/* - * Install a file pointer in the fd array. - * - * The VFS is full of places where we drop the files lock between - * setting the open_fds bitmap and installing the file in the file - * array.  At any such point, we are vulnerable to a dup2() race - * installing a file in the array before us.  We need to detect this and - * fput() the struct file we are about to overwrite in this case. - * - * It should never happen - if we allow dup2() do it, _really_ bad things - * will follow. - */ - -void fd_install(unsigned int fd, struct file *file) -{ -	struct files_struct *files = current->files; -	struct fdtable *fdt; -	spin_lock(&files->file_lock); -	fdt = files_fdtable(files); -	BUG_ON(fdt->fd[fd] != NULL); -	rcu_assign_pointer(fdt->fd[fd], file); -	spin_unlock(&files->file_lock); -} - -EXPORT_SYMBOL(fd_install); -  static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)  {  	int lookup_flags = 0; @@ -858,7 +843,7 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o  		op->mode = 0;  	/* Must never be set by userspace */ -	flags &= ~FMODE_NONOTIFY; +	flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC;  	/*  	 * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only @@ -909,6 +894,24 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o  }  /** + * file_open_name - open file and return file pointer + * + * @name:	struct filename containing path to open + * @flags:	open flags as per the open(2) second argument + * @mode:	mode for the new file if O_CREAT is set, else ignored + * + * This is the helper to open a file from kernelspace if you really + * have to.  But in generally you should not do this, so please move + * along, nothing to see here.. + */ +struct file *file_open_name(struct filename *name, int flags, umode_t mode) +{ +	struct open_flags op; +	int lookup = build_open_flags(flags, mode, &op); +	return do_filp_open(AT_FDCWD, name, &op, lookup); +} + +/**   * filp_open - open file and return file pointer   *   * @filename:	path to open @@ -921,9 +924,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o   */  struct file *filp_open(const char *filename, int flags, umode_t mode)  { -	struct open_flags op; -	int lookup = build_open_flags(flags, mode, &op); -	return do_filp_open(AT_FDCWD, filename, &op, lookup); +	struct filename name = {.name = filename}; +	return file_open_name(&name, flags, mode);  }  EXPORT_SYMBOL(filp_open); @@ -945,7 +947,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)  {  	struct open_flags op;  	int lookup = build_open_flags(flags, mode, &op); -	char *tmp = getname(filename); +	struct filename *tmp = getname(filename);  	int fd = PTR_ERR(tmp);  	if (!IS_ERR(tmp)) { @@ -1038,23 +1040,7 @@ EXPORT_SYMBOL(filp_close);   */  SYSCALL_DEFINE1(close, unsigned int, fd)  { -	struct file * filp; -	struct files_struct *files = current->files; -	struct fdtable *fdt; -	int retval; - -	spin_lock(&files->file_lock); -	fdt = files_fdtable(files); -	if (fd >= fdt->max_fds) -		goto out_unlock; -	filp = fdt->fd[fd]; -	if (!filp) -		goto out_unlock; -	rcu_assign_pointer(fdt->fd[fd], NULL); -	__clear_close_on_exec(fd, fdt); -	__put_unused_fd(files, fd); -	spin_unlock(&files->file_lock); -	retval = filp_close(filp, files); +	int retval = __close_fd(current->files, fd);  	/* can't restart close syscall because file table entry was cleared */  	if (unlikely(retval == -ERESTARTSYS || @@ -1064,10 +1050,6 @@ SYSCALL_DEFINE1(close, unsigned int, fd)  		retval = -EINTR;  	return retval; - -out_unlock: -	spin_unlock(&files->file_lock); -	return -EBADF;  }  EXPORT_SYMBOL(sys_close);  |