diff options
Diffstat (limited to 'fs/open.c')
| -rw-r--r-- | fs/open.c | 211 | 
1 files changed, 65 insertions, 146 deletions
diff --git a/fs/open.c b/fs/open.c index 1540632d838..1e914b397e1 100644 --- a/fs/open.c +++ b/fs/open.c @@ -537,25 +537,6 @@ static int chown_common(struct path *path, uid_t user, gid_t group)  	return error;  } -SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group) -{ -	struct path path; -	int error; - -	error = user_path(filename, &path); -	if (error) -		goto out; -	error = mnt_want_write(path.mnt); -	if (error) -		goto out_release; -	error = chown_common(&path, user, group); -	mnt_drop_write(path.mnt); -out_release: -	path_put(&path); -out: -	return error; -} -  SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,  		gid_t, group, int, flag)  { @@ -583,23 +564,15 @@ out:  	return error;  } -SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group) +SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group)  { -	struct path path; -	int error; +	return sys_fchownat(AT_FDCWD, filename, user, group, 0); +} -	error = user_lpath(filename, &path); -	if (error) -		goto out; -	error = mnt_want_write(path.mnt); -	if (error) -		goto out_release; -	error = chown_common(&path, user, group); -	mnt_drop_write(path.mnt); -out_release: -	path_put(&path); -out: -	return error; +SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group) +{ +	return sys_fchownat(AT_FDCWD, filename, user, group, +			    AT_SYMLINK_NOFOLLOW);  }  SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) @@ -667,10 +640,9 @@ int open_check_o_direct(struct file *f)  	return 0;  } -static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt, -				   struct file *f, -				   int (*open)(struct inode *, struct file *), -				   const struct cred *cred) +static int do_dentry_open(struct file *f, +			  int (*open)(struct inode *, struct file *), +			  const struct cred *cred)  {  	static const struct file_operations empty_fops = {};  	struct inode *inode; @@ -682,9 +654,9 @@ static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt,  	if (unlikely(f->f_flags & O_PATH))  		f->f_mode = FMODE_PATH; -	inode = dentry->d_inode; +	inode = f->f_path.dentry->d_inode;  	if (f->f_mode & FMODE_WRITE) { -		error = __get_file_write_access(inode, mnt); +		error = __get_file_write_access(inode, f->f_path.mnt);  		if (error)  			goto cleanup_file;  		if (!special_file(inode->i_mode)) @@ -692,14 +664,12 @@ static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt,  	}  	f->f_mapping = inode->i_mapping; -	f->f_path.dentry = dentry; -	f->f_path.mnt = mnt;  	f->f_pos = 0;  	file_sb_list_add(f, inode->i_sb);  	if (unlikely(f->f_mode & FMODE_PATH)) {  		f->f_op = &empty_fops; -		return f; +		return 0;  	}  	f->f_op = fops_get(inode->i_fop); @@ -726,10 +696,11 @@ static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt,  	file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping); -	return f; +	return 0;  cleanup_all:  	fops_put(f->f_op); +	file_sb_list_del(f);  	if (f->f_mode & FMODE_WRITE) {  		put_write_access(inode);  		if (!special_file(inode->i_mode)) { @@ -740,124 +711,62 @@ cleanup_all:  			 * here, so just reset the state.  			 */  			file_reset_write(f); -			mnt_drop_write(mnt); +			mnt_drop_write(f->f_path.mnt);  		}  	} -	file_sb_list_del(f); -	f->f_path.dentry = NULL; -	f->f_path.mnt = NULL;  cleanup_file: -	dput(dentry); -	mntput(mnt); -	return ERR_PTR(error); -} - -static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, -				struct file *f, -				int (*open)(struct inode *, struct file *), -				const struct cred *cred) -{ -	struct file *res = do_dentry_open(dentry, mnt, f, open, cred); -	if (!IS_ERR(res)) { -		int error = open_check_o_direct(f); -		if (error) { -			fput(res); -			res = ERR_PTR(error); -		} -	} else { -		put_filp(f); -	} -	return res; +	path_put(&f->f_path); +	f->f_path.mnt = NULL; +	f->f_path.dentry = NULL; +	return error;  }  /** - * lookup_instantiate_filp - instantiates the open intent filp - * @nd: pointer to nameidata + * finish_open - finish opening a file + * @od: opaque open data   * @dentry: pointer to dentry   * @open: open callback   * - * Helper for filesystems that want to use lookup open intents and pass back - * a fully instantiated struct file to the caller. - * This function is meant to be called from within a filesystem's - * lookup method. - * Beware of calling it for non-regular files! Those ->open methods might block - * (e.g. in fifo_open), leaving you with parent locked (and in case of fifo, - * leading to a deadlock, as nobody can open that fifo anymore, because - * another process to open fifo will block on locked parent when doing lookup). - * Note that in case of error, nd->intent.open.file is destroyed, but the - * path information remains valid. + * This can be used to finish opening a file passed to i_op->atomic_open(). + *   * If the open callback is set to NULL, then the standard f_op->open()   * filesystem callback is substituted.   */ -struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, -		int (*open)(struct inode *, struct file *)) +int finish_open(struct file *file, struct dentry *dentry, +		int (*open)(struct inode *, struct file *), +		int *opened)  { -	const struct cred *cred = current_cred(); +	int error; +	BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */ -	if (IS_ERR(nd->intent.open.file)) -		goto out; -	if (IS_ERR(dentry)) -		goto out_err; -	nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->path.mnt), -					     nd->intent.open.file, -					     open, cred); -out: -	return nd->intent.open.file; -out_err: -	release_open_intent(nd); -	nd->intent.open.file = ERR_CAST(dentry); -	goto out; +	mntget(file->f_path.mnt); +	file->f_path.dentry = dget(dentry); + +	error = do_dentry_open(file, open, current_cred()); +	if (!error) +		*opened |= FILE_OPENED; + +	return error;  } -EXPORT_SYMBOL_GPL(lookup_instantiate_filp); +EXPORT_SYMBOL(finish_open);  /** - * nameidata_to_filp - convert a nameidata to an open filp. - * @nd: pointer to nameidata - * @flags: open flags + * finish_no_open - finish ->atomic_open() without opening the file + * + * @od: opaque open data + * @dentry: dentry or NULL (as returned from ->lookup())   * - * Note that this function destroys the original nameidata + * This can be used to set the result of a successful lookup in ->atomic_open(). + * The filesystem's atomic_open() method shall return NULL after calling this.   */ -struct file *nameidata_to_filp(struct nameidata *nd) +int finish_no_open(struct file *file, struct dentry *dentry)  { -	const struct cred *cred = current_cred(); -	struct file *filp; - -	/* Pick up the filp from the open intent */ -	filp = nd->intent.open.file; - -	/* Has the filesystem initialised the file for us? */ -	if (filp->f_path.dentry != NULL) { -		nd->intent.open.file = NULL; -	} else { -		struct file *res; - -		path_get(&nd->path); -		res = do_dentry_open(nd->path.dentry, nd->path.mnt, -				     filp, NULL, cred); -		if (!IS_ERR(res)) { -			int error; - -			nd->intent.open.file = NULL; -			BUG_ON(res != filp); - -			error = open_check_o_direct(filp); -			if (error) { -				fput(filp); -				filp = ERR_PTR(error); -			} -		} else { -			/* Allow nd->intent.open.file to be recycled */ -			filp = res; -		} -	} -	return filp; +	file->f_path.dentry = dentry; +	return 1;  } +EXPORT_SYMBOL(finish_no_open); -/* - * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an - * error. - */ -struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags, +struct file *dentry_open(const struct path *path, int flags,  			 const struct cred *cred)  {  	int error; @@ -866,18 +775,28 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,  	validate_creds(cred);  	/* We must always pass in a valid mount pointer. */ -	BUG_ON(!mnt); +	BUG_ON(!path->mnt);  	error = -ENFILE;  	f = get_empty_filp(); -	if (f == NULL) { -		dput(dentry); -		mntput(mnt); +	if (f == NULL)  		return ERR_PTR(error); -	}  	f->f_flags = flags; -	return __dentry_open(dentry, mnt, f, NULL, cred); +	f->f_path = *path; +	path_get(&f->f_path); +	error = do_dentry_open(f, NULL, cred); +	if (!error) { +		error = open_check_o_direct(f); +		if (error) { +			fput(f); +			f = ERR_PTR(error); +		} +	} else {  +		put_filp(f); +		f = ERR_PTR(error); +	} +	return f;  }  EXPORT_SYMBOL(dentry_open);  |