|
@@ -30,6 +30,8 @@
|
|
|
#include <linux/audit.h>
|
|
|
#include <linux/capability.h>
|
|
|
#include <linux/file.h>
|
|
|
+#include <linux/fcntl.h>
|
|
|
+#include <linux/namei.h>
|
|
|
#include <asm/namei.h>
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
@@ -1063,7 +1065,8 @@ set_it:
|
|
|
}
|
|
|
|
|
|
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
|
|
|
-int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata *nd)
|
|
|
+static int fastcall do_path_lookup(int dfd, const char *name,
|
|
|
+ unsigned int flags, struct nameidata *nd)
|
|
|
{
|
|
|
int retval = 0;
|
|
|
|
|
@@ -1083,9 +1086,38 @@ int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata
|
|
|
}
|
|
|
nd->mnt = mntget(current->fs->rootmnt);
|
|
|
nd->dentry = dget(current->fs->root);
|
|
|
- } else {
|
|
|
+ } else if (dfd == AT_FDCWD) {
|
|
|
nd->mnt = mntget(current->fs->pwdmnt);
|
|
|
nd->dentry = dget(current->fs->pwd);
|
|
|
+ } else {
|
|
|
+ struct file *file;
|
|
|
+ int fput_needed;
|
|
|
+ struct dentry *dentry;
|
|
|
+
|
|
|
+ file = fget_light(dfd, &fput_needed);
|
|
|
+ if (!file) {
|
|
|
+ retval = -EBADF;
|
|
|
+ goto out_fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ dentry = file->f_dentry;
|
|
|
+
|
|
|
+ if (!S_ISDIR(dentry->d_inode->i_mode)) {
|
|
|
+ retval = -ENOTDIR;
|
|
|
+ fput_light(file, fput_needed);
|
|
|
+ goto out_fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval = file_permission(file, MAY_EXEC);
|
|
|
+ if (retval) {
|
|
|
+ fput_light(file, fput_needed);
|
|
|
+ goto out_fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ nd->mnt = mntget(file->f_vfsmnt);
|
|
|
+ nd->dentry = dget(dentry);
|
|
|
+
|
|
|
+ fput_light(file, fput_needed);
|
|
|
}
|
|
|
read_unlock(¤t->fs->lock);
|
|
|
current->total_link_count = 0;
|
|
@@ -1094,11 +1126,19 @@ out:
|
|
|
if (unlikely(current->audit_context
|
|
|
&& nd && nd->dentry && nd->dentry->d_inode))
|
|
|
audit_inode(name, nd->dentry->d_inode, flags);
|
|
|
+out_fail:
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags,
|
|
|
- struct nameidata *nd, int open_flags, int create_mode)
|
|
|
+int fastcall path_lookup(const char *name, unsigned int flags,
|
|
|
+ struct nameidata *nd)
|
|
|
+{
|
|
|
+ return do_path_lookup(AT_FDCWD, name, flags, nd);
|
|
|
+}
|
|
|
+
|
|
|
+static int __path_lookup_intent_open(int dfd, const char *name,
|
|
|
+ unsigned int lookup_flags, struct nameidata *nd,
|
|
|
+ int open_flags, int create_mode)
|
|
|
{
|
|
|
struct file *filp = get_empty_filp();
|
|
|
int err;
|
|
@@ -1108,7 +1148,7 @@ static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags
|
|
|
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);
|
|
|
+ err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd);
|
|
|
if (IS_ERR(nd->intent.open.file)) {
|
|
|
if (err == 0) {
|
|
|
err = PTR_ERR(nd->intent.open.file);
|
|
@@ -1126,10 +1166,10 @@ static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags
|
|
|
* @nd: pointer to nameidata
|
|
|
* @open_flags: open intent flags
|
|
|
*/
|
|
|
-int path_lookup_open(const char *name, unsigned int lookup_flags,
|
|
|
+int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags,
|
|
|
struct nameidata *nd, int open_flags)
|
|
|
{
|
|
|
- return __path_lookup_intent_open(name, lookup_flags, nd,
|
|
|
+ return __path_lookup_intent_open(dfd, name, lookup_flags, nd,
|
|
|
open_flags, 0);
|
|
|
}
|
|
|
|
|
@@ -1141,12 +1181,12 @@ int path_lookup_open(const char *name, unsigned int lookup_flags,
|
|
|
* @open_flags: open intent flags
|
|
|
* @create_mode: create intent flags
|
|
|
*/
|
|
|
-static int path_lookup_create(const char *name, unsigned int lookup_flags,
|
|
|
- struct nameidata *nd, int open_flags,
|
|
|
- int create_mode)
|
|
|
+static int path_lookup_create(int dfd, 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);
|
|
|
+ return __path_lookup_intent_open(dfd, name, lookup_flags|LOOKUP_CREATE,
|
|
|
+ nd, open_flags, create_mode);
|
|
|
}
|
|
|
|
|
|
int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
|
|
@@ -1156,7 +1196,7 @@ int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
|
|
|
int err = PTR_ERR(tmp);
|
|
|
|
|
|
if (!IS_ERR(tmp)) {
|
|
|
- err = __path_lookup_intent_open(tmp, lookup_flags, nd, open_flags, 0);
|
|
|
+ err = __path_lookup_intent_open(AT_FDCWD, tmp, lookup_flags, nd, open_flags, 0);
|
|
|
putname(tmp);
|
|
|
}
|
|
|
return err;
|
|
@@ -1248,18 +1288,24 @@ access:
|
|
|
* that namei follows links, while lnamei does not.
|
|
|
* SMP-safe
|
|
|
*/
|
|
|
-int fastcall __user_walk(const char __user *name, unsigned flags, struct nameidata *nd)
|
|
|
+int fastcall __user_walk_fd(int dfd, const char __user *name, unsigned flags,
|
|
|
+ struct nameidata *nd)
|
|
|
{
|
|
|
char *tmp = getname(name);
|
|
|
int err = PTR_ERR(tmp);
|
|
|
|
|
|
if (!IS_ERR(tmp)) {
|
|
|
- err = path_lookup(tmp, flags, nd);
|
|
|
+ err = do_path_lookup(dfd, tmp, flags, nd);
|
|
|
putname(tmp);
|
|
|
}
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+int fastcall __user_walk(const char __user *name, unsigned flags, struct nameidata *nd)
|
|
|
+{
|
|
|
+ return __user_walk_fd(AT_FDCWD, name, flags, nd);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* It's inline, so penalty for filesystems that don't use sticky bit is
|
|
|
* minimal.
|
|
@@ -1518,7 +1564,8 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
|
|
|
* for symlinks (where the permissions are checked later).
|
|
|
* SMP-safe
|
|
|
*/
|
|
|
-int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
|
|
|
+int open_namei(int dfd, const char *pathname, int flag,
|
|
|
+ int mode, struct nameidata *nd)
|
|
|
{
|
|
|
int acc_mode, error;
|
|
|
struct path path;
|
|
@@ -1540,7 +1587,8 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
|
|
|
* The simplest case - just a plain lookup.
|
|
|
*/
|
|
|
if (!(flag & O_CREAT)) {
|
|
|
- error = path_lookup_open(pathname, lookup_flags(flag), nd, flag);
|
|
|
+ error = path_lookup_open(dfd, pathname, lookup_flags(flag),
|
|
|
+ nd, flag);
|
|
|
if (error)
|
|
|
return error;
|
|
|
goto ok;
|
|
@@ -1549,7 +1597,7 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
|
|
|
/*
|
|
|
* Create - we need to know the parent.
|
|
|
*/
|
|
|
- error = path_lookup_create(pathname, LOOKUP_PARENT, nd, flag, mode);
|
|
|
+ error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
|
|
|
if (error)
|
|
|
return error;
|
|
|
|
|
@@ -1744,7 +1792,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-asmlinkage long sys_mknod(const char __user * filename, int mode, unsigned dev)
|
|
|
+asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
|
|
|
+ unsigned dev)
|
|
|
{
|
|
|
int error = 0;
|
|
|
char * tmp;
|
|
@@ -1757,7 +1806,7 @@ asmlinkage long sys_mknod(const char __user * filename, int mode, unsigned dev)
|
|
|
if (IS_ERR(tmp))
|
|
|
return PTR_ERR(tmp);
|
|
|
|
|
|
- error = path_lookup(tmp, LOOKUP_PARENT, &nd);
|
|
|
+ error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
dentry = lookup_create(&nd, 0);
|
|
@@ -1793,6 +1842,11 @@ out:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+asmlinkage long sys_mknod(const char __user *filename, int mode, unsigned dev)
|
|
|
+{
|
|
|
+ return sys_mknodat(AT_FDCWD, filename, mode, dev);
|
|
|
+}
|
|
|
+
|
|
|
int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|
|
{
|
|
|
int error = may_create(dir, dentry, NULL);
|
|
@@ -1815,7 +1869,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-asmlinkage long sys_mkdir(const char __user * pathname, int mode)
|
|
|
+asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
|
|
|
{
|
|
|
int error = 0;
|
|
|
char * tmp;
|
|
@@ -1826,7 +1880,7 @@ asmlinkage long sys_mkdir(const char __user * pathname, int mode)
|
|
|
struct dentry *dentry;
|
|
|
struct nameidata nd;
|
|
|
|
|
|
- error = path_lookup(tmp, LOOKUP_PARENT, &nd);
|
|
|
+ error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
dentry = lookup_create(&nd, 1);
|
|
@@ -1846,6 +1900,11 @@ out:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+asmlinkage long sys_mkdir(const char __user *pathname, int mode)
|
|
|
+{
|
|
|
+ return sys_mkdirat(AT_FDCWD, pathname, mode);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* We try to drop the dentry early: we should have
|
|
|
* a usage count of 2 if we're the only user of this
|
|
@@ -1907,7 +1966,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-asmlinkage long sys_rmdir(const char __user * pathname)
|
|
|
+static long do_rmdir(int dfd, const char __user *pathname)
|
|
|
{
|
|
|
int error = 0;
|
|
|
char * name;
|
|
@@ -1918,7 +1977,7 @@ asmlinkage long sys_rmdir(const char __user * pathname)
|
|
|
if(IS_ERR(name))
|
|
|
return PTR_ERR(name);
|
|
|
|
|
|
- error = path_lookup(name, LOOKUP_PARENT, &nd);
|
|
|
+ error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
|
|
|
if (error)
|
|
|
goto exit;
|
|
|
|
|
@@ -1948,6 +2007,11 @@ exit:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+asmlinkage long sys_rmdir(const char __user *pathname)
|
|
|
+{
|
|
|
+ return do_rmdir(AT_FDCWD, pathname);
|
|
|
+}
|
|
|
+
|
|
|
int vfs_unlink(struct inode *dir, struct dentry *dentry)
|
|
|
{
|
|
|
int error = may_delete(dir, dentry, 0);
|
|
@@ -1984,7 +2048,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
|
|
|
* writeout happening, and we don't want to prevent access to the directory
|
|
|
* while waiting on the I/O.
|
|
|
*/
|
|
|
-asmlinkage long sys_unlink(const char __user * pathname)
|
|
|
+static long do_unlinkat(int dfd, const char __user *pathname)
|
|
|
{
|
|
|
int error = 0;
|
|
|
char * name;
|
|
@@ -1996,7 +2060,7 @@ asmlinkage long sys_unlink(const char __user * pathname)
|
|
|
if(IS_ERR(name))
|
|
|
return PTR_ERR(name);
|
|
|
|
|
|
- error = path_lookup(name, LOOKUP_PARENT, &nd);
|
|
|
+ error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
|
|
|
if (error)
|
|
|
goto exit;
|
|
|
error = -EISDIR;
|
|
@@ -2031,6 +2095,22 @@ slashes:
|
|
|
goto exit2;
|
|
|
}
|
|
|
|
|
|
+asmlinkage long sys_unlinkat(int dfd, const char __user *pathname, int flag)
|
|
|
+{
|
|
|
+ if ((flag & ~AT_REMOVEDIR) != 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (flag & AT_REMOVEDIR)
|
|
|
+ return do_rmdir(dfd, pathname);
|
|
|
+
|
|
|
+ return do_unlinkat(dfd, pathname);
|
|
|
+}
|
|
|
+
|
|
|
+asmlinkage long sys_unlink(const char __user *pathname)
|
|
|
+{
|
|
|
+ return do_unlinkat(AT_FDCWD, pathname);
|
|
|
+}
|
|
|
+
|
|
|
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
|
|
|
{
|
|
|
int error = may_create(dir, dentry, NULL);
|
|
@@ -2052,7 +2132,8 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-asmlinkage long sys_symlink(const char __user * oldname, const char __user * newname)
|
|
|
+asmlinkage long sys_symlinkat(const char __user *oldname,
|
|
|
+ int newdfd, const char __user *newname)
|
|
|
{
|
|
|
int error = 0;
|
|
|
char * from;
|
|
@@ -2067,7 +2148,7 @@ asmlinkage long sys_symlink(const char __user * oldname, const char __user * new
|
|
|
struct dentry *dentry;
|
|
|
struct nameidata nd;
|
|
|
|
|
|
- error = path_lookup(to, LOOKUP_PARENT, &nd);
|
|
|
+ error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
dentry = lookup_create(&nd, 0);
|
|
@@ -2085,6 +2166,11 @@ out:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+asmlinkage long sys_symlink(const char __user *oldname, const char __user *newname)
|
|
|
+{
|
|
|
+ return sys_symlinkat(oldname, AT_FDCWD, newname);
|
|
|
+}
|
|
|
+
|
|
|
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
|
|
|
{
|
|
|
struct inode *inode = old_dentry->d_inode;
|
|
@@ -2132,7 +2218,8 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
|
|
* with linux 2.0, and to avoid hard-linking to directories
|
|
|
* and other special files. --ADM
|
|
|
*/
|
|
|
-asmlinkage long sys_link(const char __user * oldname, const char __user * newname)
|
|
|
+asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
|
|
|
+ int newdfd, const char __user *newname)
|
|
|
{
|
|
|
struct dentry *new_dentry;
|
|
|
struct nameidata nd, old_nd;
|
|
@@ -2143,10 +2230,10 @@ asmlinkage long sys_link(const char __user * oldname, const char __user * newnam
|
|
|
if (IS_ERR(to))
|
|
|
return PTR_ERR(to);
|
|
|
|
|
|
- error = __user_walk(oldname, 0, &old_nd);
|
|
|
+ error = __user_walk_fd(olddfd, oldname, 0, &old_nd);
|
|
|
if (error)
|
|
|
goto exit;
|
|
|
- error = path_lookup(to, LOOKUP_PARENT, &nd);
|
|
|
+ error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
error = -EXDEV;
|
|
@@ -2169,6 +2256,11 @@ exit:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+asmlinkage long sys_link(const char __user *oldname, const char __user *newname)
|
|
|
+{
|
|
|
+ return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* The worst of all namespace operations - renaming directory. "Perverted"
|
|
|
* doesn't even start to describe it. Somebody in UCB had a heck of a trip...
|
|
@@ -2315,7 +2407,8 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-static int do_rename(const char * oldname, const char * newname)
|
|
|
+static int do_rename(int olddfd, const char *oldname,
|
|
|
+ int newdfd, const char *newname)
|
|
|
{
|
|
|
int error = 0;
|
|
|
struct dentry * old_dir, * new_dir;
|
|
@@ -2323,11 +2416,11 @@ static int do_rename(const char * oldname, const char * newname)
|
|
|
struct dentry * trap;
|
|
|
struct nameidata oldnd, newnd;
|
|
|
|
|
|
- error = path_lookup(oldname, LOOKUP_PARENT, &oldnd);
|
|
|
+ error = do_path_lookup(olddfd, oldname, LOOKUP_PARENT, &oldnd);
|
|
|
if (error)
|
|
|
goto exit;
|
|
|
|
|
|
- error = path_lookup(newname, LOOKUP_PARENT, &newnd);
|
|
|
+ error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &newnd);
|
|
|
if (error)
|
|
|
goto exit1;
|
|
|
|
|
@@ -2391,7 +2484,8 @@ exit:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-asmlinkage long sys_rename(const char __user * oldname, const char __user * newname)
|
|
|
+asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
|
|
|
+ int newdfd, const char __user *newname)
|
|
|
{
|
|
|
int error;
|
|
|
char * from;
|
|
@@ -2403,13 +2497,18 @@ asmlinkage long sys_rename(const char __user * oldname, const char __user * newn
|
|
|
to = getname(newname);
|
|
|
error = PTR_ERR(to);
|
|
|
if (!IS_ERR(to)) {
|
|
|
- error = do_rename(from,to);
|
|
|
+ error = do_rename(olddfd, from, newdfd, to);
|
|
|
putname(to);
|
|
|
}
|
|
|
putname(from);
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+asmlinkage long sys_rename(const char __user *oldname, const char __user *newname)
|
|
|
+{
|
|
|
+ return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname);
|
|
|
+}
|
|
|
+
|
|
|
int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
|
|
|
{
|
|
|
int len;
|
|
@@ -2553,6 +2652,7 @@ struct inode_operations page_symlink_inode_operations = {
|
|
|
};
|
|
|
|
|
|
EXPORT_SYMBOL(__user_walk);
|
|
|
+EXPORT_SYMBOL(__user_walk_fd);
|
|
|
EXPORT_SYMBOL(follow_down);
|
|
|
EXPORT_SYMBOL(follow_up);
|
|
|
EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
|