open-style analog of vfs_path_lookup()
new function: file_open_root(dentry, mnt, name, flags) opens the file
vfs_path_lookup would arrive to.
Note that name can be empty; in that case the usual requirement that
dentry should be a directory is lifted.
open-coded equivalents switched to it, may_open() got down exactly
one caller and became static.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index 975613b..c70e047 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -124,35 +124,18 @@
#if 0
void mconsole_proc(struct mc_request *req)
{
- struct nameidata nd;
struct vfsmount *mnt = current->nsproxy->pid_ns->proc_mnt;
struct file *file;
- int n, err;
+ int n;
char *ptr = req->request.data, *buf;
mm_segment_t old_fs = get_fs();
ptr += strlen("proc");
ptr = skip_spaces(ptr);
- err = vfs_path_lookup(mnt->mnt_root, mnt, ptr, LOOKUP_FOLLOW, &nd);
- if (err) {
- mconsole_reply(req, "Failed to look up file", 1, 0);
- goto out;
- }
-
- err = may_open(&nd.path, MAY_READ, O_RDONLY);
- if (result) {
- mconsole_reply(req, "Failed to open file", 1, 0);
- path_put(&nd.path);
- goto out;
- }
-
- file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
- current_cred());
- err = PTR_ERR(file);
+ file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY);
if (IS_ERR(file)) {
mconsole_reply(req, "Failed to open file", 1, 0);
- path_put(&nd.path);
goto out;
}
diff --git a/fs/internal.h b/fs/internal.h
index 6fdbdf2..52abc52 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -114,6 +114,8 @@
};
extern struct file *do_filp_open(int dfd, const char *pathname,
const struct open_flags *op, int lookup_flags);
+extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
+ const char *, const struct open_flags *, int lookup_flags);
/*
* inode.c
diff --git a/fs/namei.c b/fs/namei.c
index 8ee7785..abc8d2d 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1487,11 +1487,13 @@
nd->depth = 0;
if (flags & LOOKUP_ROOT) {
struct inode *inode = nd->root.dentry->d_inode;
- if (!inode->i_op->lookup)
- return -ENOTDIR;
- retval = inode_permission(inode, MAY_EXEC);
- if (retval)
- return retval;
+ if (*name) {
+ if (!inode->i_op->lookup)
+ return -ENOTDIR;
+ retval = inode_permission(inode, MAY_EXEC);
+ if (retval)
+ return retval;
+ }
nd->path = nd->root;
nd->inode = inode;
if (flags & LOOKUP_RCU) {
@@ -1937,7 +1939,7 @@
return error;
}
-int may_open(struct path *path, int acc_mode, int flag)
+static int may_open(struct path *path, int acc_mode, int flag)
{
struct dentry *dentry = path->dentry;
struct inode *inode = dentry->d_inode;
@@ -2250,11 +2252,10 @@
}
static struct file *path_openat(int dfd, const char *pathname,
- const struct open_flags *op, int flags)
+ struct nameidata *nd, const struct open_flags *op, int flags)
{
struct file *base = NULL;
struct file *filp;
- struct nameidata nd;
struct path path;
int count = 0;
int error;
@@ -2264,27 +2265,27 @@
return ERR_PTR(-ENFILE);
filp->f_flags = op->open_flag;
- nd.intent.open.file = filp;
- nd.intent.open.flags = open_to_namei_flags(op->open_flag);
- nd.intent.open.create_mode = op->mode;
+ nd->intent.open.file = filp;
+ nd->intent.open.flags = open_to_namei_flags(op->open_flag);
+ nd->intent.open.create_mode = op->mode;
- error = path_init(dfd, pathname, flags | LOOKUP_PARENT, &nd, &base);
+ error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
if (unlikely(error))
goto out_filp;
current->total_link_count = 0;
- error = link_path_walk(pathname, &nd);
+ error = link_path_walk(pathname, nd);
if (unlikely(error))
goto out_filp;
- filp = do_last(&nd, &path, op, pathname);
+ filp = do_last(nd, &path, op, pathname);
while (unlikely(!filp)) { /* trailing symlink */
struct path link = path;
struct inode *linki = link.dentry->d_inode;
void *cookie;
- if (!(nd.flags & LOOKUP_FOLLOW) || count++ == 32) {
- path_put_conditional(&path, &nd);
- path_put(&nd.path);
+ if (!(nd->flags & LOOKUP_FOLLOW) || count++ == 32) {
+ path_put_conditional(&path, nd);
+ path_put(&nd->path);
filp = ERR_PTR(-ELOOP);
break;
}
@@ -2299,23 +2300,23 @@
* have to putname() it when we are done. Procfs-like symlinks
* just set LAST_BIND.
*/
- nd.flags |= LOOKUP_PARENT;
- nd.flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
- error = __do_follow_link(&link, &nd, &cookie);
+ nd->flags |= LOOKUP_PARENT;
+ nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
+ error = __do_follow_link(&link, nd, &cookie);
if (unlikely(error))
filp = ERR_PTR(error);
else
- filp = do_last(&nd, &path, op, pathname);
+ filp = do_last(nd, &path, op, pathname);
if (!IS_ERR(cookie) && linki->i_op->put_link)
- linki->i_op->put_link(link.dentry, &nd, cookie);
+ linki->i_op->put_link(link.dentry, nd, cookie);
path_put(&link);
}
out:
- if (nd.root.mnt && !(nd.flags & LOOKUP_ROOT))
- path_put(&nd.root);
+ if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))
+ path_put(&nd->root);
if (base)
fput(base);
- release_open_intent(&nd);
+ release_open_intent(nd);
return filp;
out_filp:
@@ -2326,16 +2327,39 @@
struct file *do_filp_open(int dfd, const char *pathname,
const struct open_flags *op, int flags)
{
+ struct nameidata nd;
struct file *filp;
- filp = path_openat(dfd, pathname, op, flags | LOOKUP_RCU);
+ filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
if (unlikely(filp == ERR_PTR(-ECHILD)))
- filp = path_openat(dfd, pathname, op, flags);
+ filp = path_openat(dfd, pathname, &nd, op, flags);
if (unlikely(filp == ERR_PTR(-ESTALE)))
- filp = path_openat(dfd, pathname, op, flags | LOOKUP_REVAL);
+ filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
return filp;
}
+struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const struct open_flags *op, int flags)
+{
+ struct nameidata nd;
+ struct file *file;
+
+ nd.root.mnt = mnt;
+ nd.root.dentry = dentry;
+
+ flags |= LOOKUP_ROOT;
+
+ if (dentry->d_inode->i_op->follow_link)
+ return ERR_PTR(-ELOOP);
+
+ file = path_openat(-1, name, &nd, op, flags | LOOKUP_RCU);
+ if (unlikely(file == ERR_PTR(-ECHILD)))
+ file = path_openat(-1, name, &nd, op, flags);
+ if (unlikely(file == ERR_PTR(-ESTALE)))
+ file = path_openat(-1, name, &nd, op, flags | LOOKUP_REVAL);
+ return file;
+}
+
/**
* lookup_create - lookup a dentry, creating it if it doesn't exist
* @nd: nameidata info
diff --git a/fs/nfsctl.c b/fs/nfsctl.c
index bf9cbd2..124e8fc 100644
--- a/fs/nfsctl.c
+++ b/fs/nfsctl.c
@@ -22,30 +22,17 @@
static struct file *do_open(char *name, int flags)
{
- struct nameidata nd;
struct vfsmount *mnt;
- int error;
+ struct file *file;
mnt = do_kern_mount("nfsd", 0, "nfsd", NULL);
if (IS_ERR(mnt))
return (struct file *)mnt;
- error = vfs_path_lookup(mnt->mnt_root, mnt, name, 0, &nd);
+ file = file_open_root(mnt->mnt_root, mnt, name, flags);
+
mntput(mnt); /* drop do_kern_mount reference */
- if (error)
- return ERR_PTR(error);
-
- if (flags == O_RDWR)
- error = may_open(&nd.path, MAY_READ|MAY_WRITE, flags);
- else
- error = may_open(&nd.path, MAY_WRITE, flags);
-
- if (!error)
- return dentry_open(nd.path.dentry, nd.path.mnt, flags,
- current_cred());
-
- path_put(&nd.path);
- return ERR_PTR(error);
+ return file;
}
static struct {
diff --git a/fs/open.c b/fs/open.c
index d05e18c..48afc5c 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -959,6 +959,20 @@
}
EXPORT_SYMBOL(filp_open);
+struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
+ const char *filename, int flags)
+{
+ struct open_flags op;
+ int lookup = build_open_flags(flags, 0, &op);
+ if (flags & O_CREAT)
+ return ERR_PTR(-EINVAL);
+ if (!filename && (flags & O_DIRECTORY))
+ if (!dentry->d_inode->i_op->lookup)
+ return ERR_PTR(-ENOTDIR);
+ return do_file_open_root(dentry, mnt, filename, &op, lookup);
+}
+EXPORT_SYMBOL(file_open_root);
+
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
struct open_flags op;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 9c75714..bf5c3c8 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1990,6 +1990,8 @@
extern long do_sys_open(int dfd, const char __user *filename, int flags,
int mode);
extern struct file *filp_open(const char *, int, int);
+extern struct file *file_open_root(struct dentry *, struct vfsmount *,
+ const char *, int);
extern struct file * dentry_open(struct dentry *, struct vfsmount *, int,
const struct cred *);
extern int filp_close(struct file *, fl_owner_t id);
@@ -2205,8 +2207,6 @@
extern struct file *create_write_pipe(int flags);
extern void free_write_pipe(struct file *);
-extern int may_open(struct path *, int, int);
-
extern int kernel_read(struct file *, loff_t, char *, unsigned long);
extern struct file * open_exec(const char *);
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c
index b875bed..3b8e028 100644
--- a/kernel/sysctl_binary.c
+++ b/kernel/sysctl_binary.c
@@ -1321,13 +1321,11 @@
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
{
const struct bin_table *table = NULL;
- struct nameidata nd;
struct vfsmount *mnt;
struct file *file;
ssize_t result;
char *pathname;
int flags;
- int acc_mode;
pathname = sysctl_getname(name, nlen, &table);
result = PTR_ERR(pathname);
@@ -1337,28 +1335,17 @@
/* How should the sysctl be accessed? */
if (oldval && oldlen && newval && newlen) {
flags = O_RDWR;
- acc_mode = MAY_READ | MAY_WRITE;
} else if (newval && newlen) {
flags = O_WRONLY;
- acc_mode = MAY_WRITE;
} else if (oldval && oldlen) {
flags = O_RDONLY;
- acc_mode = MAY_READ;
} else {
result = 0;
goto out_putname;
}
mnt = current->nsproxy->pid_ns->proc_mnt;
- result = vfs_path_lookup(mnt->mnt_root, mnt, pathname, 0, &nd);
- if (result)
- goto out_putname;
-
- result = may_open(&nd.path, acc_mode, flags);
- if (result)
- goto out_putpath;
-
- file = dentry_open(nd.path.dentry, nd.path.mnt, flags, current_cred());
+ file = file_open_root(mnt->mnt_root, mnt, pathname, flags);
result = PTR_ERR(file);
if (IS_ERR(file))
goto out_putname;
@@ -1370,10 +1357,6 @@
putname(pathname);
out:
return result;
-
-out_putpath:
- path_put(&nd.path);
- goto out_putname;
}