Merge commit 'v2.6.30' into for-2.6.31
[safe/jmp/linux-2.6] / fs / namei.c
index e584f04..967c3db 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
@@ -31,6 +32,7 @@
 #include <linux/file.h>
 #include <linux/fcntl.h>
 #include <linux/device_cgroup.h>
+#include <linux/fs_struct.h>
 #include <asm/uaccess.h>
 
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
@@ -186,7 +188,7 @@ int generic_permission(struct inode *inode, int mask,
 
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
 
-       if (current->fsuid == inode->i_uid)
+       if (current_fsuid() == inode->i_uid)
                mode >>= 6;
        else {
                if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
@@ -212,8 +214,7 @@ int generic_permission(struct inode *inode, int mask,
         * Read/write DACs are always overridable.
         * Executable DACs are overridable if at least one exec bit is set.
         */
-       if (!(mask & MAY_EXEC) ||
-           (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
+       if (!(mask & MAY_EXEC) || execute_ok(inode))
                if (capable(CAP_DAC_OVERRIDE))
                        return 0;
 
@@ -227,6 +228,16 @@ int generic_permission(struct inode *inode, int mask,
        return -EACCES;
 }
 
+/**
+ * inode_permission  -  check for access rights to a given inode
+ * @inode:     inode to check permission on
+ * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ *
+ * Used to check for read/write/execute permissions on an inode.
+ * We use "fsuid" for this, letting us set arbitrary permissions
+ * for filesystem access without changing the "normal" uids which
+ * are used for other things.
+ */
 int inode_permission(struct inode *inode, int mask)
 {
        int retval;
@@ -248,24 +259,11 @@ int inode_permission(struct inode *inode, int mask)
                        return -EACCES;
        }
 
-       /* Ordinary permission routines do not understand MAY_APPEND. */
-       if (inode->i_op && inode->i_op->permission) {
+       if (inode->i_op->permission)
                retval = inode->i_op->permission(inode, mask);
-               if (!retval) {
-                       /*
-                        * Exec permission on a regular file is denied if none
-                        * of the execute bits are set.
-                        *
-                        * This check should be done by the ->permission()
-                        * method.
-                        */
-                       if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) &&
-                           !(inode->i_mode & S_IXUGO))
-                               return -EACCES;
-               }
-       } else {
+       else
                retval = generic_permission(inode, mask, NULL);
-       }
+
        if (retval)
                return retval;
 
@@ -278,21 +276,6 @@ int inode_permission(struct inode *inode, int mask)
 }
 
 /**
- * vfs_permission  -  check for access rights to a given path
- * @nd:                lookup result that describes the path
- * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
- *
- * Used to check for read/write/execute permissions on a path.
- * We use "fsuid" for this, letting us set arbitrary permissions
- * for filesystem access without changing the "normal" uids which
- * are used for other things.
- */
-int vfs_permission(struct nameidata *nd, int mask)
-{
-       return inode_permission(nd->path.dentry->d_inode, mask);
-}
-
-/**
  * file_permission  -  check for additional access rights to a given file
  * @file:      file to check access rights for
  * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
@@ -302,7 +285,7 @@ int vfs_permission(struct nameidata *nd, int mask)
  *
  * Note:
  *     Do not use this function in new code.  All access checks should
- *     be done using vfs_permission().
+ *     be done using inode_permission().
  */
 int file_permission(struct file *file, int mask)
 {
@@ -451,10 +434,10 @@ static int exec_permission_lite(struct inode *inode)
 {
        umode_t mode = inode->i_mode;
 
-       if (inode->i_op && inode->i_op->permission)
+       if (inode->i_op->permission)
                return -EAGAIN;
 
-       if (current->fsuid == inode->i_uid)
+       if (current_fsuid() == inode->i_uid)
                mode >>= 6;
        else if (in_group_p(inode->i_gid))
                mode >>= 3;
@@ -540,18 +523,6 @@ out_unlock:
        return result;
 }
 
-/* SMP-safe */
-static __always_inline void
-walk_init_root(const char *name, struct nameidata *nd)
-{
-       struct fs_struct *fs = current->fs;
-
-       read_lock(&fs->lock);
-       nd->path = fs->root;
-       path_get(&fs->root);
-       read_unlock(&fs->lock);
-}
-
 /*
  * Wrapper to retry pathname resolution whenever the underlying
  * file system returns an ESTALE.
@@ -589,9 +560,16 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                goto fail;
 
        if (*link == '/') {
+               struct fs_struct *fs = current->fs;
+
                path_put(&nd->path);
-               walk_init_root(link, nd);
+
+               read_lock(&fs->lock);
+               nd->path = fs->root;
+               path_get(&fs->root);
+               read_unlock(&fs->lock);
        }
+
        res = link_path_walk(link, nd);
        if (nd->depth || res || nd->last_type!=LAST_NORM)
                return res;
@@ -872,7 +850,10 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
                nd->flags |= LOOKUP_CONTINUE;
                err = exec_permission_lite(inode);
                if (err == -EAGAIN)
-                       err = vfs_permission(nd, MAY_EXEC);
+                       err = inode_permission(nd->path.dentry->d_inode,
+                                              MAY_EXEC);
+               if (!err)
+                       err = ima_path_check(&nd->path, MAY_EXEC);
                if (err)
                        break;
 
@@ -931,9 +912,6 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
                inode = next.dentry->d_inode;
                if (!inode)
                        goto out_dput;
-               err = -ENOTDIR; 
-               if (!inode->i_op)
-                       goto out_dput;
 
                if (inode->i_op->follow_link) {
                        err = do_follow_link(&next, nd);
@@ -943,9 +921,6 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
                        inode = nd->path.dentry->d_inode;
                        if (!inode)
                                break;
-                       err = -ENOTDIR; 
-                       if (!inode->i_op)
-                               break;
                } else
                        path_to_nameidata(&next, nd);
                err = -ENOTDIR; 
@@ -984,7 +959,7 @@ last_component:
                        break;
                inode = next.dentry->d_inode;
                if ((lookup_flags & LOOKUP_FOLLOW)
-                   && inode && inode->i_op && inode->i_op->follow_link) {
+                   && inode && inode->i_op->follow_link) {
                        err = do_follow_link(&next, nd);
                        if (err)
                                goto return_err;
@@ -996,7 +971,7 @@ last_component:
                        break;
                if (lookup_flags & LOOKUP_DIRECTORY) {
                        err = -ENOTDIR; 
-                       if (!inode->i_op || !inode->i_op->lookup)
+                       if (!inode->i_op->lookup)
                                break;
                }
                goto return_base;
@@ -1155,8 +1130,8 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
  * @nd: pointer to nameidata
  * @open_flags: open intent flags
  */
-int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags,
-               struct nameidata *nd, int open_flags)
+static int path_lookup_open(int dfd, const char *name,
+               unsigned int lookup_flags, struct nameidata *nd, int open_flags)
 {
        struct file *filp = get_empty_filp();
        int err;
@@ -1273,6 +1248,8 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
        int err;
        struct qstr this;
 
+       WARN_ON_ONCE(!mutex_is_locked(&base->d_inode->i_mutex));
+
        err = __lookup_one_len(name, &this, base, len);
        if (err)
                return ERR_PTR(err);
@@ -1347,11 +1324,13 @@ static int user_path_parent(int dfd, const char __user *path,
  */
 static inline int check_sticky(struct inode *dir, struct inode *inode)
 {
+       uid_t fsuid = current_fsuid();
+
        if (!(dir->i_mode & S_ISVTX))
                return 0;
-       if (inode->i_uid == current->fsuid)
+       if (inode->i_uid == fsuid)
                return 0;
-       if (dir->i_uid == current->fsuid)
+       if (dir->i_uid == fsuid)
                return 0;
        return !capable(CAP_FOWNER);
 }
@@ -1391,7 +1370,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
        if (IS_APPEND(dir))
                return -EPERM;
        if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
-           IS_IMMUTABLE(victim->d_inode))
+           IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
                return -EPERM;
        if (isdir) {
                if (!S_ISDIR(victim->d_inode->i_mode))
@@ -1454,20 +1433,18 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
 
        mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
 
-       for (p = p1; p->d_parent != p; p = p->d_parent) {
-               if (p->d_parent == p2) {
-                       mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT);
-                       mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD);
-                       return p;
-               }
+       p = d_ancestor(p2, p1);
+       if (p) {
+               mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD);
+               return p;
        }
 
-       for (p = p2; p->d_parent != p; p = p->d_parent) {
-               if (p->d_parent == p1) {
-                       mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
-                       mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
-                       return p;
-               }
+       p = d_ancestor(p1, p2);
+       if (p) {
+               mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
+               return p;
        }
 
        mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
@@ -1492,50 +1469,53 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
        if (error)
                return error;
 
-       if (!dir->i_op || !dir->i_op->create)
+       if (!dir->i_op->create)
                return -EACCES; /* shouldn't it be ENOSYS? */
        mode &= S_IALLUGO;
        mode |= S_IFREG;
        error = security_inode_create(dir, dentry, mode);
        if (error)
                return error;
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->create(dir, dentry, mode, nd);
        if (!error)
                fsnotify_create(dir, dentry);
        return error;
 }
 
-int may_open(struct nameidata *nd, int acc_mode, int flag)
+int may_open(struct path *path, int acc_mode, int flag)
 {
-       struct dentry *dentry = nd->path.dentry;
+       struct dentry *dentry = path->dentry;
        struct inode *inode = dentry->d_inode;
        int error;
 
        if (!inode)
                return -ENOENT;
 
-       if (S_ISLNK(inode->i_mode))
+       switch (inode->i_mode & S_IFMT) {
+       case S_IFLNK:
                return -ELOOP;
-       
-       if (S_ISDIR(inode->i_mode) && (acc_mode & MAY_WRITE))
-               return -EISDIR;
-
-       /*
-        * FIFO's, sockets and device files are special: they don't
-        * actually live on the filesystem itself, and as such you
-        * can write to them even if the filesystem is read-only.
-        */
-       if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
-               flag &= ~O_TRUNC;
-       } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
-               if (nd->path.mnt->mnt_flags & MNT_NODEV)
+       case S_IFDIR:
+               if (acc_mode & MAY_WRITE)
+                       return -EISDIR;
+               break;
+       case S_IFBLK:
+       case S_IFCHR:
+               if (path->mnt->mnt_flags & MNT_NODEV)
                        return -EACCES;
-
+               /*FALLTHRU*/
+       case S_IFIFO:
+       case S_IFSOCK:
                flag &= ~O_TRUNC;
+               break;
        }
 
-       error = vfs_permission(nd, acc_mode);
+       error = inode_permission(inode, acc_mode);
+       if (error)
+               return error;
+
+       error = ima_path_check(path,
+                              acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
        if (error)
                return error;
        /*
@@ -1569,8 +1549,11 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
                 * Refuse to truncate files with mandatory locks held on them.
                 */
                error = locks_verify_locked(inode);
+               if (!error)
+                       error = security_path_truncate(path, 0,
+                                              ATTR_MTIME|ATTR_CTIME|ATTR_OPEN);
                if (!error) {
-                       DQUOT_INIT(inode);
+                       vfs_dq_init(inode);
 
                        error = do_truncate(dentry, 0,
                                            ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
@@ -1581,7 +1564,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
                        return error;
        } else
                if (flag & FMODE_WRITE)
-                       DQUOT_INIT(inode);
+                       vfs_dq_init(inode);
 
        return 0;
 }
@@ -1598,15 +1581,19 @@ static int __open_namei_create(struct nameidata *nd, struct path *path,
        struct dentry *dir = nd->path.dentry;
 
        if (!IS_POSIXACL(dir->d_inode))
-               mode &= ~current->fs->umask;
+               mode &= ~current_umask();
+       error = security_path_mknod(&nd->path, path->dentry, mode, 0);
+       if (error)
+               goto out_unlock;
        error = vfs_create(dir->d_inode, path->dentry, mode, nd);
+out_unlock:
        mutex_unlock(&dir->d_inode->i_mutex);
        dput(nd->path.dentry);
        nd->path.dentry = path->dentry;
        if (error)
                return error;
        /* Don't check for write permission, don't truncate */
-       return may_open(nd, 0, flag & ~O_TRUNC);
+       return may_open(&nd->path, 0, flag & ~O_TRUNC);
 }
 
 /*
@@ -1650,18 +1637,19 @@ static int open_will_write_to_fs(int flag, struct inode *inode)
  * open_to_namei_flags() for more details.
  */
 struct file *do_filp_open(int dfd, const char *pathname,
-               int open_flag, int mode)
+               int open_flag, int mode, int acc_mode)
 {
        struct file *filp;
        struct nameidata nd;
-       int acc_mode, error;
+       int error;
        struct path path;
        struct dentry *dir;
        int count = 0;
        int will_write;
        int flag = open_to_namei_flags(open_flag);
 
-       acc_mode = MAY_OPEN | ACC_MODE(flag);
+       if (!acc_mode)
+               acc_mode = MAY_OPEN | ACC_MODE(flag);
 
        /* O_TRUNC implies we need access checks for write permissions */
        if (flag & O_TRUNC)
@@ -1709,6 +1697,8 @@ struct file *do_filp_open(int dfd, const char *pathname,
        dir = nd.path.dentry;
        nd.flags &= ~LOOKUP_PARENT;
        nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;
+       if (flag & O_EXCL)
+               nd.flags |= LOOKUP_EXCL;
        mutex_lock(&dir->d_inode->i_mutex);
        path.dentry = lookup_hash(&nd);
        path.mnt = nd.path.mnt;
@@ -1766,7 +1756,7 @@ do_last:
        error = -ENOENT;
        if (!path.dentry->d_inode)
                goto exit_dput;
-       if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
+       if (path.dentry->d_inode->i_op->follow_link)
                goto do_link;
 
        path_to_nameidata(&path, &nd);
@@ -1790,7 +1780,7 @@ ok:
                if (error)
                        goto exit;
        }
-       error = may_open(&nd, acc_mode, flag);
+       error = may_open(&nd.path, acc_mode, flag);
        if (error) {
                if (will_write)
                        mnt_drop_write(nd.path.mnt);
@@ -1880,7 +1870,7 @@ do_link:
  */
 struct file *filp_open(const char *filename, int flags, int mode)
 {
-       return do_filp_open(AT_FDCWD, filename, flags, mode);
+       return do_filp_open(AT_FDCWD, filename, flags, mode, 0);
 }
 EXPORT_SYMBOL(filp_open);
 
@@ -1906,7 +1896,7 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
        if (nd->last_type != LAST_NORM)
                goto fail;
        nd->flags &= ~LOOKUP_PARENT;
-       nd->flags |= LOOKUP_CREATE;
+       nd->flags |= LOOKUP_CREATE | LOOKUP_EXCL;
        nd->intent.open.flags = O_EXCL;
 
        /*
@@ -1947,7 +1937,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
                return -EPERM;
 
-       if (!dir->i_op || !dir->i_op->mknod)
+       if (!dir->i_op->mknod)
                return -EPERM;
 
        error = devcgroup_inode_mknod(mode, dev);
@@ -1958,7 +1948,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        if (error)
                return error;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->mknod(dir, dentry, mode, dev);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -1982,8 +1972,8 @@ static int may_mknod(mode_t mode)
        }
 }
 
-asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
-                               unsigned dev)
+SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, int, mode,
+               unsigned, dev)
 {
        int error;
        char *tmp;
@@ -2003,13 +1993,16 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                goto out_unlock;
        }
        if (!IS_POSIXACL(nd.path.dentry->d_inode))
-               mode &= ~current->fs->umask;
+               mode &= ~current_umask();
        error = may_mknod(mode);
        if (error)
                goto out_dput;
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto out_dput;
+       error = security_path_mknod(&nd.path, dentry, mode, dev);
+       if (error)
+               goto out_drop_write;
        switch (mode & S_IFMT) {
                case 0: case S_IFREG:
                        error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
@@ -2022,6 +2015,7 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                        error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
                        break;
        }
+out_drop_write:
        mnt_drop_write(nd.path.mnt);
 out_dput:
        dput(dentry);
@@ -2033,7 +2027,7 @@ out_unlock:
        return error;
 }
 
-asmlinkage long sys_mknod(const char __user *filename, int mode, unsigned dev)
+SYSCALL_DEFINE3(mknod, const char __user *, filename, int, mode, unsigned, dev)
 {
        return sys_mknodat(AT_FDCWD, filename, mode, dev);
 }
@@ -2045,7 +2039,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (error)
                return error;
 
-       if (!dir->i_op || !dir->i_op->mkdir)
+       if (!dir->i_op->mkdir)
                return -EPERM;
 
        mode &= (S_IRWXUGO|S_ISVTX);
@@ -2053,14 +2047,14 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (error)
                return error;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->mkdir(dir, dentry, mode);
        if (!error)
                fsnotify_mkdir(dir, dentry);
        return error;
 }
 
-asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
+SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, int, mode)
 {
        int error = 0;
        char * tmp;
@@ -2077,11 +2071,15 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
                goto out_unlock;
 
        if (!IS_POSIXACL(nd.path.dentry->d_inode))
-               mode &= ~current->fs->umask;
+               mode &= ~current_umask();
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto out_dput;
+       error = security_path_mkdir(&nd.path, dentry, mode);
+       if (error)
+               goto out_drop_write;
        error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+out_drop_write:
        mnt_drop_write(nd.path.mnt);
 out_dput:
        dput(dentry);
@@ -2093,7 +2091,7 @@ out_err:
        return error;
 }
 
-asmlinkage long sys_mkdir(const char __user *pathname, int mode)
+SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
 {
        return sys_mkdirat(AT_FDCWD, pathname, mode);
 }
@@ -2132,10 +2130,10 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (error)
                return error;
 
-       if (!dir->i_op || !dir->i_op->rmdir)
+       if (!dir->i_op->rmdir)
                return -EPERM;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
 
        mutex_lock(&dentry->d_inode->i_mutex);
        dentry_unhash(dentry);
@@ -2170,16 +2168,19 @@ static long do_rmdir(int dfd, const char __user *pathname)
                return error;
 
        switch(nd.last_type) {
-               case LAST_DOTDOT:
-                       error = -ENOTEMPTY;
-                       goto exit1;
-               case LAST_DOT:
-                       error = -EINVAL;
-                       goto exit1;
-               case LAST_ROOT:
-                       error = -EBUSY;
-                       goto exit1;
+       case LAST_DOTDOT:
+               error = -ENOTEMPTY;
+               goto exit1;
+       case LAST_DOT:
+               error = -EINVAL;
+               goto exit1;
+       case LAST_ROOT:
+               error = -EBUSY;
+               goto exit1;
        }
+
+       nd.flags &= ~LOOKUP_PARENT;
+
        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
        error = PTR_ERR(dentry);
@@ -2188,7 +2189,11 @@ static long do_rmdir(int dfd, const char __user *pathname)
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto exit3;
+       error = security_path_rmdir(&nd.path, dentry);
+       if (error)
+               goto exit4;
        error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+exit4:
        mnt_drop_write(nd.path.mnt);
 exit3:
        dput(dentry);
@@ -2200,7 +2205,7 @@ exit1:
        return error;
 }
 
-asmlinkage long sys_rmdir(const char __user *pathname)
+SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
 {
        return do_rmdir(AT_FDCWD, pathname);
 }
@@ -2212,10 +2217,10 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
        if (error)
                return error;
 
-       if (!dir->i_op || !dir->i_op->unlink)
+       if (!dir->i_op->unlink)
                return -EPERM;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
 
        mutex_lock(&dentry->d_inode->i_mutex);
        if (d_mountpoint(dentry))
@@ -2257,6 +2262,9 @@ static long do_unlinkat(int dfd, const char __user *pathname)
        error = -EISDIR;
        if (nd.last_type != LAST_NORM)
                goto exit1;
+
+       nd.flags &= ~LOOKUP_PARENT;
+
        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
        error = PTR_ERR(dentry);
@@ -2270,7 +2278,11 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                error = mnt_want_write(nd.path.mnt);
                if (error)
                        goto exit2;
+               error = security_path_unlink(&nd.path, dentry);
+               if (error)
+                       goto exit3;
                error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+exit3:
                mnt_drop_write(nd.path.mnt);
        exit2:
                dput(dentry);
@@ -2289,7 +2301,7 @@ slashes:
        goto exit2;
 }
 
-asmlinkage long sys_unlinkat(int dfd, const char __user *pathname, int flag)
+SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag)
 {
        if ((flag & ~AT_REMOVEDIR) != 0)
                return -EINVAL;
@@ -2300,7 +2312,7 @@ asmlinkage long sys_unlinkat(int dfd, const char __user *pathname, int flag)
        return do_unlinkat(dfd, pathname);
 }
 
-asmlinkage long sys_unlink(const char __user *pathname)
+SYSCALL_DEFINE1(unlink, const char __user *, pathname)
 {
        return do_unlinkat(AT_FDCWD, pathname);
 }
@@ -2312,22 +2324,22 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
        if (error)
                return error;
 
-       if (!dir->i_op || !dir->i_op->symlink)
+       if (!dir->i_op->symlink)
                return -EPERM;
 
        error = security_inode_symlink(dir, dentry, oldname);
        if (error)
                return error;
 
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->symlink(dir, dentry, oldname);
        if (!error)
                fsnotify_create(dir, dentry);
        return error;
 }
 
-asmlinkage long sys_symlinkat(const char __user *oldname,
-                             int newdfd, const char __user *newname)
+SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
+               int, newdfd, const char __user *, newname)
 {
        int error;
        char *from;
@@ -2351,7 +2363,11 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto out_dput;
+       error = security_path_symlink(&nd.path, dentry, from);
+       if (error)
+               goto out_drop_write;
        error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
+out_drop_write:
        mnt_drop_write(nd.path.mnt);
 out_dput:
        dput(dentry);
@@ -2364,7 +2380,7 @@ out_putname:
        return error;
 }
 
-asmlinkage long sys_symlink(const char __user *oldname, const char __user *newname)
+SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname)
 {
        return sys_symlinkat(oldname, AT_FDCWD, newname);
 }
@@ -2389,7 +2405,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
         */
        if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
                return -EPERM;
-       if (!dir->i_op || !dir->i_op->link)
+       if (!dir->i_op->link)
                return -EPERM;
        if (S_ISDIR(inode->i_mode))
                return -EPERM;
@@ -2399,7 +2415,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
                return error;
 
        mutex_lock(&inode->i_mutex);
-       DQUOT_INIT(dir);
+       vfs_dq_init(dir);
        error = dir->i_op->link(old_dentry, dir, new_dentry);
        mutex_unlock(&inode->i_mutex);
        if (!error)
@@ -2416,9 +2432,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_linkat(int olddfd, const char __user *oldname,
-                          int newdfd, const char __user *newname,
-                          int flags)
+SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
+               int, newdfd, const char __user *, newname, int, flags)
 {
        struct dentry *new_dentry;
        struct nameidata nd;
@@ -2448,7 +2463,11 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto out_dput;
+       error = security_path_link(old_path.dentry, &nd.path, new_dentry);
+       if (error)
+               goto out_drop_write;
        error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
+out_drop_write:
        mnt_drop_write(nd.path.mnt);
 out_dput:
        dput(new_dentry);
@@ -2463,7 +2482,7 @@ out:
        return error;
 }
 
-asmlinkage long sys_link(const char __user *oldname, const char __user *newname)
+SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname)
 {
        return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
@@ -2592,11 +2611,11 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (error)
                return error;
 
-       if (!old_dir->i_op || !old_dir->i_op->rename)
+       if (!old_dir->i_op->rename)
                return -EPERM;
 
-       DQUOT_INIT(old_dir);
-       DQUOT_INIT(new_dir);
+       vfs_dq_init(old_dir);
+       vfs_dq_init(new_dir);
 
        old_name = fsnotify_oldname_init(old_dentry->d_name.name);
 
@@ -2614,8 +2633,8 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        return error;
 }
 
-asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
-                            int newdfd, const char __user *newname)
+SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
+               int, newdfd, const char __user *, newname)
 {
        struct dentry *old_dir, *new_dir;
        struct dentry *old_dentry, *new_dentry;
@@ -2646,6 +2665,10 @@ asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
        if (newnd.last_type != LAST_NORM)
                goto exit2;
 
+       oldnd.flags &= ~LOOKUP_PARENT;
+       newnd.flags &= ~LOOKUP_PARENT;
+       newnd.flags |= LOOKUP_RENAME_TARGET;
+
        trap = lock_rename(new_dir, old_dir);
 
        old_dentry = lookup_hash(&oldnd);
@@ -2680,8 +2703,13 @@ asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
        error = mnt_want_write(oldnd.path.mnt);
        if (error)
                goto exit5;
+       error = security_path_rename(&oldnd.path, old_dentry,
+                                    &newnd.path, new_dentry);
+       if (error)
+               goto exit6;
        error = vfs_rename(old_dir->d_inode, old_dentry,
                                   new_dir->d_inode, new_dentry);
+exit6:
        mnt_drop_write(oldnd.path.mnt);
 exit5:
        dput(new_dentry);
@@ -2699,7 +2727,7 @@ exit:
        return error;
 }
 
-asmlinkage long sys_rename(const char __user *oldname, const char __user *newname)
+SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
 {
        return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname);
 }
@@ -2751,13 +2779,16 @@ int vfs_follow_link(struct nameidata *nd, const char *link)
 /* get the link contents into pagecache */
 static char *page_getlink(struct dentry * dentry, struct page **ppage)
 {
-       struct page * page;
+       char *kaddr;
+       struct page *page;
        struct address_space *mapping = dentry->d_inode->i_mapping;
        page = read_mapping_page(mapping, 0, NULL);
        if (IS_ERR(page))
                return (char*)page;
        *ppage = page;
-       return kmap(page);
+       kaddr = kmap(page);
+       nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1);
+       return kaddr;
 }
 
 int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
@@ -2789,18 +2820,23 @@ void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
        }
 }
 
-int __page_symlink(struct inode *inode, const char *symname, int len,
-               gfp_t gfp_mask)
+/*
+ * The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS
+ */
+int __page_symlink(struct inode *inode, const char *symname, int len, int nofs)
 {
        struct address_space *mapping = inode->i_mapping;
        struct page *page;
        void *fsdata;
        int err;
        char *kaddr;
+       unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE;
+       if (nofs)
+               flags |= AOP_FLAG_NOFS;
 
 retry:
        err = pagecache_write_begin(NULL, mapping, 0, len-1,
-                               AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
+                               flags, &page, &fsdata);
        if (err)
                goto fail;
 
@@ -2824,7 +2860,7 @@ fail:
 int page_symlink(struct inode *inode, const char *symname, int len)
 {
        return __page_symlink(inode, symname, len,
-                       mapping_gfp_mask(inode->i_mapping));
+                       !(mapping_gfp_mask(inode->i_mapping) & __GFP_FS));
 }
 
 const struct inode_operations page_symlink_inode_operations = {
@@ -2850,7 +2886,6 @@ EXPORT_SYMBOL(path_lookup);
 EXPORT_SYMBOL(kern_path);
 EXPORT_SYMBOL(vfs_path_lookup);
 EXPORT_SYMBOL(inode_permission);
-EXPORT_SYMBOL(vfs_permission);
 EXPORT_SYMBOL(file_permission);
 EXPORT_SYMBOL(unlock_rename);
 EXPORT_SYMBOL(vfs_create);