string: factorize skip_spaces and export it to be generally available
[safe/jmp/linux-2.6] / fs / namei.c
index e203691..87f97ba 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])
@@ -167,19 +169,10 @@ void putname(const char *name)
 EXPORT_SYMBOL(putname);
 #endif
 
-
-/**
- * generic_permission  -  check for access rights on a Posix-like filesystem
- * @inode:     inode to check access rights for
- * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
- * @check_acl: optional callback to check for Posix ACLs
- *
- * Used to check for read/write/execute permissions on a file.
- * We use "fsuid" for this, letting us set arbitrary permissions
- * for filesystem access without changing the "normal" uids which
- * are used for other things..
+/*
+ * This does basic POSIX ACL permission checking
  */
-int generic_permission(struct inode *inode, int mask,
+static int acl_permission_check(struct inode *inode, int mask,
                int (*check_acl)(struct inode *inode, int mask))
 {
        umode_t                 mode = inode->i_mode;
@@ -191,9 +184,7 @@ int generic_permission(struct inode *inode, int mask,
        else {
                if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
                        int error = check_acl(inode, mask);
-                       if (error == -EACCES)
-                               goto check_capabilities;
-                       else if (error != -EAGAIN)
+                       if (error != -EAGAIN)
                                return error;
                }
 
@@ -206,8 +197,32 @@ int generic_permission(struct inode *inode, int mask,
         */
        if ((mask & ~mode) == 0)
                return 0;
+       return -EACCES;
+}
+
+/**
+ * generic_permission  -  check for access rights on a Posix-like filesystem
+ * @inode:     inode to check access rights for
+ * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @check_acl: optional callback to check for Posix ACLs
+ *
+ * Used to check for read/write/execute permissions on a file.
+ * 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 generic_permission(struct inode *inode, int mask,
+               int (*check_acl)(struct inode *inode, int mask))
+{
+       int ret;
+
+       /*
+        * Do the basic POSIX ACL permission checks.
+        */
+       ret = acl_permission_check(inode, mask, check_acl);
+       if (ret != -EACCES)
+               return ret;
 
- check_capabilities:
        /*
         * Read/write DACs are always overridable.
         * Executable DACs are overridable if at least one exec bit is set.
@@ -257,10 +272,10 @@ int inode_permission(struct inode *inode, int mask)
                        return -EACCES;
        }
 
-       if (inode->i_op && inode->i_op->permission)
+       if (inode->i_op->permission)
                retval = inode->i_op->permission(inode, mask);
        else
-               retval = generic_permission(inode, mask, NULL);
+               retval = generic_permission(inode, mask, inode->i_op->check_acl);
 
        if (retval)
                return retval;
@@ -430,29 +445,22 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
  */
 static int exec_permission_lite(struct inode *inode)
 {
-       umode_t mode = inode->i_mode;
-
-       if (inode->i_op && inode->i_op->permission)
-               return -EAGAIN;
-
-       if (current_fsuid() == inode->i_uid)
-               mode >>= 6;
-       else if (in_group_p(inode->i_gid))
-               mode >>= 3;
+       int ret;
 
-       if (mode & MAY_EXEC)
-               goto ok;
-
-       if ((inode->i_mode & S_IXUGO) && capable(CAP_DAC_OVERRIDE))
-               goto ok;
-
-       if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_OVERRIDE))
+       if (inode->i_op->permission) {
+               ret = inode->i_op->permission(inode, MAY_EXEC);
+               if (!ret)
+                       goto ok;
+               return ret;
+       }
+       ret = acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl);
+       if (!ret)
                goto ok;
 
-       if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_READ_SEARCH))
+       if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH))
                goto ok;
 
-       return -EACCES;
+       return ret;
 ok:
        return security_inode_permission(inode, MAY_EXEC);
 }
@@ -550,6 +558,17 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd
        return result;
 }
 
+static __always_inline void set_root(struct nameidata *nd)
+{
+       if (!nd->root.mnt) {
+               struct fs_struct *fs = current->fs;
+               read_lock(&fs->lock);
+               nd->root = fs->root;
+               path_get(&nd->root);
+               read_unlock(&fs->lock);
+       }
+}
+
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
 {
        int res = 0;
@@ -558,14 +577,10 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                goto fail;
 
        if (*link == '/') {
-               struct fs_struct *fs = current->fs;
-
+               set_root(nd);
                path_put(&nd->path);
-
-               read_lock(&fs->lock);
-               nd->path = fs->root;
-               path_get(&fs->root);
-               read_unlock(&fs->lock);
+               nd->path = nd->root;
+               path_get(&nd->root);
        }
 
        res = link_path_walk(link, nd);
@@ -666,23 +681,23 @@ loop:
        return err;
 }
 
-int follow_up(struct vfsmount **mnt, struct dentry **dentry)
+int follow_up(struct path *path)
 {
        struct vfsmount *parent;
        struct dentry *mountpoint;
        spin_lock(&vfsmount_lock);
-       parent=(*mnt)->mnt_parent;
-       if (parent == *mnt) {
+       parent = path->mnt->mnt_parent;
+       if (parent == path->mnt) {
                spin_unlock(&vfsmount_lock);
                return 0;
        }
        mntget(parent);
-       mountpoint=dget((*mnt)->mnt_mountpoint);
+       mountpoint = dget(path->mnt->mnt_mountpoint);
        spin_unlock(&vfsmount_lock);
-       dput(*dentry);
-       *dentry = mountpoint;
-       mntput(*mnt);
-       *mnt = parent;
+       dput(path->dentry);
+       path->dentry = mountpoint;
+       mntput(path->mnt);
+       path->mnt = parent;
        return 1;
 }
 
@@ -693,7 +708,7 @@ static int __follow_mount(struct path *path)
 {
        int res = 0;
        while (d_mountpoint(path->dentry)) {
-               struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry);
+               struct vfsmount *mounted = lookup_mnt(path);
                if (!mounted)
                        break;
                dput(path->dentry);
@@ -706,32 +721,32 @@ static int __follow_mount(struct path *path)
        return res;
 }
 
-static void follow_mount(struct vfsmount **mnt, struct dentry **dentry)
+static void follow_mount(struct path *path)
 {
-       while (d_mountpoint(*dentry)) {
-               struct vfsmount *mounted = lookup_mnt(*mnt, *dentry);
+       while (d_mountpoint(path->dentry)) {
+               struct vfsmount *mounted = lookup_mnt(path);
                if (!mounted)
                        break;
-               dput(*dentry);
-               mntput(*mnt);
-               *mnt = mounted;
-               *dentry = dget(mounted->mnt_root);
+               dput(path->dentry);
+               mntput(path->mnt);
+               path->mnt = mounted;
+               path->dentry = dget(mounted->mnt_root);
        }
 }
 
 /* no need for dcache_lock, as serialization is taken care in
  * namespace.c
  */
-int follow_down(struct vfsmount **mnt, struct dentry **dentry)
+int follow_down(struct path *path)
 {
        struct vfsmount *mounted;
 
-       mounted = lookup_mnt(*mnt, *dentry);
+       mounted = lookup_mnt(path);
        if (mounted) {
-               dput(*dentry);
-               mntput(*mnt);
-               *mnt = mounted;
-               *dentry = dget(mounted->mnt_root);
+               dput(path->dentry);
+               mntput(path->mnt);
+               path->mnt = mounted;
+               path->dentry = dget(mounted->mnt_root);
                return 1;
        }
        return 0;
@@ -739,19 +754,16 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry)
 
 static __always_inline void follow_dotdot(struct nameidata *nd)
 {
-       struct fs_struct *fs = current->fs;
+       set_root(nd);
 
        while(1) {
                struct vfsmount *parent;
                struct dentry *old = nd->path.dentry;
 
-                read_lock(&fs->lock);
-               if (nd->path.dentry == fs->root.dentry &&
-                   nd->path.mnt == fs->root.mnt) {
-                        read_unlock(&fs->lock);
+               if (nd->path.dentry == nd->root.dentry &&
+                   nd->path.mnt == nd->root.mnt) {
                        break;
                }
-                read_unlock(&fs->lock);
                spin_lock(&dcache_lock);
                if (nd->path.dentry != nd->path.mnt->mnt_root) {
                        nd->path.dentry = dget(nd->path.dentry->d_parent);
@@ -773,7 +785,7 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
                mntput(nd->path.mnt);
                nd->path.mnt = parent;
        }
-       follow_mount(&nd->path.mnt, &nd->path.dentry);
+       follow_mount(&nd->path);
 }
 
 /*
@@ -847,9 +859,6 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
 
                nd->flags |= LOOKUP_CONTINUE;
                err = exec_permission_lite(inode);
-               if (err == -EAGAIN)
-                       err = inode_permission(nd->path.dentry->d_inode,
-                                              MAY_EXEC);
                if (err)
                        break;
 
@@ -908,9 +917,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);
@@ -920,9 +926,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; 
@@ -961,7 +964,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;
@@ -973,7 +976,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;
@@ -1018,25 +1021,23 @@ static int path_walk(const char *name, struct nameidata *nd)
        return link_path_walk(name, nd);
 }
 
-/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
-static int do_path_lookup(int dfd, const char *name,
-                               unsigned int flags, struct nameidata *nd)
+static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd)
 {
        int retval = 0;
        int fput_needed;
        struct file *file;
-       struct fs_struct *fs = current->fs;
 
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags;
        nd->depth = 0;
+       nd->root.mnt = NULL;
 
        if (*name=='/') {
-               read_lock(&fs->lock);
-               nd->path = fs->root;
-               path_get(&fs->root);
-               read_unlock(&fs->lock);
+               set_root(nd);
+               nd->path = nd->root;
+               path_get(&nd->root);
        } else if (dfd == AT_FDCWD) {
+               struct fs_struct *fs = current->fs;
                read_lock(&fs->lock);
                nd->path = fs->pwd;
                path_get(&fs->pwd);
@@ -1064,17 +1065,29 @@ static int do_path_lookup(int dfd, const char *name,
 
                fput_light(file, fput_needed);
        }
+       return 0;
 
-       retval = path_walk(name, nd);
+fput_fail:
+       fput_light(file, fput_needed);
+out_fail:
+       return retval;
+}
+
+/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
+static int do_path_lookup(int dfd, const char *name,
+                               unsigned int flags, struct nameidata *nd)
+{
+       int retval = path_init(dfd, name, flags, nd);
+       if (!retval)
+               retval = path_walk(name, nd);
        if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
                                nd->path.dentry->d_inode))
                audit_inode(name, nd->path.dentry);
-out_fail:
+       if (nd->root.mnt) {
+               path_put(&nd->root);
+               nd->root.mnt = NULL;
+       }
        return retval;
-
-fput_fail:
-       fput_light(file, fput_needed);
-       goto out_fail;
 }
 
 int path_lookup(const char *name, unsigned int flags,
@@ -1114,14 +1127,18 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
        nd->path.dentry = dentry;
        nd->path.mnt = mnt;
        path_get(&nd->path);
+       nd->root = nd->path;
+       path_get(&nd->root);
 
        retval = path_walk(name, nd);
        if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
                                nd->path.dentry->d_inode))
                audit_inode(name, nd->path.dentry);
 
-       return retval;
+       path_put(&nd->root);
+       nd->root.mnt = NULL;
 
+       return retval;
 }
 
 /**
@@ -1132,8 +1149,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;
@@ -1250,6 +1267,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);
@@ -1260,28 +1279,6 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
        return __lookup_hash(&this, base, NULL);
 }
 
-/**
- * lookup_one_noperm - bad hack for sysfs
- * @name:      pathname component to lookup
- * @base:      base directory to lookup from
- *
- * This is a variant of lookup_one_len that doesn't perform any permission
- * checks.   It's a horrible hack to work around the braindead sysfs
- * architecture and should not be used anywhere else.
- *
- * DON'T USE THIS FUNCTION EVER, thanks.
- */
-struct dentry *lookup_one_noperm(const char *name, struct dentry *base)
-{
-       int err;
-       struct qstr this;
-
-       err = __lookup_one_len(name, &this, base, strlen(name));
-       if (err)
-               return ERR_PTR(err);
-       return __lookup_hash(&this, base, NULL);
-}
-
 int user_path_at(int dfd, const char __user *name, unsigned flags,
                 struct path *path)
 {
@@ -1469,14 +1466,14 @@ 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);
@@ -1492,55 +1489,64 @@ int may_open(struct path *path, int acc_mode, int flag)
        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)) {
+       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 = inode_permission(inode, acc_mode);
        if (error)
                return error;
+
+       error = ima_path_check(path, acc_mode ?
+                              acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) :
+                              ACC_MODE(flag) & (MAY_READ | MAY_WRITE),
+                              IMA_COUNT_UPDATE);
+
+       if (error)
+               return error;
        /*
         * An append-only file must be opened in append mode for writing.
         */
        if (IS_APPEND(inode)) {
+               error = -EPERM;
                if  ((flag & FMODE_WRITE) && !(flag & O_APPEND))
-                       return -EPERM;
+                       goto err_out;
                if (flag & O_TRUNC)
-                       return -EPERM;
+                       goto err_out;
        }
 
        /* O_NOATIME can only be set by the owner or superuser */
        if (flag & O_NOATIME)
-               if (!is_owner_or_cap(inode))
-                       return -EPERM;
+               if (!is_owner_or_cap(inode)) {
+                       error = -EPERM;
+                       goto err_out;
+               }
 
        /*
         * Ensure there are no outstanding leases on the file.
         */
        error = break_lease(inode, flag);
        if (error)
-               return error;
+               goto err_out;
 
        if (flag & O_TRUNC) {
                error = get_write_access(inode);
                if (error)
-                       return error;
+                       goto err_out;
 
                /*
                 * Refuse to truncate files with mandatory locks held on them.
@@ -1550,7 +1556,7 @@ int may_open(struct path *path, int acc_mode, int flag)
                        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,
@@ -1558,12 +1564,17 @@ int may_open(struct path *path, int acc_mode, int flag)
                }
                put_write_access(inode);
                if (error)
-                       return error;
+                       goto err_out;
        } else
                if (flag & FMODE_WRITE)
-                       DQUOT_INIT(inode);
+                       vfs_dq_init(inode);
 
        return 0;
+err_out:
+       ima_counts_put(path, acc_mode ?
+                      acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) :
+                      ACC_MODE(flag) & (MAY_READ | MAY_WRITE));
+       return error;
 }
 
 /*
@@ -1578,7 +1589,7 @@ 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;
@@ -1634,18 +1645,28 @@ 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);
+       /*
+        * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
+        * check for O_DSYNC if the need any syncing at all we enforce it's
+        * always set instead of having to deal with possibly weird behaviour
+        * for malicious applications setting only __O_SYNC.
+        */
+       if (open_flag & __O_SYNC)
+               open_flag |= O_DSYNC;
+
+       if (!acc_mode)
+               acc_mode = MAY_OPEN | ACC_MODE(flag);
 
        /* O_TRUNC implies we need access checks for write permissions */
        if (flag & O_TRUNC)
@@ -1670,9 +1691,17 @@ struct file *do_filp_open(int dfd, const char *pathname,
        /*
         * Create - we need to know the parent.
         */
-       error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd);
+       error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
        if (error)
                return ERR_PTR(error);
+       error = path_walk(pathname, &nd);
+       if (error) {
+               if (nd.root.mnt)
+                       path_put(&nd.root);
+               return ERR_PTR(error);
+       }
+       if (unlikely(!audit_dummy_context()))
+               audit_inode(pathname, nd.path.dentry);
 
        /*
         * We have the parent and last component. First of all, check
@@ -1729,7 +1758,13 @@ do_last:
                        goto exit;
                }
                filp = nameidata_to_filp(&nd, open_flag);
+               if (IS_ERR(filp))
+                       ima_counts_put(&nd.path,
+                                      acc_mode & (MAY_READ | MAY_WRITE |
+                                                  MAY_EXEC));
                mnt_drop_write(nd.path.mnt);
+               if (nd.root.mnt)
+                       path_put(&nd.root);
                return filp;
        }
 
@@ -1752,7 +1787,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);
@@ -1783,6 +1818,9 @@ ok:
                goto exit;
        }
        filp = nameidata_to_filp(&nd, open_flag);
+       if (IS_ERR(filp))
+               ima_counts_put(&nd.path,
+                              acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
        /*
         * It is now safe to drop the mnt write
         * because the filp has had a write taken
@@ -1790,6 +1828,8 @@ ok:
         */
        if (will_write)
                mnt_drop_write(nd.path.mnt);
+       if (nd.root.mnt)
+               path_put(&nd.root);
        return filp;
 
 exit_mutex_unlock:
@@ -1800,6 +1840,8 @@ exit:
        if (!IS_ERR(nd.intent.open.file))
                release_open_intent(&nd);
 exit_parent:
+       if (nd.root.mnt)
+               path_put(&nd.root);
        path_put(&nd.path);
        return ERR_PTR(error);
 
@@ -1828,6 +1870,8 @@ do_link:
                 * with "intent.open".
                 */
                release_open_intent(&nd);
+               if (nd.root.mnt)
+                       path_put(&nd.root);
                return ERR_PTR(error);
        }
        nd.flags &= ~LOOKUP_PARENT;
@@ -1866,7 +1910,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);
 
@@ -1933,7 +1977,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);
@@ -1944,7 +1988,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);
@@ -1968,8 +2012,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;
@@ -1989,7 +2033,7 @@ 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;
@@ -2023,7 +2067,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);
 }
@@ -2035,7 +2079,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);
@@ -2043,14 +2087,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;
@@ -2067,7 +2111,7 @@ 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;
@@ -2087,7 +2131,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);
 }
@@ -2126,10 +2170,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);
@@ -2201,7 +2245,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);
 }
@@ -2213,10 +2257,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))
@@ -2297,7 +2341,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;
@@ -2308,7 +2352,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);
 }
@@ -2320,22 +2364,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;
@@ -2376,7 +2420,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);
 }
@@ -2401,7 +2445,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;
@@ -2411,7 +2455,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)
@@ -2428,9 +2472,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;
@@ -2479,7 +2522,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);
 }
@@ -2608,11 +2651,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);
 
@@ -2630,8 +2673,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;
@@ -2724,7 +2767,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);
 }
@@ -2817,18 +2860,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;
 
@@ -2852,7 +2900,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 = {
@@ -2893,10 +2941,3 @@ EXPORT_SYMBOL(vfs_symlink);
 EXPORT_SYMBOL(vfs_unlink);
 EXPORT_SYMBOL(dentry_unhash);
 EXPORT_SYMBOL(generic_readlink);
-
-/* to be mentioned only in INIT_TASK */
-struct fs_struct init_fs = {
-       .count          = ATOMIC_INIT(1),
-       .lock           = RW_LOCK_UNLOCKED,
-       .umask          = 0022,
-};