Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 834f2a4a authored by Trond Myklebust's avatar Trond Myklebust
Browse files

VFS: Allow the filesystem to return a full file pointer on open intent



 This is needed by NFSv4 for atomicity reasons: our open command is in
 fact a lookup+open, so we need to be able to propagate open context
 information from lookup() into the resulting struct file's
 private_data field.

 Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 039c4d7a
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -126,8 +126,7 @@ asmlinkage long sys_uselib(const char __user * library)
	struct nameidata nd;
	int error;

	nd.intent.open.flags = FMODE_READ;
	error = __user_walk(library, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
	error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ);
	if (error)
		goto out;

@@ -139,7 +138,7 @@ asmlinkage long sys_uselib(const char __user * library)
	if (error)
		goto exit;

	file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
	file = nameidata_to_filp(&nd, O_RDONLY);
	error = PTR_ERR(file);
	if (IS_ERR(file))
		goto out;
@@ -167,6 +166,7 @@ asmlinkage long sys_uselib(const char __user * library)
out:
  	return error;
exit:
	release_open_intent(&nd);
	path_release(&nd);
	goto out;
}
@@ -490,8 +490,7 @@ struct file *open_exec(const char *name)
	int err;
	struct file *file;

	nd.intent.open.flags = FMODE_READ;
	err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
	err = path_lookup_open(name, LOOKUP_FOLLOW, &nd, FMODE_READ);
	file = ERR_PTR(err);

	if (!err) {
@@ -504,7 +503,7 @@ struct file *open_exec(const char *name)
				err = -EACCES;
			file = ERR_PTR(err);
			if (!err) {
				file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
				file = nameidata_to_filp(&nd, O_RDONLY);
				if (!IS_ERR(file)) {
					err = deny_write_access(file);
					if (err) {
@@ -516,6 +515,7 @@ struct file *open_exec(const char *name)
				return file;
			}
		}
		release_open_intent(&nd);
		path_release(&nd);
	}
	goto out;
+86 −7
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <linux/syscalls.h>
#include <linux/mount.h>
#include <linux/audit.h>
#include <linux/file.h>
#include <asm/namei.h>
#include <asm/uaccess.h>

@@ -317,6 +318,18 @@ void path_release_on_umount(struct nameidata *nd)
	mntput_no_expire(nd->mnt);
}

/**
 * release_open_intent - free up open intent resources
 * @nd: pointer to nameidata
 */
void release_open_intent(struct nameidata *nd)
{
	if (nd->intent.open.file->f_dentry == NULL)
		put_filp(nd->intent.open.file);
	else
		fput(nd->intent.open.file);
}

/*
 * Internal lookup() using the new generic dcache.
 * SMP-safe
@@ -1052,6 +1065,70 @@ int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata
	return retval;
}

static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags,
		struct nameidata *nd, int open_flags, int create_mode)
{
	struct file *filp = get_empty_filp();
	int err;

	if (filp == NULL)
		return -ENFILE;
	nd->intent.open.file = filp;
	nd->intent.open.flags = open_flags;
	nd->intent.open.create_mode = create_mode;
	err = path_lookup(name, lookup_flags|LOOKUP_OPEN, nd);
	if (IS_ERR(nd->intent.open.file)) {
		if (err == 0) {
			err = PTR_ERR(nd->intent.open.file);
			path_release(nd);
		}
	} else if (err != 0)
		release_open_intent(nd);
	return err;
}

/**
 * path_lookup_open - lookup a file path with open intent
 * @name: pointer to file name
 * @lookup_flags: lookup intent flags
 * @nd: pointer to nameidata
 * @open_flags: open intent flags
 */
int path_lookup_open(const char *name, unsigned int lookup_flags,
		struct nameidata *nd, int open_flags)
{
	return __path_lookup_intent_open(name, lookup_flags, nd,
			open_flags, 0);
}

/**
 * path_lookup_create - lookup a file path with open + create intent
 * @name: pointer to file name
 * @lookup_flags: lookup intent flags
 * @nd: pointer to nameidata
 * @open_flags: open intent flags
 * @create_mode: create intent flags
 */
int path_lookup_create(const char *name, unsigned int lookup_flags,
		struct nameidata *nd, int open_flags, int create_mode)
{
	return __path_lookup_intent_open(name, lookup_flags|LOOKUP_CREATE, nd,
			open_flags, create_mode);
}

int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
		struct nameidata *nd, int open_flags)
{
	char *tmp = getname(name);
	int err = PTR_ERR(tmp);

	if (!IS_ERR(tmp)) {
		err = __path_lookup_intent_open(tmp, lookup_flags, nd, open_flags, 0);
		putname(tmp);
	}
	return err;
}

/*
 * Restricted form of lookup. Doesn't follow links, single-component only,
 * needs parent already locked. Doesn't follow mounts.
@@ -1416,27 +1493,27 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
 */
int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
{
	int acc_mode, error = 0;
	int acc_mode, error;
	struct path path;
	struct dentry *dir;
	int count = 0;

	acc_mode = ACC_MODE(flag);

	/* O_TRUNC implies we need access checks for write permissions */
	if (flag & O_TRUNC)
		acc_mode |= MAY_WRITE;

	/* Allow the LSM permission hook to distinguish append 
	   access from general write access. */
	if (flag & O_APPEND)
		acc_mode |= MAY_APPEND;

	/* Fill in the open() intent data */
	nd->intent.open.flags = flag;
	nd->intent.open.create_mode = mode;

	/*
	 * The simplest case - just a plain lookup.
	 */
	if (!(flag & O_CREAT)) {
		error = path_lookup(pathname, lookup_flags(flag)|LOOKUP_OPEN, nd);
		error = path_lookup_open(pathname, lookup_flags(flag), nd, flag);
		if (error)
			return error;
		goto ok;
@@ -1445,7 +1522,7 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
	/*
	 * Create - we need to know the parent.
	 */
	error = path_lookup(pathname, LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE, nd);
	error = path_lookup_create(pathname, LOOKUP_PARENT, nd, flag, mode);
	if (error)
		return error;

@@ -1520,6 +1597,8 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
exit_dput:
	dput_path(&path, nd);
exit:
	if (!IS_ERR(nd->intent.open.file))
		release_open_intent(nd);
	path_release(nd);
	return error;

+65 −14
Original line number Diff line number Diff line
@@ -739,7 +739,8 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
}

static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
					int flags, struct file *f)
					int flags, struct file *f,
					int (*open)(struct inode *, struct file *))
{
	struct inode *inode;
	int error;
@@ -761,11 +762,14 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
	f->f_op = fops_get(inode->i_fop);
	file_move(f, &inode->i_sb->s_files);

	if (f->f_op && f->f_op->open) {
		error = f->f_op->open(inode,f);
	if (!open && f->f_op)
		open = f->f_op->open;
	if (open) {
		error = open(inode, f);
		if (error)
			goto cleanup_all;
	}

	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);

	file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
@@ -814,28 +818,75 @@ struct file *filp_open(const char * filename, int flags, int mode)
{
	int namei_flags, error;
	struct nameidata nd;
	struct file *f;

	namei_flags = flags;
	if ((namei_flags+1) & O_ACCMODE)
		namei_flags++;
	if (namei_flags & O_TRUNC)
		namei_flags |= 2;

	error = -ENFILE;
	f = get_empty_filp();
	if (f == NULL)
		return ERR_PTR(error);

	error = open_namei(filename, namei_flags, mode, &nd);
	if (!error)
		return __dentry_open(nd.dentry, nd.mnt, flags, f);
		return nameidata_to_filp(&nd, flags);

	put_filp(f);
	return ERR_PTR(error);
}
EXPORT_SYMBOL(filp_open);

/**
 * lookup_instantiate_filp - instantiates the open intent filp
 * @nd: pointer to nameidata
 * @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.
 * Note that in case of error, nd->intent.open.file is destroyed, but the
 * path information remains valid.
 * 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 *))
{
	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->mnt),
					     nd->intent.open.flags - 1,
					     nd->intent.open.file,
					     open);
out:
	return nd->intent.open.file;
out_err:
	release_open_intent(nd);
	nd->intent.open.file = (struct file *)dentry;
	goto out;
}
EXPORT_SYMBOL_GPL(lookup_instantiate_filp);

/**
 * nameidata_to_filp - convert a nameidata to an open filp.
 * @nd: pointer to nameidata
 * @flags: open flags
 *
 * Note that this function destroys the original nameidata
 */
struct file *nameidata_to_filp(struct nameidata *nd, int flags)
{
	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_dentry == NULL)
		filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL);
	else
		path_release(nd);
	return filp;
}

struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
{
	int error;
@@ -846,7 +897,7 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
	if (f == NULL)
		return ERR_PTR(error);

	return __dentry_open(dentry, mnt, flags, f);
	return __dentry_open(dentry, mnt, flags, f, NULL);
}
EXPORT_SYMBOL(dentry_open);

+8 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ struct vfsmount;
struct open_intent {
	int	flags;
	int	create_mode;
	struct file *file;
};

enum { MAX_NESTED_LINKS = 5 };
@@ -65,6 +66,13 @@ extern int FASTCALL(link_path_walk(const char *, struct nameidata *));
extern void path_release(struct nameidata *);
extern void path_release_on_umount(struct nameidata *);

extern int __user_path_lookup_open(const char __user *, unsigned lookup_flags, struct nameidata *nd, int open_flags);
extern int path_lookup_open(const char *, unsigned lookup_flags, struct nameidata *, int open_flags);
extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
		int (*open)(struct inode *, struct file *));
extern struct file *nameidata_to_filp(struct nameidata *nd, int flags);
extern void release_open_intent(struct nameidata *);

extern struct dentry * lookup_one_len(const char *, struct dentry *, int);
extern struct dentry * lookup_hash(struct qstr *, struct dentry *);