mm: memcg: group swapped-out statistics counter logically
[platform/adaptation/renesas_rcar/renesas_kernel.git] / fs / namei.c
index c427919..c651f02 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/init.h>
 #include <linux/export.h>
+#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/namei.h>
  * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
  * PATH_MAX includes the nul terminator --RR.
  */
-static int do_getname(const char __user *filename, char *page)
-{
-       int retval;
-       unsigned long len = PATH_MAX;
-
-       if (!segment_eq(get_fs(), KERNEL_DS)) {
-               if ((unsigned long) filename >= TASK_SIZE)
-                       return -EFAULT;
-               if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
-                       len = TASK_SIZE - (unsigned long) filename;
-       }
-
-       retval = strncpy_from_user(page, filename, len);
-       if (retval > 0) {
-               if (retval < len)
-                       return 0;
-               return -ENAMETOOLONG;
-       } else if (!retval)
-               retval = -ENOENT;
-       return retval;
-}
-
 static char *getname_flags(const char __user *filename, int flags, int *empty)
 {
-       char *result = __getname();
-       int retval;
+       char *result = __getname(), *err;
+       int len;
 
-       if (!result)
+       if (unlikely(!result))
                return ERR_PTR(-ENOMEM);
 
-       retval = do_getname(filename, result);
-       if (retval < 0) {
-               if (retval == -ENOENT && empty)
+       len = strncpy_from_user(result, filename, PATH_MAX);
+       err = ERR_PTR(len);
+       if (unlikely(len < 0))
+               goto error;
+
+       /* The empty path is special. */
+       if (unlikely(!len)) {
+               if (empty)
                        *empty = 1;
-               if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) {
-                       __putname(result);
-                       return ERR_PTR(retval);
-               }
+               err = ERR_PTR(-ENOENT);
+               if (!(flags & LOOKUP_EMPTY))
+                       goto error;
+       }
+
+       err = ERR_PTR(-ENAMETOOLONG);
+       if (likely(len < PATH_MAX)) {
+               audit_getname(result);
+               return result;
        }
-       audit_getname(result);
-       return result;
+
+error:
+       __putname(result);
+       return err;
 }
 
 char *getname(const char __user * filename)
@@ -228,10 +219,7 @@ static int acl_permission_check(struct inode *inode, int mask)
 {
        unsigned int mode = inode->i_mode;
 
-       if (current_user_ns() != inode_userns(inode))
-               goto other_perms;
-
-       if (likely(current_fsuid() == inode->i_uid))
+       if (likely(uid_eq(current_fsuid(), inode->i_uid)))
                mode >>= 6;
        else {
                if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
@@ -244,7 +232,6 @@ static int acl_permission_check(struct inode *inode, int mask)
                        mode >>= 3;
        }
 
-other_perms:
        /*
         * If the DACs are ok we don't need any capability check.
         */
@@ -280,10 +267,10 @@ int generic_permission(struct inode *inode, int mask)
 
        if (S_ISDIR(inode->i_mode)) {
                /* DACs are overridable for directories */
-               if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE))
+               if (inode_capable(inode, CAP_DAC_OVERRIDE))
                        return 0;
                if (!(mask & MAY_WRITE))
-                       if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH))
+                       if (inode_capable(inode, CAP_DAC_READ_SEARCH))
                                return 0;
                return -EACCES;
        }
@@ -293,7 +280,7 @@ int generic_permission(struct inode *inode, int mask)
         * at least one exec bit set.
         */
        if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
-               if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE))
+               if (inode_capable(inode, CAP_DAC_OVERRIDE))
                        return 0;
 
        /*
@@ -301,7 +288,7 @@ int generic_permission(struct inode *inode, int mask)
         */
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
        if (mask == MAY_READ)
-               if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH))
+               if (inode_capable(inode, CAP_DAC_READ_SEARCH))
                        return 0;
 
        return -EACCES;
@@ -1154,12 +1141,25 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
         */
        if (nd->flags & LOOKUP_RCU) {
                unsigned seq;
-               *inode = nd->inode;
-               dentry = __d_lookup_rcu(parent, name, &seq, inode);
+               dentry = __d_lookup_rcu(parent, name, &seq, nd->inode);
                if (!dentry)
                        goto unlazy;
 
-               /* Memory barrier in read_seqcount_begin of child is enough */
+               /*
+                * This sequence count validates that the inode matches
+                * the dentry name information from lookup.
+                */
+               *inode = dentry->d_inode;
+               if (read_seqcount_retry(&dentry->d_seq, seq))
+                       return -ECHILD;
+
+               /*
+                * This sequence count validates that the parent had no
+                * changes while we did the lookup of the dentry above.
+                *
+                * The memory barrier in read_seqcount_begin of child is
+                *  enough, we can use __read_seqcount_retry here.
+                */
                if (__read_seqcount_retry(&parent->d_seq, nd->seq))
                        return -ECHILD;
                nd->seq = seq;
@@ -1452,7 +1452,8 @@ EXPORT_SYMBOL(full_name_hash);
  */
 static inline unsigned long hash_name(const char *name, unsigned int *hashp)
 {
-       unsigned long a, mask, hash, len;
+       unsigned long a, b, adata, bdata, mask, hash, len;
+       const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
 
        hash = a = 0;
        len = -sizeof(unsigned long);
@@ -1460,17 +1461,18 @@ static inline unsigned long hash_name(const char *name, unsigned int *hashp)
                hash = (hash + a) * 9;
                len += sizeof(unsigned long);
                a = load_unaligned_zeropad(name+len);
-               /* Do we have any NUL or '/' bytes in this word? */
-               mask = has_zero(a) | has_zero(a ^ REPEAT_BYTE('/'));
-       } while (!mask);
-
-       /* The mask *below* the first high bit set */
-       mask = (mask - 1) & ~mask;
-       mask >>= 7;
-       hash += a & mask;
+               b = a ^ REPEAT_BYTE('/');
+       } while (!(has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants)));
+
+       adata = prep_zero_mask(a, adata, &constants);
+       bdata = prep_zero_mask(b, bdata, &constants);
+
+       mask = create_zero_mask(adata | bdata);
+
+       hash += a & zero_bytemask(mask);
        *hashp = fold_hash(hash);
 
-       return len + count_masked_bytes(mask);
+       return len + find_zero(mask);
 }
 
 #else
@@ -1931,19 +1933,15 @@ 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();
+       kuid_t fsuid = current_fsuid();
 
        if (!(dir->i_mode & S_ISVTX))
                return 0;
-       if (current_user_ns() != inode_userns(inode))
-               goto other_userns;
-       if (inode->i_uid == fsuid)
+       if (uid_eq(inode->i_uid, fsuid))
                return 0;
-       if (dir->i_uid == fsuid)
+       if (uid_eq(dir->i_uid, fsuid))
                return 0;
-
-other_userns:
-       return !ns_capable(inode_userns(inode), CAP_FOWNER);
+       return !inode_capable(inode, CAP_FOWNER);
 }
 
 /*
@@ -2531,8 +2529,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
        if (error)
                return error;
 
-       if ((S_ISCHR(mode) || S_ISBLK(mode)) &&
-           !ns_capable(inode_userns(dir), CAP_MKNOD))
+       if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
                return -EPERM;
 
        if (!dir->i_op->mknod)