Merge tag 'lsk-v3.10-android-14.07' into develop-3.10
[firefly-linux-kernel-4.4.55.git] / fs / open.c
index b52cf013ffa145de32e5c641aba7a3625de8cc29..86092bde31f4827eec31eec454ec60e2c1dcb1e8 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -30,6 +30,7 @@
 #include <linux/fs_struct.h>
 #include <linux/ima.h>
 #include <linux/dnotify.h>
+#include <linux/compat.h>
 
 #include "internal.h"
 
@@ -61,33 +62,22 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
        return ret;
 }
 
-static long do_sys_truncate(const char __user *pathname, loff_t length)
+long vfs_truncate(struct path *path, loff_t length)
 {
-       struct path path;
        struct inode *inode;
-       int error;
+       long error;
 
-       error = -EINVAL;
-       if (length < 0) /* sorry, but loff_t says... */
-               goto out;
-
-       error = user_path(pathname, &path);
-       if (error)
-               goto out;
-       inode = path.dentry->d_inode;
+       inode = path->dentry->d_inode;
 
        /* For directories it's -EISDIR, for other non-regulars - -EINVAL */
-       error = -EISDIR;
        if (S_ISDIR(inode->i_mode))
-               goto dput_and_out;
-
-       error = -EINVAL;
+               return -EISDIR;
        if (!S_ISREG(inode->i_mode))
-               goto dput_and_out;
+               return -EINVAL;
 
-       error = mnt_want_write(path.mnt);
+       error = mnt_want_write(path->mnt);
        if (error)
-               goto dput_and_out;
+               goto out;
 
        error = inode_permission(inode, MAY_WRITE);
        if (error)
@@ -111,48 +101,76 @@ static long do_sys_truncate(const char __user *pathname, loff_t length)
 
        error = locks_verify_truncate(inode, NULL, length);
        if (!error)
-               error = security_path_truncate(&path);
+               error = security_path_truncate(path);
        if (!error)
-               error = do_truncate(path.dentry, length, 0, NULL);
+               error = do_truncate(path->dentry, length, 0, NULL);
 
 put_write_and_out:
        put_write_access(inode);
 mnt_drop_write_and_out:
-       mnt_drop_write(path.mnt);
-dput_and_out:
-       path_put(&path);
+       mnt_drop_write(path->mnt);
 out:
        return error;
 }
+EXPORT_SYMBOL_GPL(vfs_truncate);
+
+static long do_sys_truncate(const char __user *pathname, loff_t length)
+{
+       unsigned int lookup_flags = LOOKUP_FOLLOW;
+       struct path path;
+       int error;
+
+       if (length < 0) /* sorry, but loff_t says... */
+               return -EINVAL;
+
+retry:
+       error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+       if (!error) {
+               error = vfs_truncate(&path, length);
+               path_put(&path);
+       }
+       if (retry_estale(error, lookup_flags)) {
+               lookup_flags |= LOOKUP_REVAL;
+               goto retry;
+       }
+       return error;
+}
 
 SYSCALL_DEFINE2(truncate, const char __user *, path, long, length)
 {
        return do_sys_truncate(path, length);
 }
 
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, length)
+{
+       return do_sys_truncate(path, length);
+}
+#endif
+
 static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
 {
-       struct inode * inode;
+       struct inode *inode;
        struct dentry *dentry;
-       struct file * file;
+       struct fd f;
        int error;
 
        error = -EINVAL;
        if (length < 0)
                goto out;
        error = -EBADF;
-       file = fget(fd);
-       if (!file)
+       f = fdget(fd);
+       if (!f.file)
                goto out;
 
        /* explicitly opened as large or we are on 64-bit box */
-       if (file->f_flags & O_LARGEFILE)
+       if (f.file->f_flags & O_LARGEFILE)
                small = 0;
 
-       dentry = file->f_path.dentry;
+       dentry = f.file->f_path.dentry;
        inode = dentry->d_inode;
        error = -EINVAL;
-       if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
+       if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
                goto out_putf;
 
        error = -EINVAL;
@@ -164,59 +182,48 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
        if (IS_APPEND(inode))
                goto out_putf;
 
-       error = locks_verify_truncate(inode, file, length);
+       sb_start_write(inode->i_sb);
+       error = locks_verify_truncate(inode, f.file, length);
        if (!error)
-               error = security_path_truncate(&file->f_path);
+               error = security_path_truncate(&f.file->f_path);
        if (!error)
-               error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
+               error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
+       sb_end_write(inode->i_sb);
 out_putf:
-       fput(file);
+       fdput(f);
 out:
        return error;
 }
 
 SYSCALL_DEFINE2(ftruncate, unsigned int, fd, unsigned long, length)
 {
-       long ret = do_sys_ftruncate(fd, length, 1);
-       /* avoid REGPARM breakage on x86: */
-       asmlinkage_protect(2, ret, fd, length);
-       return ret;
+       return do_sys_ftruncate(fd, length, 1);
 }
 
-/* LFS versions of truncate are only needed on 32 bit machines */
-#if BITS_PER_LONG == 32
-SYSCALL_DEFINE(truncate64)(const char __user * path, loff_t length)
-{
-       return do_sys_truncate(path, length);
-}
-#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
-asmlinkage long SyS_truncate64(long path, loff_t length)
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE2(ftruncate, unsigned int, fd, compat_ulong_t, length)
 {
-       return SYSC_truncate64((const char __user *) path, length);
+       return do_sys_ftruncate(fd, length, 1);
 }
-SYSCALL_ALIAS(sys_truncate64, SyS_truncate64);
 #endif
 
-SYSCALL_DEFINE(ftruncate64)(unsigned int fd, loff_t length)
+/* LFS versions of truncate are only needed on 32 bit machines */
+#if BITS_PER_LONG == 32
+SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length)
 {
-       long ret = do_sys_ftruncate(fd, length, 0);
-       /* avoid REGPARM breakage on x86: */
-       asmlinkage_protect(2, ret, fd, length);
-       return ret;
+       return do_sys_truncate(path, length);
 }
-#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
-asmlinkage long SyS_ftruncate64(long fd, loff_t length)
+
+SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length)
 {
-       return SYSC_ftruncate64((unsigned int) fd, length);
+       return do_sys_ftruncate(fd, length, 0);
 }
-SYSCALL_ALIAS(sys_ftruncate64, SyS_ftruncate64);
-#endif
 #endif /* BITS_PER_LONG == 32 */
 
 
 int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
+       struct inode *inode = file_inode(file);
        long ret;
 
        if (offset < 0 || len <= 0)
@@ -266,31 +273,24 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
        if (!file->f_op->fallocate)
                return -EOPNOTSUPP;
 
-       return file->f_op->fallocate(file, mode, offset, len);
+       sb_start_write(inode->i_sb);
+       ret = file->f_op->fallocate(file, mode, offset, len);
+       sb_end_write(inode->i_sb);
+       return ret;
 }
 
-SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len)
+SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
 {
-       struct file *file;
+       struct fd f = fdget(fd);
        int error = -EBADF;
 
-       file = fget(fd);
-       if (file) {
-               error = do_fallocate(file, mode, offset, len);
-               fput(file);
+       if (f.file) {
+               error = do_fallocate(f.file, mode, offset, len);
+               fdput(f);
        }
-
        return error;
 }
 
-#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
-asmlinkage long SyS_fallocate(long fd, long mode, loff_t offset, loff_t len)
-{
-       return SYSC_fallocate((int)fd, (int)mode, offset, len);
-}
-SYSCALL_ALIAS(sys_fallocate, SyS_fallocate);
-#endif
-
 /*
  * access() needs to use the real uid/gid, not the effective uid/gid.
  * We do this by temporarily clearing all FS-related capabilities and
@@ -303,6 +303,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
        struct path path;
        struct inode *inode;
        int res;
+       unsigned int lookup_flags = LOOKUP_FOLLOW;
 
        if (mode & ~S_IRWXO)    /* where's F_OK, X_OK, W_OK, R_OK? */
                return -EINVAL;
@@ -316,7 +317,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
 
        if (!issecure(SECURE_NO_SETUID_FIXUP)) {
                /* Clear the capabilities if we switch to a non-root user */
-               if (override_cred->uid)
+               kuid_t root_uid = make_kuid(override_cred->user_ns, 0);
+               if (!uid_eq(override_cred->uid, root_uid))
                        cap_clear(override_cred->cap_effective);
                else
                        override_cred->cap_effective =
@@ -324,8 +326,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
        }
 
        old_cred = override_creds(override_cred);
-
-       res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
+retry:
+       res = user_path_at(dfd, filename, lookup_flags, &path);
        if (res)
                goto out;
 
@@ -360,6 +362,10 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
 
 out_path_release:
        path_put(&path);
+       if (retry_estale(res, lookup_flags)) {
+               lookup_flags |= LOOKUP_REVAL;
+               goto retry;
+       }
 out:
        revert_creds(old_cred);
        put_cred(override_cred);
@@ -375,8 +381,9 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename)
 {
        struct path path;
        int error;
-
-       error = user_path_dir(filename, &path);
+       unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+retry:
+       error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
        if (error)
                goto out;
 
@@ -388,22 +395,25 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename)
 
 dput_and_out:
        path_put(&path);
+       if (retry_estale(error, lookup_flags)) {
+               lookup_flags |= LOOKUP_REVAL;
+               goto retry;
+       }
 out:
        return error;
 }
 
 SYSCALL_DEFINE1(fchdir, unsigned int, fd)
 {
-       struct file *file;
+       struct fd f = fdget_raw(fd);
        struct inode *inode;
-       int error;
+       int error = -EBADF;
 
        error = -EBADF;
-       file = fget(fd);
-       if (!file)
+       if (!f.file)
                goto out;
 
-       inode = file->f_path.dentry->d_inode;
+       inode = file_inode(f.file);
 
        error = -ENOTDIR;
        if (!S_ISDIR(inode->i_mode))
@@ -411,9 +421,9 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
 
        error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
        if (!error)
-               set_fs_pwd(current->fs, &file->f_path);
+               set_fs_pwd(current->fs, &f.file->f_path);
 out_putf:
-       fput(file);
+       fdput(f);
 out:
        return error;
 }
@@ -422,8 +432,9 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
 {
        struct path path;
        int error;
-
-       error = user_path_dir(filename, &path);
+       unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+retry:
+       error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
        if (error)
                goto out;
 
@@ -432,7 +443,7 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
                goto dput_and_out;
 
        error = -EPERM;
-       if (!capable(CAP_SYS_CHROOT))
+       if (!nsown_capable(CAP_SYS_CHROOT))
                goto dput_and_out;
        error = security_path_chroot(&path);
        if (error)
@@ -442,82 +453,69 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
        error = 0;
 dput_and_out:
        path_put(&path);
+       if (retry_estale(error, lookup_flags)) {
+               lookup_flags |= LOOKUP_REVAL;
+               goto retry;
+       }
 out:
        return error;
 }
 
-SYSCALL_DEFINE2(fchmod, unsigned int, fd, mode_t, mode)
+static int chmod_common(struct path *path, umode_t mode)
 {
-       struct inode * inode;
-       struct dentry * dentry;
-       struct file * file;
-       int err = -EBADF;
+       struct inode *inode = path->dentry->d_inode;
        struct iattr newattrs;
+       int error;
 
-       file = fget(fd);
-       if (!file)
-               goto out;
-
-       dentry = file->f_path.dentry;
-       inode = dentry->d_inode;
-
-       audit_inode(NULL, dentry);
-
-       err = mnt_want_write_file(file);
-       if (err)
-               goto out_putf;
+       error = mnt_want_write(path->mnt);
+       if (error)
+               return error;
        mutex_lock(&inode->i_mutex);
-       err = security_path_chmod(dentry, file->f_vfsmnt, mode);
-       if (err)
+       error = security_path_chmod(path, mode);
+       if (error)
                goto out_unlock;
-       if (mode == (mode_t) -1)
-               mode = inode->i_mode;
        newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
        newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-       err = notify_change(dentry, &newattrs);
+       error = notify_change(path->dentry, &newattrs);
 out_unlock:
        mutex_unlock(&inode->i_mutex);
-       mnt_drop_write(file->f_path.mnt);
-out_putf:
-       fput(file);
-out:
+       mnt_drop_write(path->mnt);
+       return error;
+}
+
+SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode)
+{
+       struct file * file;
+       int err = -EBADF;
+
+       file = fget(fd);
+       if (file) {
+               audit_inode(NULL, file->f_path.dentry, 0);
+               err = chmod_common(&file->f_path, mode);
+               fput(file);
+       }
        return err;
 }
 
-SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, mode_t, mode)
+SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, umode_t, mode)
 {
        struct path path;
-       struct inode *inode;
        int error;
-       struct iattr newattrs;
-
-       error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
-       if (error)
-               goto out;
-       inode = path.dentry->d_inode;
-
-       error = mnt_want_write(path.mnt);
-       if (error)
-               goto dput_and_out;
-       mutex_lock(&inode->i_mutex);
-       error = security_path_chmod(path.dentry, path.mnt, mode);
-       if (error)
-               goto out_unlock;
-       if (mode == (mode_t) -1)
-               mode = inode->i_mode;
-       newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
-       newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-       error = notify_change(path.dentry, &newattrs);
-out_unlock:
-       mutex_unlock(&inode->i_mutex);
-       mnt_drop_write(path.mnt);
-dput_and_out:
-       path_put(&path);
-out:
+       unsigned int lookup_flags = LOOKUP_FOLLOW;
+retry:
+       error = user_path_at(dfd, filename, lookup_flags, &path);
+       if (!error) {
+               error = chmod_common(&path, mode);
+               path_put(&path);
+               if (retry_estale(error, lookup_flags)) {
+                       lookup_flags |= LOOKUP_REVAL;
+                       goto retry;
+               }
+       }
        return error;
 }
 
-SYSCALL_DEFINE2(chmod, const char __user *, filename, mode_t, mode)
+SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode)
 {
        return sys_fchmodat(AT_FDCWD, filename, mode);
 }
@@ -527,21 +525,30 @@ static int chown_common(struct path *path, uid_t user, gid_t group)
        struct inode *inode = path->dentry->d_inode;
        int error;
        struct iattr newattrs;
+       kuid_t uid;
+       kgid_t gid;
+
+       uid = make_kuid(current_user_ns(), user);
+       gid = make_kgid(current_user_ns(), group);
 
        newattrs.ia_valid =  ATTR_CTIME;
        if (user != (uid_t) -1) {
+               if (!uid_valid(uid))
+                       return -EINVAL;
                newattrs.ia_valid |= ATTR_UID;
-               newattrs.ia_uid = user;
+               newattrs.ia_uid = uid;
        }
        if (group != (gid_t) -1) {
+               if (!gid_valid(gid))
+                       return -EINVAL;
                newattrs.ia_valid |= ATTR_GID;
-               newattrs.ia_gid = group;
+               newattrs.ia_gid = gid;
        }
        if (!S_ISDIR(inode->i_mode))
                newattrs.ia_valid |=
                        ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
        mutex_lock(&inode->i_mutex);
-       error = security_path_chown(path, user, group);
+       error = security_path_chown(path, uid, gid);
        if (!error)
                error = notify_change(path->dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
@@ -549,25 +556,6 @@ static int chown_common(struct path *path, uid_t user, gid_t group)
        return error;
 }
 
-SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group)
-{
-       struct path path;
-       int error;
-
-       error = user_path(filename, &path);
-       if (error)
-               goto out;
-       error = mnt_want_write(path.mnt);
-       if (error)
-               goto out_release;
-       error = chown_common(&path, user, group);
-       mnt_drop_write(path.mnt);
-out_release:
-       path_put(&path);
-out:
-       return error;
-}
-
 SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,
                gid_t, group, int, flag)
 {
@@ -581,6 +569,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,
        lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
        if (flag & AT_EMPTY_PATH)
                lookup_flags |= LOOKUP_EMPTY;
+retry:
        error = user_path_at(dfd, filename, lookup_flags, &path);
        if (error)
                goto out;
@@ -591,48 +580,41 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,
        mnt_drop_write(path.mnt);
 out_release:
        path_put(&path);
+       if (retry_estale(error, lookup_flags)) {
+               lookup_flags |= LOOKUP_REVAL;
+               goto retry;
+       }
 out:
        return error;
 }
 
-SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group)
+SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group)
 {
-       struct path path;
-       int error;
+       return sys_fchownat(AT_FDCWD, filename, user, group, 0);
+}
 
-       error = user_lpath(filename, &path);
-       if (error)
-               goto out;
-       error = mnt_want_write(path.mnt);
-       if (error)
-               goto out_release;
-       error = chown_common(&path, user, group);
-       mnt_drop_write(path.mnt);
-out_release:
-       path_put(&path);
-out:
-       return error;
+SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group)
+{
+       return sys_fchownat(AT_FDCWD, filename, user, group,
+                           AT_SYMLINK_NOFOLLOW);
 }
 
 SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group)
 {
-       struct file * file;
+       struct fd f = fdget(fd);
        int error = -EBADF;
-       struct dentry * dentry;
 
-       file = fget(fd);
-       if (!file)
+       if (!f.file)
                goto out;
 
-       error = mnt_want_write_file(file);
+       error = mnt_want_write_file(f.file);
        if (error)
                goto out_fput;
-       dentry = file->f_path.dentry;
-       audit_inode(NULL, dentry);
-       error = chown_common(&file->f_path, user, group);
-       mnt_drop_write(file->f_path.mnt);
+       audit_inode(NULL, f.file->f_path.dentry, 0);
+       error = chown_common(&f.file->f_path, user, group);
+       mnt_drop_write_file(f.file);
 out_fput:
-       fput(file);
+       fdput(f);
 out:
        return error;
 }
@@ -646,30 +628,31 @@ out:
 static inline int __get_file_write_access(struct inode *inode,
                                          struct vfsmount *mnt)
 {
-       int error;
-       error = get_write_access(inode);
+       int error = get_write_access(inode);
        if (error)
                return error;
-       /*
-        * Do not take mount writer counts on
-        * special files since no writes to
-        * the mount itself will occur.
-        */
-       if (!special_file(inode->i_mode)) {
-               /*
-                * Balanced in __fput()
-                */
-               error = mnt_want_write(mnt);
-               if (error)
-                       put_write_access(inode);
-       }
+       error = __mnt_want_write(mnt);
+       if (error)
+               put_write_access(inode);
        return error;
 }
 
-static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
-                                       struct file *f,
-                                       int (*open)(struct inode *, struct file *),
-                                       const struct cred *cred)
+int open_check_o_direct(struct file *f)
+{
+       /* NB: we're sure to have correct a_ops only after f_op->open */
+       if (f->f_flags & O_DIRECT) {
+               if (!f->f_mapping->a_ops ||
+                   ((!f->f_mapping->a_ops->direct_IO) &&
+                   (!f->f_mapping->a_ops->get_xip_mem))) {
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+static int do_dentry_open(struct file *f,
+                         int (*open)(struct inode *, struct file *),
+                         const struct cred *cred)
 {
        static const struct file_operations empty_fops = {};
        struct inode *inode;
@@ -681,29 +664,30 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
        if (unlikely(f->f_flags & O_PATH))
                f->f_mode = FMODE_PATH;
 
-       inode = dentry->d_inode;
-       if (f->f_mode & FMODE_WRITE) {
-               error = __get_file_write_access(inode, mnt);
+       path_get(&f->f_path);
+       inode = f->f_inode = f->f_path.dentry->d_inode;
+       if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
+               error = __get_file_write_access(inode, f->f_path.mnt);
                if (error)
                        goto cleanup_file;
-               if (!special_file(inode->i_mode))
-                       file_take_write(f);
+               file_take_write(f);
        }
 
        f->f_mapping = inode->i_mapping;
-       f->f_path.dentry = dentry;
-       f->f_path.mnt = mnt;
-       f->f_pos = 0;
        file_sb_list_add(f, inode->i_sb);
 
        if (unlikely(f->f_mode & FMODE_PATH)) {
                f->f_op = &empty_fops;
-               return f;
+               return 0;
        }
 
        f->f_op = fops_get(inode->i_fop);
 
-       error = security_dentry_open(f, cred);
+       error = security_file_open(f, cred);
+       if (error)
+               goto cleanup_all;
+
+       error = break_lease(inode, f->f_flags);
        if (error)
                goto cleanup_all;
 
@@ -721,22 +705,12 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
 
        file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
 
-       /* NB: we're sure to have correct a_ops only after f_op->open */
-       if (f->f_flags & O_DIRECT) {
-               if (!f->f_mapping->a_ops ||
-                   ((!f->f_mapping->a_ops->direct_IO) &&
-                   (!f->f_mapping->a_ops->get_xip_mem))) {
-                       fput(f);
-                       f = ERR_PTR(-EINVAL);
-               }
-       }
-
-       return f;
+       return 0;
 
 cleanup_all:
        fops_put(f->f_op);
+       file_sb_list_del(f);
        if (f->f_mode & FMODE_WRITE) {
-               put_write_access(inode);
                if (!special_file(inode->i_mode)) {
                        /*
                         * We don't consider this a real
@@ -744,90 +718,63 @@ cleanup_all:
                         * because it all happenend right
                         * here, so just reset the state.
                         */
+                       put_write_access(inode);
                        file_reset_write(f);
-                       mnt_drop_write(mnt);
+                       __mnt_drop_write(f->f_path.mnt);
                }
        }
-       file_sb_list_del(f);
-       f->f_path.dentry = NULL;
-       f->f_path.mnt = NULL;
 cleanup_file:
-       put_filp(f);
-       dput(dentry);
-       mntput(mnt);
-       return ERR_PTR(error);
+       path_put(&f->f_path);
+       f->f_path.mnt = NULL;
+       f->f_path.dentry = NULL;
+       f->f_inode = NULL;
+       return error;
 }
 
 /**
- * lookup_instantiate_filp - instantiates the open intent filp
- * @nd: pointer to nameidata
+ * finish_open - finish opening a file
+ * @od: opaque open data
  * @dentry: pointer to dentry
  * @open: open callback
  *
- * Helper for filesystems that want to use lookup open intents and pass back
- * a fully instantiated struct file to the caller.
- * This function is meant to be called from within a filesystem's
- * lookup method.
- * Beware of calling it for non-regular files! Those ->open methods might block
- * (e.g. in fifo_open), leaving you with parent locked (and in case of fifo,
- * leading to a deadlock, as nobody can open that fifo anymore, because
- * another process to open fifo will block on locked parent when doing lookup).
- * Note that in case of error, nd->intent.open.file is destroyed, but the
- * path information remains valid.
+ * This can be used to finish opening a file passed to i_op->atomic_open().
+ *
  * If the open callback is set to NULL, then the standard f_op->open()
  * filesystem callback is substituted.
  */
-struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
-               int (*open)(struct inode *, struct file *))
+int finish_open(struct file *file, struct dentry *dentry,
+               int (*open)(struct inode *, struct file *),
+               int *opened)
 {
-       const struct cred *cred = current_cred();
+       int error;
+       BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
 
-       if (IS_ERR(nd->intent.open.file))
-               goto out;
-       if (IS_ERR(dentry))
-               goto out_err;
-       nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->path.mnt),
-                                            nd->intent.open.file,
-                                            open, cred);
-out:
-       return nd->intent.open.file;
-out_err:
-       release_open_intent(nd);
-       nd->intent.open.file = (struct file *)dentry;
-       goto out;
+       file->f_path.dentry = dentry;
+       error = do_dentry_open(file, open, current_cred());
+       if (!error)
+               *opened |= FILE_OPENED;
+
+       return error;
 }
-EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
+EXPORT_SYMBOL(finish_open);
 
 /**
- * nameidata_to_filp - convert a nameidata to an open filp.
- * @nd: pointer to nameidata
- * @flags: open flags
+ * finish_no_open - finish ->atomic_open() without opening the file
+ *
+ * @od: opaque open data
+ * @dentry: dentry or NULL (as returned from ->lookup())
  *
- * Note that this function destroys the original nameidata
+ * This can be used to set the result of a successful lookup in ->atomic_open().
+ * The filesystem's atomic_open() method shall return NULL after calling this.
  */
-struct file *nameidata_to_filp(struct nameidata *nd)
+int finish_no_open(struct file *file, struct dentry *dentry)
 {
-       const struct cred *cred = current_cred();
-       struct file *filp;
-
-       /* Pick up the filp from the open intent */
-       filp = nd->intent.open.file;
-       nd->intent.open.file = NULL;
-
-       /* Has the filesystem initialised the file for us? */
-       if (filp->f_path.dentry == NULL) {
-               path_get(&nd->path);
-               filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp,
-                                    NULL, cred);
-       }
-       return filp;
+       file->f_path.dentry = dentry;
+       return 1;
 }
+EXPORT_SYMBOL(finish_no_open);
 
-/*
- * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an
- * error.
- */
-struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
+struct file *dentry_open(const struct path *path, int flags,
                         const struct cred *cred)
 {
        int error;
@@ -836,76 +783,41 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
        validate_creds(cred);
 
        /* We must always pass in a valid mount pointer. */
-       BUG_ON(!mnt);
+       BUG_ON(!path->mnt);
 
-       error = -ENFILE;
        f = get_empty_filp();
-       if (f == NULL) {
-               dput(dentry);
-               mntput(mnt);
-               return ERR_PTR(error);
+       if (!IS_ERR(f)) {
+               f->f_flags = flags;
+               f->f_path = *path;
+               error = do_dentry_open(f, NULL, cred);
+               if (!error) {
+                       /* from now on we need fput() to dispose of f */
+                       error = open_check_o_direct(f);
+                       if (error) {
+                               fput(f);
+                               f = ERR_PTR(error);
+                       }
+               } else { 
+                       put_filp(f);
+                       f = ERR_PTR(error);
+               }
        }
-
-       f->f_flags = flags;
-       return __dentry_open(dentry, mnt, f, NULL, cred);
+       return f;
 }
 EXPORT_SYMBOL(dentry_open);
 
-static void __put_unused_fd(struct files_struct *files, unsigned int fd)
-{
-       struct fdtable *fdt = files_fdtable(files);
-       __FD_CLR(fd, fdt->open_fds);
-       if (fd < files->next_fd)
-               files->next_fd = fd;
-}
-
-void put_unused_fd(unsigned int fd)
-{
-       struct files_struct *files = current->files;
-       spin_lock(&files->file_lock);
-       __put_unused_fd(files, fd);
-       spin_unlock(&files->file_lock);
-}
-
-EXPORT_SYMBOL(put_unused_fd);
-
-/*
- * Install a file pointer in the fd array.
- *
- * The VFS is full of places where we drop the files lock between
- * setting the open_fds bitmap and installing the file in the file
- * array.  At any such point, we are vulnerable to a dup2() race
- * installing a file in the array before us.  We need to detect this and
- * fput() the struct file we are about to overwrite in this case.
- *
- * It should never happen - if we allow dup2() do it, _really_ bad things
- * will follow.
- */
-
-void fd_install(unsigned int fd, struct file *file)
-{
-       struct files_struct *files = current->files;
-       struct fdtable *fdt;
-       spin_lock(&files->file_lock);
-       fdt = files_fdtable(files);
-       BUG_ON(fdt->fd[fd] != NULL);
-       rcu_assign_pointer(fdt->fd[fd], file);
-       spin_unlock(&files->file_lock);
-}
-
-EXPORT_SYMBOL(fd_install);
-
-static inline int build_open_flags(int flags, int mode, struct open_flags *op)
+static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
 {
        int lookup_flags = 0;
        int acc_mode;
 
-       if (!(flags & O_CREAT))
-               mode = 0;
-       op->mode = mode;
+       if (flags & O_CREAT)
+               op->mode = (mode & S_IALLUGO) | S_IFREG;
+       else
+               op->mode = 0;
 
        /* Must never be set by userspace */
-       flags &= ~FMODE_NONOTIFY;
+       flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC;
 
        /*
         * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
@@ -956,9 +868,9 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op)
 }
 
 /**
- * filp_open - open file and return file pointer
+ * file_open_name - open file and return file pointer
  *
- * @filename:  path to open
+ * @name:      struct filename containing path to open
  * @flags:     open flags as per the open(2) second argument
  * @mode:      mode for the new file if O_CREAT is set, else ignored
  *
@@ -966,11 +878,28 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op)
  * have to.  But in generally you should not do this, so please move
  * along, nothing to see here..
  */
-struct file *filp_open(const char *filename, int flags, int mode)
+struct file *file_open_name(struct filename *name, int flags, umode_t mode)
 {
        struct open_flags op;
        int lookup = build_open_flags(flags, mode, &op);
-       return do_filp_open(AT_FDCWD, filename, &op, lookup);
+       return do_filp_open(AT_FDCWD, name, &op, lookup);
+}
+
+/**
+ * filp_open - open file and return file pointer
+ *
+ * @filename:  path to open
+ * @flags:     open flags as per the open(2) second argument
+ * @mode:      mode for the new file if O_CREAT is set, else ignored
+ *
+ * This is the helper to open a file from kernelspace if you really
+ * have to.  But in generally you should not do this, so please move
+ * along, nothing to see here..
+ */
+struct file *filp_open(const char *filename, int flags, umode_t mode)
+{
+       struct filename name = {.name = filename};
+       return file_open_name(&name, flags, mode);
 }
 EXPORT_SYMBOL(filp_open);
 
@@ -988,11 +917,11 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
 }
 EXPORT_SYMBOL(file_open_root);
 
-long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
+long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
 {
        struct open_flags op;
        int lookup = build_open_flags(flags, mode, &op);
-       char *tmp = getname(filename);
+       struct filename *tmp = getname(filename);
        int fd = PTR_ERR(tmp);
 
        if (!IS_ERR(tmp)) {
@@ -1012,31 +941,21 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
        return fd;
 }
 
-SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
+SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
 {
-       long ret;
-
        if (force_o_largefile())
                flags |= O_LARGEFILE;
 
-       ret = do_sys_open(AT_FDCWD, filename, flags, mode);
-       /* avoid REGPARM breakage on x86: */
-       asmlinkage_protect(3, ret, filename, flags, mode);
-       return ret;
+       return do_sys_open(AT_FDCWD, filename, flags, mode);
 }
 
 SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
-               int, mode)
+               umode_t, mode)
 {
-       long ret;
-
        if (force_o_largefile())
                flags |= O_LARGEFILE;
 
-       ret = do_sys_open(dfd, filename, flags, mode);
-       /* avoid REGPARM breakage on x86: */
-       asmlinkage_protect(4, ret, dfd, filename, flags, mode);
-       return ret;
+       return do_sys_open(dfd, filename, flags, mode);
 }
 
 #ifndef __alpha__
@@ -1045,7 +964,7 @@ SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
  * For backward compatibility?  Maybe this should be moved
  * into arch/i386 instead?
  */
-SYSCALL_DEFINE2(creat, const char __user *, pathname, int, mode)
+SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode)
 {
        return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
 }
@@ -1085,23 +1004,7 @@ EXPORT_SYMBOL(filp_close);
  */
 SYSCALL_DEFINE1(close, unsigned int, fd)
 {
-       struct file * filp;
-       struct files_struct *files = current->files;
-       struct fdtable *fdt;
-       int retval;
-
-       spin_lock(&files->file_lock);
-       fdt = files_fdtable(files);
-       if (fd >= fdt->max_fds)
-               goto out_unlock;
-       filp = fdt->fd[fd];
-       if (!filp)
-               goto out_unlock;
-       rcu_assign_pointer(fdt->fd[fd], NULL);
-       FD_CLR(fd, fdt->close_on_exec);
-       __put_unused_fd(files, fd);
-       spin_unlock(&files->file_lock);
-       retval = filp_close(filp, files);
+       int retval = __close_fd(current->files, fd);
 
        /* can't restart close syscall because file table entry was cleared */
        if (unlikely(retval == -ERESTARTSYS ||
@@ -1111,10 +1014,6 @@ SYSCALL_DEFINE1(close, unsigned int, fd)
                retval = -EINTR;
 
        return retval;
-
-out_unlock:
-       spin_unlock(&files->file_lock);
-       return -EBADF;
 }
 EXPORT_SYMBOL(sys_close);