#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>
* POSIX.1 2.4: an empty pathname is invalid (ENOENT).
* PATH_MAX includes the nul terminator --RR.
*/
-static inline int do_getname(const char __user *filename, char *page)
+static int do_getname(const char __user *filename, char *page)
{
int retval;
unsigned long len = PATH_MAX;
* short-cut DAC fails, then call permission() to do more
* complete permission check.
*/
-static inline int exec_permission_lite(struct inode *inode,
+static int exec_permission_lite(struct inode *inode,
struct nameidata *nd)
{
umode_t mode = inode->i_mode;
static int __emul_lookup_dentry(const char *, struct nameidata *);
/* SMP-safe */
-static inline int
+static __always_inline int
walk_init_root(const char *name, struct nameidata *nd)
{
read_lock(¤t->fs->lock);
return 1;
}
-static inline int __vfs_follow_link(struct nameidata *nd, const char *link)
+static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
{
int res = 0;
char *name;
struct dentry *dentry;
};
-static inline int __do_follow_link(struct path *path, struct nameidata *nd)
+static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
{
int error;
void *cookie;
return 0;
}
-static inline void follow_dotdot(struct nameidata *nd)
+static __always_inline void follow_dotdot(struct nameidata *nd)
{
while(1) {
struct vfsmount *parent;
inode = nd->dentry->d_inode;
if (nd->depth)
- lookup_flags = LOOKUP_FOLLOW;
+ lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
/* At this point we know we have a real path component. */
for(;;) {
last_with_slashes:
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
- nd->flags &= ~LOOKUP_CONTINUE;
+ /* Clear LOOKUP_CONTINUE iff it was previously unset */
+ nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
if (lookup_flags & LOOKUP_PARENT)
goto lookup_parent;
if (this.name[0] == '.') switch (this.len) {
}
/* 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;
+ int fput_needed;
+ struct file *file;
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
}
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 dentry *dentry;
+
+ file = fget_light(dfd, &fput_needed);
+ retval = -EBADF;
+ if (!file)
+ goto unlock_fail;
+
+ dentry = file->f_dentry;
+
+ retval = -ENOTDIR;
+ if (!S_ISDIR(dentry->d_inode->i_mode))
+ goto fput_unlock_fail;
+
+ retval = file_permission(file, MAY_EXEC);
+ if (retval)
+ goto fput_unlock_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;
retval = link_path_walk(name, nd);
out:
- if (unlikely(current->audit_context
- && nd && nd->dentry && nd->dentry->d_inode))
+ if (likely(retval == 0)) {
+ if (unlikely(current->audit_context && nd && nd->dentry &&
+ nd->dentry->d_inode))
audit_inode(name, nd->dentry->d_inode, flags);
+ }
return retval;
+
+fput_unlock_fail:
+ fput_light(file, fput_needed);
+unlock_fail:
+ read_unlock(¤t->fs->lock);
+ return retval;
+}
+
+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(const char *name, unsigned int lookup_flags,
- struct nameidata *nd, int open_flags, int create_mode)
+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;
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);
/**
* path_lookup_open - lookup a file path with open intent
+ * @dfd: the directory to use as base, or AT_FDCWD
* @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,
+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);
}
/**
* path_lookup_create - lookup a file path with open + create intent
+ * @dfd: the directory to use as base, or AT_FDCWD
* @name: pointer to file name
* @lookup_flags: lookup intent flags
* @nd: pointer to nameidata
* @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,
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;
* 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.
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static inline int may_delete(struct inode *dir,struct dentry *victim,int isdir)
+static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
{
int error;
* 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;
* 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;
/*
* 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;
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;
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);
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);
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;
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);
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
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;
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;
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);
* 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;
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;
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);
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;
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);
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;
* 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,
+ int flags)
{
struct dentry *new_dentry;
struct nameidata nd, old_nd;
int error;
char * to;
+ if (flags != 0)
+ return -EINVAL;
+
to = getname(newname);
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;
return error;
}
+asmlinkage long sys_link(const char __user *oldname, const char __user *newname)
+{
+ return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
+}
+
/*
* 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...
return error;
}
-static inline 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;
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;
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;
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;
};
EXPORT_SYMBOL(__user_walk);
+EXPORT_SYMBOL(__user_walk_fd);
EXPORT_SYMBOL(follow_down);
EXPORT_SYMBOL(follow_up);
EXPORT_SYMBOL(get_write_access); /* binfmt_aout */