|
@@ -31,7 +31,6 @@
|
|
#include <linux/file.h>
|
|
#include <linux/file.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/device_cgroup.h>
|
|
#include <linux/device_cgroup.h>
|
|
-#include <asm/namei.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
|
|
#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
|
|
@@ -185,6 +184,8 @@ int generic_permission(struct inode *inode, int mask,
|
|
{
|
|
{
|
|
umode_t mode = inode->i_mode;
|
|
umode_t mode = inode->i_mode;
|
|
|
|
|
|
|
|
+ mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
|
|
|
|
+
|
|
if (current->fsuid == inode->i_uid)
|
|
if (current->fsuid == inode->i_uid)
|
|
mode >>= 6;
|
|
mode >>= 6;
|
|
else {
|
|
else {
|
|
@@ -203,7 +204,7 @@ int generic_permission(struct inode *inode, int mask,
|
|
/*
|
|
/*
|
|
* If the DACs are ok we don't need any capability check.
|
|
* If the DACs are ok we don't need any capability check.
|
|
*/
|
|
*/
|
|
- if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
|
|
|
|
|
|
+ if ((mask & ~mode) == 0)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
check_capabilities:
|
|
check_capabilities:
|
|
@@ -226,13 +227,9 @@ int generic_permission(struct inode *inode, int mask,
|
|
return -EACCES;
|
|
return -EACCES;
|
|
}
|
|
}
|
|
|
|
|
|
-int permission(struct inode *inode, int mask, struct nameidata *nd)
|
|
|
|
|
|
+int inode_permission(struct inode *inode, int mask)
|
|
{
|
|
{
|
|
- int retval, submask;
|
|
|
|
- struct vfsmount *mnt = NULL;
|
|
|
|
-
|
|
|
|
- if (nd)
|
|
|
|
- mnt = nd->path.mnt;
|
|
|
|
|
|
+ int retval;
|
|
|
|
|
|
if (mask & MAY_WRITE) {
|
|
if (mask & MAY_WRITE) {
|
|
umode_t mode = inode->i_mode;
|
|
umode_t mode = inode->i_mode;
|
|
@@ -251,19 +248,9 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
|
|
return -EACCES;
|
|
return -EACCES;
|
|
}
|
|
}
|
|
|
|
|
|
- if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
|
|
|
|
- /*
|
|
|
|
- * MAY_EXEC on regular files is denied if the fs is mounted
|
|
|
|
- * with the "noexec" flag.
|
|
|
|
- */
|
|
|
|
- if (mnt && (mnt->mnt_flags & MNT_NOEXEC))
|
|
|
|
- return -EACCES;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/* Ordinary permission routines do not understand MAY_APPEND. */
|
|
/* Ordinary permission routines do not understand MAY_APPEND. */
|
|
- submask = mask & ~MAY_APPEND;
|
|
|
|
if (inode->i_op && inode->i_op->permission) {
|
|
if (inode->i_op && inode->i_op->permission) {
|
|
- retval = inode->i_op->permission(inode, submask, nd);
|
|
|
|
|
|
+ retval = inode->i_op->permission(inode, mask);
|
|
if (!retval) {
|
|
if (!retval) {
|
|
/*
|
|
/*
|
|
* Exec permission on a regular file is denied if none
|
|
* Exec permission on a regular file is denied if none
|
|
@@ -277,7 +264,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
|
|
return -EACCES;
|
|
return -EACCES;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- retval = generic_permission(inode, submask, NULL);
|
|
|
|
|
|
+ retval = generic_permission(inode, mask, NULL);
|
|
}
|
|
}
|
|
if (retval)
|
|
if (retval)
|
|
return retval;
|
|
return retval;
|
|
@@ -286,7 +273,8 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
|
|
if (retval)
|
|
if (retval)
|
|
return retval;
|
|
return retval;
|
|
|
|
|
|
- return security_inode_permission(inode, mask, nd);
|
|
|
|
|
|
+ return security_inode_permission(inode,
|
|
|
|
+ mask & (MAY_READ|MAY_WRITE|MAY_EXEC));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -301,7 +289,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
|
|
*/
|
|
*/
|
|
int vfs_permission(struct nameidata *nd, int mask)
|
|
int vfs_permission(struct nameidata *nd, int mask)
|
|
{
|
|
{
|
|
- return permission(nd->path.dentry->d_inode, mask, nd);
|
|
|
|
|
|
+ return inode_permission(nd->path.dentry->d_inode, mask);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -318,7 +306,7 @@ int vfs_permission(struct nameidata *nd, int mask)
|
|
*/
|
|
*/
|
|
int file_permission(struct file *file, int mask)
|
|
int file_permission(struct file *file, int mask)
|
|
{
|
|
{
|
|
- return permission(file->f_path.dentry->d_inode, mask, NULL);
|
|
|
|
|
|
+ return inode_permission(file->f_path.dentry->d_inode, mask);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -459,8 +447,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
|
|
* short-cut DAC fails, then call permission() to do more
|
|
* short-cut DAC fails, then call permission() to do more
|
|
* complete permission check.
|
|
* complete permission check.
|
|
*/
|
|
*/
|
|
-static int exec_permission_lite(struct inode *inode,
|
|
|
|
- struct nameidata *nd)
|
|
|
|
|
|
+static int exec_permission_lite(struct inode *inode)
|
|
{
|
|
{
|
|
umode_t mode = inode->i_mode;
|
|
umode_t mode = inode->i_mode;
|
|
|
|
|
|
@@ -486,7 +473,7 @@ static int exec_permission_lite(struct inode *inode,
|
|
|
|
|
|
return -EACCES;
|
|
return -EACCES;
|
|
ok:
|
|
ok:
|
|
- return security_inode_permission(inode, MAY_EXEC, nd);
|
|
|
|
|
|
+ return security_inode_permission(inode, MAY_EXEC);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -519,7 +506,14 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
|
|
*/
|
|
*/
|
|
result = d_lookup(parent, name);
|
|
result = d_lookup(parent, name);
|
|
if (!result) {
|
|
if (!result) {
|
|
- struct dentry * dentry = d_alloc(parent, name);
|
|
|
|
|
|
+ struct dentry *dentry;
|
|
|
|
+
|
|
|
|
+ /* Don't create child dentry for a dead directory. */
|
|
|
|
+ result = ERR_PTR(-ENOENT);
|
|
|
|
+ if (IS_DEADDIR(dir))
|
|
|
|
+ goto out_unlock;
|
|
|
|
+
|
|
|
|
+ dentry = d_alloc(parent, name);
|
|
result = ERR_PTR(-ENOMEM);
|
|
result = ERR_PTR(-ENOMEM);
|
|
if (dentry) {
|
|
if (dentry) {
|
|
result = dir->i_op->lookup(dir, dentry, nd);
|
|
result = dir->i_op->lookup(dir, dentry, nd);
|
|
@@ -528,6 +522,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
|
|
else
|
|
else
|
|
result = dentry;
|
|
result = dentry;
|
|
}
|
|
}
|
|
|
|
+out_unlock:
|
|
mutex_unlock(&dir->i_mutex);
|
|
mutex_unlock(&dir->i_mutex);
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
@@ -545,27 +540,16 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
-static int __emul_lookup_dentry(const char *, struct nameidata *);
|
|
|
|
-
|
|
|
|
/* SMP-safe */
|
|
/* SMP-safe */
|
|
-static __always_inline int
|
|
|
|
|
|
+static __always_inline void
|
|
walk_init_root(const char *name, struct nameidata *nd)
|
|
walk_init_root(const char *name, struct nameidata *nd)
|
|
{
|
|
{
|
|
struct fs_struct *fs = current->fs;
|
|
struct fs_struct *fs = current->fs;
|
|
|
|
|
|
read_lock(&fs->lock);
|
|
read_lock(&fs->lock);
|
|
- if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
|
|
|
|
- nd->path = fs->altroot;
|
|
|
|
- path_get(&fs->altroot);
|
|
|
|
- read_unlock(&fs->lock);
|
|
|
|
- if (__emul_lookup_dentry(name,nd))
|
|
|
|
- return 0;
|
|
|
|
- read_lock(&fs->lock);
|
|
|
|
- }
|
|
|
|
nd->path = fs->root;
|
|
nd->path = fs->root;
|
|
path_get(&fs->root);
|
|
path_get(&fs->root);
|
|
read_unlock(&fs->lock);
|
|
read_unlock(&fs->lock);
|
|
- return 1;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -606,12 +590,9 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
|
|
|
|
|
|
if (*link == '/') {
|
|
if (*link == '/') {
|
|
path_put(&nd->path);
|
|
path_put(&nd->path);
|
|
- if (!walk_init_root(link, nd))
|
|
|
|
- /* weird __emul_prefix() stuff did it */
|
|
|
|
- goto out;
|
|
|
|
|
|
+ walk_init_root(link, nd);
|
|
}
|
|
}
|
|
res = link_path_walk(link, nd);
|
|
res = link_path_walk(link, nd);
|
|
-out:
|
|
|
|
if (nd->depth || res || nd->last_type!=LAST_NORM)
|
|
if (nd->depth || res || nd->last_type!=LAST_NORM)
|
|
return res;
|
|
return res;
|
|
/*
|
|
/*
|
|
@@ -889,7 +870,7 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
|
|
unsigned int c;
|
|
unsigned int c;
|
|
|
|
|
|
nd->flags |= LOOKUP_CONTINUE;
|
|
nd->flags |= LOOKUP_CONTINUE;
|
|
- err = exec_permission_lite(inode, nd);
|
|
|
|
|
|
+ err = exec_permission_lite(inode);
|
|
if (err == -EAGAIN)
|
|
if (err == -EAGAIN)
|
|
err = vfs_permission(nd, MAY_EXEC);
|
|
err = vfs_permission(nd, MAY_EXEC);
|
|
if (err)
|
|
if (err)
|
|
@@ -1060,67 +1041,6 @@ static int path_walk(const char *name, struct nameidata *nd)
|
|
return link_path_walk(name, nd);
|
|
return link_path_walk(name, nd);
|
|
}
|
|
}
|
|
|
|
|
|
-/*
|
|
|
|
- * SMP-safe: Returns 1 and nd will have valid dentry and mnt, if
|
|
|
|
- * everything is done. Returns 0 and drops input nd, if lookup failed;
|
|
|
|
- */
|
|
|
|
-static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
|
|
|
|
-{
|
|
|
|
- if (path_walk(name, nd))
|
|
|
|
- return 0; /* something went wrong... */
|
|
|
|
-
|
|
|
|
- if (!nd->path.dentry->d_inode ||
|
|
|
|
- S_ISDIR(nd->path.dentry->d_inode->i_mode)) {
|
|
|
|
- struct path old_path = nd->path;
|
|
|
|
- struct qstr last = nd->last;
|
|
|
|
- int last_type = nd->last_type;
|
|
|
|
- struct fs_struct *fs = current->fs;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * NAME was not found in alternate root or it's a directory.
|
|
|
|
- * Try to find it in the normal root:
|
|
|
|
- */
|
|
|
|
- nd->last_type = LAST_ROOT;
|
|
|
|
- read_lock(&fs->lock);
|
|
|
|
- nd->path = fs->root;
|
|
|
|
- path_get(&fs->root);
|
|
|
|
- read_unlock(&fs->lock);
|
|
|
|
- if (path_walk(name, nd) == 0) {
|
|
|
|
- if (nd->path.dentry->d_inode) {
|
|
|
|
- path_put(&old_path);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
- path_put(&nd->path);
|
|
|
|
- }
|
|
|
|
- nd->path = old_path;
|
|
|
|
- nd->last = last;
|
|
|
|
- nd->last_type = last_type;
|
|
|
|
- }
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void set_fs_altroot(void)
|
|
|
|
-{
|
|
|
|
- char *emul = __emul_prefix();
|
|
|
|
- struct nameidata nd;
|
|
|
|
- struct path path = {}, old_path;
|
|
|
|
- int err;
|
|
|
|
- struct fs_struct *fs = current->fs;
|
|
|
|
-
|
|
|
|
- if (!emul)
|
|
|
|
- goto set_it;
|
|
|
|
- err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
|
|
|
|
- if (!err)
|
|
|
|
- path = nd.path;
|
|
|
|
-set_it:
|
|
|
|
- write_lock(&fs->lock);
|
|
|
|
- old_path = fs->altroot;
|
|
|
|
- fs->altroot = path;
|
|
|
|
- write_unlock(&fs->lock);
|
|
|
|
- if (old_path.dentry)
|
|
|
|
- path_put(&old_path);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
|
|
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
|
|
static int do_path_lookup(int dfd, const char *name,
|
|
static int do_path_lookup(int dfd, const char *name,
|
|
unsigned int flags, struct nameidata *nd)
|
|
unsigned int flags, struct nameidata *nd)
|
|
@@ -1136,14 +1056,6 @@ static int do_path_lookup(int dfd, const char *name,
|
|
|
|
|
|
if (*name=='/') {
|
|
if (*name=='/') {
|
|
read_lock(&fs->lock);
|
|
read_lock(&fs->lock);
|
|
- if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
|
|
|
|
- nd->path = fs->altroot;
|
|
|
|
- path_get(&fs->altroot);
|
|
|
|
- read_unlock(&fs->lock);
|
|
|
|
- if (__emul_lookup_dentry(name,nd))
|
|
|
|
- goto out; /* found in altroot */
|
|
|
|
- read_lock(&fs->lock);
|
|
|
|
- }
|
|
|
|
nd->path = fs->root;
|
|
nd->path = fs->root;
|
|
path_get(&fs->root);
|
|
path_get(&fs->root);
|
|
read_unlock(&fs->lock);
|
|
read_unlock(&fs->lock);
|
|
@@ -1177,7 +1089,6 @@ static int do_path_lookup(int dfd, const char *name,
|
|
}
|
|
}
|
|
|
|
|
|
retval = path_walk(name, nd);
|
|
retval = path_walk(name, nd);
|
|
-out:
|
|
|
|
if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
|
|
if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
|
|
nd->path.dentry->d_inode))
|
|
nd->path.dentry->d_inode))
|
|
audit_inode(name, nd->path.dentry);
|
|
audit_inode(name, nd->path.dentry);
|
|
@@ -1282,19 +1193,6 @@ static int path_lookup_create(int dfd, const char *name,
|
|
nd, open_flags, create_mode);
|
|
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(AT_FDCWD, tmp, lookup_flags, nd, open_flags, 0);
|
|
|
|
- putname(tmp);
|
|
|
|
- }
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static struct dentry *__lookup_hash(struct qstr *name,
|
|
static struct dentry *__lookup_hash(struct qstr *name,
|
|
struct dentry *base, struct nameidata *nd)
|
|
struct dentry *base, struct nameidata *nd)
|
|
{
|
|
{
|
|
@@ -1317,7 +1215,14 @@ static struct dentry *__lookup_hash(struct qstr *name,
|
|
|
|
|
|
dentry = cached_lookup(base, name, nd);
|
|
dentry = cached_lookup(base, name, nd);
|
|
if (!dentry) {
|
|
if (!dentry) {
|
|
- struct dentry *new = d_alloc(base, name);
|
|
|
|
|
|
+ struct dentry *new;
|
|
|
|
+
|
|
|
|
+ /* Don't create child dentry for a dead directory. */
|
|
|
|
+ dentry = ERR_PTR(-ENOENT);
|
|
|
|
+ if (IS_DEADDIR(inode))
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ new = d_alloc(base, name);
|
|
dentry = ERR_PTR(-ENOMEM);
|
|
dentry = ERR_PTR(-ENOMEM);
|
|
if (!new)
|
|
if (!new)
|
|
goto out;
|
|
goto out;
|
|
@@ -1340,7 +1245,7 @@ static struct dentry *lookup_hash(struct nameidata *nd)
|
|
{
|
|
{
|
|
int err;
|
|
int err;
|
|
|
|
|
|
- err = permission(nd->path.dentry->d_inode, MAY_EXEC, nd);
|
|
|
|
|
|
+ err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
|
|
if (err)
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
return ERR_PTR(err);
|
|
return __lookup_hash(&nd->last, nd->path.dentry, nd);
|
|
return __lookup_hash(&nd->last, nd->path.dentry, nd);
|
|
@@ -1388,7 +1293,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
|
if (err)
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
return ERR_PTR(err);
|
|
|
|
|
|
- err = permission(base->d_inode, MAY_EXEC, NULL);
|
|
|
|
|
|
+ err = inode_permission(base->d_inode, MAY_EXEC);
|
|
if (err)
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
return ERR_PTR(err);
|
|
return __lookup_hash(&this, base, NULL);
|
|
return __lookup_hash(&this, base, NULL);
|
|
@@ -1416,22 +1321,40 @@ struct dentry *lookup_one_noperm(const char *name, struct dentry *base)
|
|
return __lookup_hash(&this, base, NULL);
|
|
return __lookup_hash(&this, base, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
-int __user_walk_fd(int dfd, const char __user *name, unsigned flags,
|
|
|
|
- struct nameidata *nd)
|
|
|
|
|
|
+int user_path_at(int dfd, const char __user *name, unsigned flags,
|
|
|
|
+ struct path *path)
|
|
{
|
|
{
|
|
|
|
+ struct nameidata nd;
|
|
char *tmp = getname(name);
|
|
char *tmp = getname(name);
|
|
int err = PTR_ERR(tmp);
|
|
int err = PTR_ERR(tmp);
|
|
-
|
|
|
|
if (!IS_ERR(tmp)) {
|
|
if (!IS_ERR(tmp)) {
|
|
- err = do_path_lookup(dfd, tmp, flags, nd);
|
|
|
|
|
|
+
|
|
|
|
+ BUG_ON(flags & LOOKUP_PARENT);
|
|
|
|
+
|
|
|
|
+ err = do_path_lookup(dfd, tmp, flags, &nd);
|
|
putname(tmp);
|
|
putname(tmp);
|
|
|
|
+ if (!err)
|
|
|
|
+ *path = nd.path;
|
|
}
|
|
}
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
-int __user_walk(const char __user *name, unsigned flags, struct nameidata *nd)
|
|
|
|
|
|
+static int user_path_parent(int dfd, const char __user *path,
|
|
|
|
+ struct nameidata *nd, char **name)
|
|
{
|
|
{
|
|
- return __user_walk_fd(AT_FDCWD, name, flags, nd);
|
|
|
|
|
|
+ char *s = getname(path);
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ if (IS_ERR(s))
|
|
|
|
+ return PTR_ERR(s);
|
|
|
|
+
|
|
|
|
+ error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd);
|
|
|
|
+ if (error)
|
|
|
|
+ putname(s);
|
|
|
|
+ else
|
|
|
|
+ *name = s;
|
|
|
|
+
|
|
|
|
+ return error;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1478,7 +1401,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
|
|
BUG_ON(victim->d_parent->d_inode != dir);
|
|
BUG_ON(victim->d_parent->d_inode != dir);
|
|
audit_inode_child(victim->d_name.name, victim, dir);
|
|
audit_inode_child(victim->d_name.name, victim, dir);
|
|
|
|
|
|
- error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
|
|
|
|
|
|
+ error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
|
if (error)
|
|
if (error)
|
|
return error;
|
|
return error;
|
|
if (IS_APPEND(dir))
|
|
if (IS_APPEND(dir))
|
|
@@ -1515,7 +1438,7 @@ static inline int may_create(struct inode *dir, struct dentry *child,
|
|
return -EEXIST;
|
|
return -EEXIST;
|
|
if (IS_DEADDIR(dir))
|
|
if (IS_DEADDIR(dir))
|
|
return -ENOENT;
|
|
return -ENOENT;
|
|
- return permission(dir,MAY_WRITE | MAY_EXEC, nd);
|
|
|
|
|
|
+ return inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1755,7 +1678,7 @@ struct file *do_filp_open(int dfd, const char *pathname,
|
|
int will_write;
|
|
int will_write;
|
|
int flag = open_to_namei_flags(open_flag);
|
|
int flag = open_to_namei_flags(open_flag);
|
|
|
|
|
|
- acc_mode = ACC_MODE(flag);
|
|
|
|
|
|
+ acc_mode = MAY_OPEN | ACC_MODE(flag);
|
|
|
|
|
|
/* O_TRUNC implies we need access checks for write permissions */
|
|
/* O_TRUNC implies we need access checks for write permissions */
|
|
if (flag & O_TRUNC)
|
|
if (flag & O_TRUNC)
|
|
@@ -2071,20 +1994,18 @@ static int may_mknod(mode_t mode)
|
|
asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
|
|
asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
|
|
unsigned dev)
|
|
unsigned dev)
|
|
{
|
|
{
|
|
- int error = 0;
|
|
|
|
- char * tmp;
|
|
|
|
- struct dentry * dentry;
|
|
|
|
|
|
+ int error;
|
|
|
|
+ char *tmp;
|
|
|
|
+ struct dentry *dentry;
|
|
struct nameidata nd;
|
|
struct nameidata nd;
|
|
|
|
|
|
if (S_ISDIR(mode))
|
|
if (S_ISDIR(mode))
|
|
return -EPERM;
|
|
return -EPERM;
|
|
- tmp = getname(filename);
|
|
|
|
- if (IS_ERR(tmp))
|
|
|
|
- return PTR_ERR(tmp);
|
|
|
|
|
|
|
|
- error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
|
|
|
|
|
|
+ error = user_path_parent(dfd, filename, &nd, &tmp);
|
|
if (error)
|
|
if (error)
|
|
- goto out;
|
|
|
|
|
|
+ return error;
|
|
|
|
+
|
|
dentry = lookup_create(&nd, 0);
|
|
dentry = lookup_create(&nd, 0);
|
|
if (IS_ERR(dentry)) {
|
|
if (IS_ERR(dentry)) {
|
|
error = PTR_ERR(dentry);
|
|
error = PTR_ERR(dentry);
|
|
@@ -2116,7 +2037,6 @@ out_dput:
|
|
out_unlock:
|
|
out_unlock:
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
path_put(&nd.path);
|
|
path_put(&nd.path);
|
|
-out:
|
|
|
|
putname(tmp);
|
|
putname(tmp);
|
|
|
|
|
|
return error;
|
|
return error;
|
|
@@ -2156,14 +2076,10 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
|
|
struct dentry *dentry;
|
|
struct dentry *dentry;
|
|
struct nameidata nd;
|
|
struct nameidata nd;
|
|
|
|
|
|
- tmp = getname(pathname);
|
|
|
|
- error = PTR_ERR(tmp);
|
|
|
|
- if (IS_ERR(tmp))
|
|
|
|
|
|
+ error = user_path_parent(dfd, pathname, &nd, &tmp);
|
|
|
|
+ if (error)
|
|
goto out_err;
|
|
goto out_err;
|
|
|
|
|
|
- error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
|
|
|
|
- if (error)
|
|
|
|
- goto out;
|
|
|
|
dentry = lookup_create(&nd, 1);
|
|
dentry = lookup_create(&nd, 1);
|
|
error = PTR_ERR(dentry);
|
|
error = PTR_ERR(dentry);
|
|
if (IS_ERR(dentry))
|
|
if (IS_ERR(dentry))
|
|
@@ -2181,7 +2097,6 @@ out_dput:
|
|
out_unlock:
|
|
out_unlock:
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
path_put(&nd.path);
|
|
path_put(&nd.path);
|
|
-out:
|
|
|
|
putname(tmp);
|
|
putname(tmp);
|
|
out_err:
|
|
out_err:
|
|
return error;
|
|
return error;
|
|
@@ -2259,13 +2174,9 @@ static long do_rmdir(int dfd, const char __user *pathname)
|
|
struct dentry *dentry;
|
|
struct dentry *dentry;
|
|
struct nameidata nd;
|
|
struct nameidata nd;
|
|
|
|
|
|
- name = getname(pathname);
|
|
|
|
- if(IS_ERR(name))
|
|
|
|
- return PTR_ERR(name);
|
|
|
|
-
|
|
|
|
- error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
|
|
|
|
|
|
+ error = user_path_parent(dfd, pathname, &nd, &name);
|
|
if (error)
|
|
if (error)
|
|
- goto exit;
|
|
|
|
|
|
+ return error;
|
|
|
|
|
|
switch(nd.last_type) {
|
|
switch(nd.last_type) {
|
|
case LAST_DOTDOT:
|
|
case LAST_DOTDOT:
|
|
@@ -2294,7 +2205,6 @@ exit2:
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
exit1:
|
|
exit1:
|
|
path_put(&nd.path);
|
|
path_put(&nd.path);
|
|
-exit:
|
|
|
|
putname(name);
|
|
putname(name);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
@@ -2343,19 +2253,16 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
|
|
*/
|
|
*/
|
|
static long do_unlinkat(int dfd, const char __user *pathname)
|
|
static long do_unlinkat(int dfd, const char __user *pathname)
|
|
{
|
|
{
|
|
- int error = 0;
|
|
|
|
- char * name;
|
|
|
|
|
|
+ int error;
|
|
|
|
+ char *name;
|
|
struct dentry *dentry;
|
|
struct dentry *dentry;
|
|
struct nameidata nd;
|
|
struct nameidata nd;
|
|
struct inode *inode = NULL;
|
|
struct inode *inode = NULL;
|
|
|
|
|
|
- name = getname(pathname);
|
|
|
|
- if(IS_ERR(name))
|
|
|
|
- return PTR_ERR(name);
|
|
|
|
-
|
|
|
|
- error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
|
|
|
|
|
|
+ error = user_path_parent(dfd, pathname, &nd, &name);
|
|
if (error)
|
|
if (error)
|
|
- goto exit;
|
|
|
|
|
|
+ return error;
|
|
|
|
+
|
|
error = -EISDIR;
|
|
error = -EISDIR;
|
|
if (nd.last_type != LAST_NORM)
|
|
if (nd.last_type != LAST_NORM)
|
|
goto exit1;
|
|
goto exit1;
|
|
@@ -2382,7 +2289,6 @@ static long do_unlinkat(int dfd, const char __user *pathname)
|
|
iput(inode); /* truncate the inode here */
|
|
iput(inode); /* truncate the inode here */
|
|
exit1:
|
|
exit1:
|
|
path_put(&nd.path);
|
|
path_put(&nd.path);
|
|
-exit:
|
|
|
|
putname(name);
|
|
putname(name);
|
|
return error;
|
|
return error;
|
|
|
|
|
|
@@ -2408,7 +2314,7 @@ asmlinkage long sys_unlink(const char __user *pathname)
|
|
return do_unlinkat(AT_FDCWD, pathname);
|
|
return do_unlinkat(AT_FDCWD, pathname);
|
|
}
|
|
}
|
|
|
|
|
|
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
|
|
|
|
|
|
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
|
{
|
|
{
|
|
int error = may_create(dir, dentry, NULL);
|
|
int error = may_create(dir, dentry, NULL);
|
|
|
|
|
|
@@ -2432,23 +2338,20 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i
|
|
asmlinkage long sys_symlinkat(const char __user *oldname,
|
|
asmlinkage long sys_symlinkat(const char __user *oldname,
|
|
int newdfd, const char __user *newname)
|
|
int newdfd, const char __user *newname)
|
|
{
|
|
{
|
|
- int error = 0;
|
|
|
|
- char * from;
|
|
|
|
- char * to;
|
|
|
|
|
|
+ int error;
|
|
|
|
+ char *from;
|
|
|
|
+ char *to;
|
|
struct dentry *dentry;
|
|
struct dentry *dentry;
|
|
struct nameidata nd;
|
|
struct nameidata nd;
|
|
|
|
|
|
from = getname(oldname);
|
|
from = getname(oldname);
|
|
- if(IS_ERR(from))
|
|
|
|
|
|
+ if (IS_ERR(from))
|
|
return PTR_ERR(from);
|
|
return PTR_ERR(from);
|
|
- to = getname(newname);
|
|
|
|
- error = PTR_ERR(to);
|
|
|
|
- if (IS_ERR(to))
|
|
|
|
- goto out_putname;
|
|
|
|
|
|
|
|
- error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
|
|
|
|
|
|
+ error = user_path_parent(newdfd, newname, &nd, &to);
|
|
if (error)
|
|
if (error)
|
|
- goto out;
|
|
|
|
|
|
+ goto out_putname;
|
|
|
|
+
|
|
dentry = lookup_create(&nd, 0);
|
|
dentry = lookup_create(&nd, 0);
|
|
error = PTR_ERR(dentry);
|
|
error = PTR_ERR(dentry);
|
|
if (IS_ERR(dentry))
|
|
if (IS_ERR(dentry))
|
|
@@ -2457,14 +2360,13 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
|
|
error = mnt_want_write(nd.path.mnt);
|
|
error = mnt_want_write(nd.path.mnt);
|
|
if (error)
|
|
if (error)
|
|
goto out_dput;
|
|
goto out_dput;
|
|
- error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO);
|
|
|
|
|
|
+ error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
|
|
mnt_drop_write(nd.path.mnt);
|
|
mnt_drop_write(nd.path.mnt);
|
|
out_dput:
|
|
out_dput:
|
|
dput(dentry);
|
|
dput(dentry);
|
|
out_unlock:
|
|
out_unlock:
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
path_put(&nd.path);
|
|
path_put(&nd.path);
|
|
-out:
|
|
|
|
putname(to);
|
|
putname(to);
|
|
out_putname:
|
|
out_putname:
|
|
putname(from);
|
|
putname(from);
|
|
@@ -2498,19 +2400,19 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
|
return -EPERM;
|
|
return -EPERM;
|
|
if (!dir->i_op || !dir->i_op->link)
|
|
if (!dir->i_op || !dir->i_op->link)
|
|
return -EPERM;
|
|
return -EPERM;
|
|
- if (S_ISDIR(old_dentry->d_inode->i_mode))
|
|
|
|
|
|
+ if (S_ISDIR(inode->i_mode))
|
|
return -EPERM;
|
|
return -EPERM;
|
|
|
|
|
|
error = security_inode_link(old_dentry, dir, new_dentry);
|
|
error = security_inode_link(old_dentry, dir, new_dentry);
|
|
if (error)
|
|
if (error)
|
|
return error;
|
|
return error;
|
|
|
|
|
|
- mutex_lock(&old_dentry->d_inode->i_mutex);
|
|
|
|
|
|
+ mutex_lock(&inode->i_mutex);
|
|
DQUOT_INIT(dir);
|
|
DQUOT_INIT(dir);
|
|
error = dir->i_op->link(old_dentry, dir, new_dentry);
|
|
error = dir->i_op->link(old_dentry, dir, new_dentry);
|
|
- mutex_unlock(&old_dentry->d_inode->i_mutex);
|
|
|
|
|
|
+ mutex_unlock(&inode->i_mutex);
|
|
if (!error)
|
|
if (!error)
|
|
- fsnotify_link(dir, old_dentry->d_inode, new_dentry);
|
|
|
|
|
|
+ fsnotify_link(dir, inode, new_dentry);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2528,27 +2430,25 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
|
|
int flags)
|
|
int flags)
|
|
{
|
|
{
|
|
struct dentry *new_dentry;
|
|
struct dentry *new_dentry;
|
|
- struct nameidata nd, old_nd;
|
|
|
|
|
|
+ struct nameidata nd;
|
|
|
|
+ struct path old_path;
|
|
int error;
|
|
int error;
|
|
- char * to;
|
|
|
|
|
|
+ char *to;
|
|
|
|
|
|
if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
|
|
if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- to = getname(newname);
|
|
|
|
- if (IS_ERR(to))
|
|
|
|
- return PTR_ERR(to);
|
|
|
|
-
|
|
|
|
- error = __user_walk_fd(olddfd, oldname,
|
|
|
|
- flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
|
|
|
|
- &old_nd);
|
|
|
|
|
|
+ error = user_path_at(olddfd, oldname,
|
|
|
|
+ flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
|
|
|
|
+ &old_path);
|
|
if (error)
|
|
if (error)
|
|
- goto exit;
|
|
|
|
- error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
|
|
|
|
|
|
+ return error;
|
|
|
|
+
|
|
|
|
+ error = user_path_parent(newdfd, newname, &nd, &to);
|
|
if (error)
|
|
if (error)
|
|
goto out;
|
|
goto out;
|
|
error = -EXDEV;
|
|
error = -EXDEV;
|
|
- if (old_nd.path.mnt != nd.path.mnt)
|
|
|
|
|
|
+ if (old_path.mnt != nd.path.mnt)
|
|
goto out_release;
|
|
goto out_release;
|
|
new_dentry = lookup_create(&nd, 0);
|
|
new_dentry = lookup_create(&nd, 0);
|
|
error = PTR_ERR(new_dentry);
|
|
error = PTR_ERR(new_dentry);
|
|
@@ -2557,7 +2457,7 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
|
|
error = mnt_want_write(nd.path.mnt);
|
|
error = mnt_want_write(nd.path.mnt);
|
|
if (error)
|
|
if (error)
|
|
goto out_dput;
|
|
goto out_dput;
|
|
- error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry);
|
|
|
|
|
|
+ error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
|
|
mnt_drop_write(nd.path.mnt);
|
|
mnt_drop_write(nd.path.mnt);
|
|
out_dput:
|
|
out_dput:
|
|
dput(new_dentry);
|
|
dput(new_dentry);
|
|
@@ -2565,10 +2465,9 @@ out_unlock:
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
|
out_release:
|
|
out_release:
|
|
path_put(&nd.path);
|
|
path_put(&nd.path);
|
|
-out:
|
|
|
|
- path_put(&old_nd.path);
|
|
|
|
-exit:
|
|
|
|
putname(to);
|
|
putname(to);
|
|
|
|
+out:
|
|
|
|
+ path_put(&old_path);
|
|
|
|
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
@@ -2621,7 +2520,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
|
|
* we'll need to flip '..'.
|
|
* we'll need to flip '..'.
|
|
*/
|
|
*/
|
|
if (new_dir != old_dir) {
|
|
if (new_dir != old_dir) {
|
|
- error = permission(old_dentry->d_inode, MAY_WRITE, NULL);
|
|
|
|
|
|
+ error = inode_permission(old_dentry->d_inode, MAY_WRITE);
|
|
if (error)
|
|
if (error)
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
@@ -2724,20 +2623,22 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
-static int do_rename(int olddfd, const char *oldname,
|
|
|
|
- int newdfd, const char *newname)
|
|
|
|
|
|
+asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
|
|
|
|
+ int newdfd, const char __user *newname)
|
|
{
|
|
{
|
|
- int error = 0;
|
|
|
|
- struct dentry * old_dir, * new_dir;
|
|
|
|
- struct dentry * old_dentry, *new_dentry;
|
|
|
|
- struct dentry * trap;
|
|
|
|
|
|
+ struct dentry *old_dir, *new_dir;
|
|
|
|
+ struct dentry *old_dentry, *new_dentry;
|
|
|
|
+ struct dentry *trap;
|
|
struct nameidata oldnd, newnd;
|
|
struct nameidata oldnd, newnd;
|
|
|
|
+ char *from;
|
|
|
|
+ char *to;
|
|
|
|
+ int error;
|
|
|
|
|
|
- error = do_path_lookup(olddfd, oldname, LOOKUP_PARENT, &oldnd);
|
|
|
|
|
|
+ error = user_path_parent(olddfd, oldname, &oldnd, &from);
|
|
if (error)
|
|
if (error)
|
|
goto exit;
|
|
goto exit;
|
|
|
|
|
|
- error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &newnd);
|
|
|
|
|
|
+ error = user_path_parent(newdfd, newname, &newnd, &to);
|
|
if (error)
|
|
if (error)
|
|
goto exit1;
|
|
goto exit1;
|
|
|
|
|
|
@@ -2799,29 +2700,11 @@ exit3:
|
|
unlock_rename(new_dir, old_dir);
|
|
unlock_rename(new_dir, old_dir);
|
|
exit2:
|
|
exit2:
|
|
path_put(&newnd.path);
|
|
path_put(&newnd.path);
|
|
|
|
+ putname(to);
|
|
exit1:
|
|
exit1:
|
|
path_put(&oldnd.path);
|
|
path_put(&oldnd.path);
|
|
-exit:
|
|
|
|
- return error;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
|
|
|
|
- int newdfd, const char __user *newname)
|
|
|
|
-{
|
|
|
|
- int error;
|
|
|
|
- char * from;
|
|
|
|
- char * to;
|
|
|
|
-
|
|
|
|
- from = getname(oldname);
|
|
|
|
- if(IS_ERR(from))
|
|
|
|
- return PTR_ERR(from);
|
|
|
|
- to = getname(newname);
|
|
|
|
- error = PTR_ERR(to);
|
|
|
|
- if (!IS_ERR(to)) {
|
|
|
|
- error = do_rename(olddfd, from, newdfd, to);
|
|
|
|
- putname(to);
|
|
|
|
- }
|
|
|
|
putname(from);
|
|
putname(from);
|
|
|
|
+exit:
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2959,8 +2842,7 @@ const struct inode_operations page_symlink_inode_operations = {
|
|
.put_link = page_put_link,
|
|
.put_link = page_put_link,
|
|
};
|
|
};
|
|
|
|
|
|
-EXPORT_SYMBOL(__user_walk);
|
|
|
|
-EXPORT_SYMBOL(__user_walk_fd);
|
|
|
|
|
|
+EXPORT_SYMBOL(user_path_at);
|
|
EXPORT_SYMBOL(follow_down);
|
|
EXPORT_SYMBOL(follow_down);
|
|
EXPORT_SYMBOL(follow_up);
|
|
EXPORT_SYMBOL(follow_up);
|
|
EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
|
|
EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
|
|
@@ -2975,7 +2857,7 @@ EXPORT_SYMBOL(page_symlink);
|
|
EXPORT_SYMBOL(page_symlink_inode_operations);
|
|
EXPORT_SYMBOL(page_symlink_inode_operations);
|
|
EXPORT_SYMBOL(path_lookup);
|
|
EXPORT_SYMBOL(path_lookup);
|
|
EXPORT_SYMBOL(vfs_path_lookup);
|
|
EXPORT_SYMBOL(vfs_path_lookup);
|
|
-EXPORT_SYMBOL(permission);
|
|
|
|
|
|
+EXPORT_SYMBOL(inode_permission);
|
|
EXPORT_SYMBOL(vfs_permission);
|
|
EXPORT_SYMBOL(vfs_permission);
|
|
EXPORT_SYMBOL(file_permission);
|
|
EXPORT_SYMBOL(file_permission);
|
|
EXPORT_SYMBOL(unlock_rename);
|
|
EXPORT_SYMBOL(unlock_rename);
|