Merge tag 'audit-pr-20180403' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoor...
[linux-2.6-microblaze.git] / fs / namei.c
index 5f8e8e2..a66ed5a 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/bitops.h>
 #include <linux/init_task.h>
 #include <linux/uaccess.h>
+#include <linux/build_bug.h>
 
 #include "internal.h"
 #include "mount.h"
@@ -130,6 +131,7 @@ getname_flags(const char __user *filename, int flags, int *empty)
        struct filename *result;
        char *kname;
        int len;
+       BUILD_BUG_ON(offsetof(struct filename, iname) % sizeof(long) != 0);
 
        result = audit_reusename(filename);
        if (result)
@@ -390,50 +392,6 @@ static inline int do_inode_permission(struct inode *inode, int mask)
        return generic_permission(inode, mask);
 }
 
-/**
- * __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)
- *
- * Check for read/write/execute permissions on an inode.
- *
- * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
- *
- * This does not check for a read-only file system.  You probably want
- * inode_permission().
- */
-int __inode_permission(struct inode *inode, int mask)
-{
-       int retval;
-
-       if (unlikely(mask & MAY_WRITE)) {
-               /*
-                * Nobody gets write access to an immutable file.
-                */
-               if (IS_IMMUTABLE(inode))
-                       return -EPERM;
-
-               /*
-                * Updating mtime will likely cause i_uid and i_gid to be
-                * written back improperly if their true value is unknown
-                * to the vfs.
-                */
-               if (HAS_UNMAPPED_ID(inode))
-                       return -EACCES;
-       }
-
-       retval = do_inode_permission(inode, mask);
-       if (retval)
-               return retval;
-
-       retval = devcgroup_inode_permission(inode, mask);
-       if (retval)
-               return retval;
-
-       return security_inode_permission(inode, mask);
-}
-EXPORT_SYMBOL(__inode_permission);
-
 /**
  * sb_permission - Check superblock-level permissions
  * @sb: Superblock of inode to check permission on
@@ -472,7 +430,32 @@ int inode_permission(struct inode *inode, int mask)
        retval = sb_permission(inode->i_sb, inode, mask);
        if (retval)
                return retval;
-       return __inode_permission(inode, mask);
+
+       if (unlikely(mask & MAY_WRITE)) {
+               /*
+                * Nobody gets write access to an immutable file.
+                */
+               if (IS_IMMUTABLE(inode))
+                       return -EPERM;
+
+               /*
+                * Updating mtime will likely cause i_uid and i_gid to be
+                * written back improperly if their true value is unknown
+                * to the vfs.
+                */
+               if (HAS_UNMAPPED_ID(inode))
+                       return -EACCES;
+       }
+
+       retval = do_inode_permission(inode, mask);
+       if (retval)
+               return retval;
+
+       retval = devcgroup_inode_permission(inode, mask);
+       if (retval)
+               return retval;
+
+       return security_inode_permission(inode, mask);
 }
 EXPORT_SYMBOL(inode_permission);
 
@@ -578,9 +561,10 @@ static int __nd_alloc_stack(struct nameidata *nd)
 static bool path_connected(const struct path *path)
 {
        struct vfsmount *mnt = path->mnt;
+       struct super_block *sb = mnt->mnt_sb;
 
-       /* Only bind mounts can have disconnected paths */
-       if (mnt->mnt_root == mnt->mnt_sb->s_root)
+       /* Bind mounts and multi-root filesystems can have disconnected paths */
+       if (!(sb->s_iflags & SB_I_MULTIROOT) && (mnt->mnt_root == sb->s_root))
                return true;
 
        return is_subdir(path->dentry, mnt->mnt_root);
@@ -1134,9 +1118,6 @@ static int follow_automount(struct path *path, struct nameidata *nd,
            path->dentry->d_inode)
                return -EISDIR;
 
-       if (path->dentry->d_sb->s_user_ns != &init_user_ns)
-               return -EACCES;
-
        nd->total_link_count++;
        if (nd->total_link_count >= 40)
                return -ELOOP;
@@ -1496,43 +1477,36 @@ static struct dentry *lookup_dcache(const struct qstr *name,
 }
 
 /*
- * Call i_op->lookup on the dentry.  The dentry must be negative and
- * unhashed.
- *
- * dir->d_inode->i_mutex must be held
+ * Parent directory has inode locked exclusive.  This is one
+ * and only case when ->lookup() gets called on non in-lookup
+ * dentries - as the matter of fact, this only gets called
+ * when directory is guaranteed to have no in-lookup children
+ * at all.
  */
-static struct dentry *lookup_real(struct inode *dir, struct dentry *dentry,
-                                 unsigned int flags)
-{
-       struct dentry *old;
-
-       /* Don't create child dentry for a dead directory. */
-       if (unlikely(IS_DEADDIR(dir))) {
-               dput(dentry);
-               return ERR_PTR(-ENOENT);
-       }
-
-       old = dir->i_op->lookup(dir, dentry, flags);
-       if (unlikely(old)) {
-               dput(dentry);
-               dentry = old;
-       }
-       return dentry;
-}
-
 static struct dentry *__lookup_hash(const struct qstr *name,
                struct dentry *base, unsigned int flags)
 {
        struct dentry *dentry = lookup_dcache(name, base, flags);
+       struct dentry *old;
+       struct inode *dir = base->d_inode;
 
        if (dentry)
                return dentry;
 
+       /* Don't create child dentry for a dead directory. */
+       if (unlikely(IS_DEADDIR(dir)))
+               return ERR_PTR(-ENOENT);
+
        dentry = d_alloc(base, name);
        if (unlikely(!dentry))
                return ERR_PTR(-ENOMEM);
 
-       return lookup_real(base->d_inode, dentry, flags);
+       old = dir->i_op->lookup(dir, dentry, flags);
+       if (unlikely(old)) {
+               dput(dentry);
+               dentry = old;
+       }
+       return dentry;
 }
 
 static int lookup_fast(struct nameidata *nd,
@@ -2899,6 +2873,27 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 }
 EXPORT_SYMBOL(vfs_create);
 
+int vfs_mkobj(struct dentry *dentry, umode_t mode,
+               int (*f)(struct dentry *, umode_t, void *),
+               void *arg)
+{
+       struct inode *dir = dentry->d_parent->d_inode;
+       int error = may_create(dir, dentry);
+       if (error)
+               return error;
+
+       mode &= S_IALLUGO;
+       mode |= S_IFREG;
+       error = security_inode_create(dir, dentry, mode);
+       if (error)
+               return error;
+       error = f(dentry, mode, arg);
+       if (!error)
+               fsnotify_create(dir, dentry);
+       return error;
+}
+EXPORT_SYMBOL(vfs_mkobj);
+
 bool may_open_dev(const struct path *path)
 {
        return !(path->mnt->mnt_flags & MNT_NODEV) &&
@@ -3382,9 +3377,7 @@ finish_open_created:
                goto out;
        *opened |= FILE_OPENED;
 opened:
-       error = open_check_o_direct(file);
-       if (!error)
-               error = ima_file_check(file, op->acc_mode, *opened);
+       error = ima_file_check(file, op->acc_mode, *opened);
        if (!error && will_truncate)
                error = handle_truncate(file);
 out:
@@ -3464,9 +3457,6 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
        error = finish_open(file, child, NULL, opened);
        if (error)
                goto out2;
-       error = open_check_o_direct(file);
-       if (error)
-               fput(file);
 out2:
        mnt_drop_write(path.mnt);
 out:
@@ -3730,8 +3720,8 @@ static int may_mknod(umode_t mode)
        }
 }
 
-SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
-               unsigned, dev)
+long do_mknodat(int dfd, const char __user *filename, umode_t mode,
+               unsigned int dev)
 {
        struct dentry *dentry;
        struct path path;
@@ -3774,9 +3764,15 @@ out:
        return error;
 }
 
+SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
+               unsigned int, dev)
+{
+       return do_mknodat(dfd, filename, mode, dev);
+}
+
 SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev)
 {
-       return sys_mknodat(AT_FDCWD, filename, mode, dev);
+       return do_mknodat(AT_FDCWD, filename, mode, dev);
 }
 
 int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
@@ -3805,7 +3801,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 }
 EXPORT_SYMBOL(vfs_mkdir);
 
-SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
+long do_mkdirat(int dfd, const char __user *pathname, umode_t mode)
 {
        struct dentry *dentry;
        struct path path;
@@ -3830,9 +3826,14 @@ retry:
        return error;
 }
 
+SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
+{
+       return do_mkdirat(dfd, pathname, mode);
+}
+
 SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
 {
-       return sys_mkdirat(AT_FDCWD, pathname, mode);
+       return do_mkdirat(AT_FDCWD, pathname, mode);
 }
 
 int vfs_rmdir(struct inode *dir, struct dentry *dentry)
@@ -3874,7 +3875,7 @@ out:
 }
 EXPORT_SYMBOL(vfs_rmdir);
 
-static long do_rmdir(int dfd, const char __user *pathname)
+long do_rmdir(int dfd, const char __user *pathname)
 {
        int error = 0;
        struct filename *name;
@@ -4110,8 +4111,8 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
 }
 EXPORT_SYMBOL(vfs_symlink);
 
-SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
-               int, newdfd, const char __user *, newname)
+long do_symlinkat(const char __user *oldname, int newdfd,
+                 const char __user *newname)
 {
        int error;
        struct filename *from;
@@ -4141,9 +4142,15 @@ out_putname:
        return error;
 }
 
+SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
+               int, newdfd, const char __user *, newname)
+{
+       return do_symlinkat(oldname, newdfd, newname);
+}
+
 SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname)
 {
-       return sys_symlinkat(oldname, AT_FDCWD, newname);
+       return do_symlinkat(oldname, AT_FDCWD, newname);
 }
 
 /**
@@ -4235,8 +4242,8 @@ EXPORT_SYMBOL(vfs_link);
  * with linux 2.0, and to avoid hard-linking to directories
  * and other special files.  --ADM
  */
-SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
-               int, newdfd, const char __user *, newname, int, flags)
+int do_linkat(int olddfd, const char __user *oldname, int newdfd,
+             const char __user *newname, int flags)
 {
        struct dentry *new_dentry;
        struct path old_path, new_path;
@@ -4300,9 +4307,15 @@ out:
        return error;
 }
 
+SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
+               int, newdfd, const char __user *, newname, int, flags)
+{
+       return do_linkat(olddfd, oldname, newdfd, newname, flags);
+}
+
 SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname)
 {
-       return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
+       return do_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
 
 /**
@@ -4480,8 +4493,8 @@ out:
 }
 EXPORT_SYMBOL(vfs_rename);
 
-SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
-               int, newdfd, const char __user *, newname, unsigned int, flags)
+static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
+                       const char __user *newname, unsigned int flags)
 {
        struct dentry *old_dentry, *new_dentry;
        struct dentry *trap;
@@ -4623,15 +4636,21 @@ exit:
        return error;
 }
 
+SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
+               int, newdfd, const char __user *, newname, unsigned int, flags)
+{
+       return do_renameat2(olddfd, oldname, newdfd, newname, flags);
+}
+
 SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
                int, newdfd, const char __user *, newname)
 {
-       return sys_renameat2(olddfd, oldname, newdfd, newname, 0);
+       return do_renameat2(olddfd, oldname, newdfd, newname, 0);
 }
 
 SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
 {
-       return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
+       return do_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
 
 int vfs_whiteout(struct inode *dir, struct dentry *dentry)