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: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
93
fs/namei.c
93
fs/namei.c
@ -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 @@ out:
|
||||
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 @@ ok:
|
||||
exit_dput:
|
||||
dput_path(&path, nd);
|
||||
exit:
|
||||
if (!IS_ERR(nd->intent.open.file))
|
||||
release_open_intent(nd);
|
||||
path_release(nd);
|
||||
return error;
|
||||
|
||||
|
Reference in New Issue
Block a user