tools headers UAPI: Sync openat2.h with the kernel sources
[linux-2.6-microblaze.git] / fs / posix_acl.c
index 95882b3..f3309a7 100644 (file)
@@ -345,10 +345,13 @@ EXPORT_SYMBOL(posix_acl_from_mode);
  * by the acl. Returns -E... otherwise.
  */
 int
-posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
+posix_acl_permission(struct user_namespace *mnt_userns, struct inode *inode,
+                    const struct posix_acl *acl, int want)
 {
        const struct posix_acl_entry *pa, *pe, *mask_obj;
        int found = 0;
+       kuid_t uid;
+       kgid_t gid;
 
        want &= MAY_READ | MAY_WRITE | MAY_EXEC;
 
@@ -356,22 +359,26 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
                 switch(pa->e_tag) {
                         case ACL_USER_OBJ:
                                /* (May have been checked already) */
-                               if (uid_eq(inode->i_uid, current_fsuid()))
+                               uid = i_uid_into_mnt(mnt_userns, inode);
+                               if (uid_eq(uid, current_fsuid()))
                                         goto check_perm;
                                 break;
                         case ACL_USER:
-                               if (uid_eq(pa->e_uid, current_fsuid()))
+                               uid = kuid_into_mnt(mnt_userns, pa->e_uid);
+                               if (uid_eq(uid, current_fsuid()))
                                         goto mask;
                                break;
                         case ACL_GROUP_OBJ:
-                                if (in_group_p(inode->i_gid)) {
+                               gid = i_gid_into_mnt(mnt_userns, inode);
+                               if (in_group_p(gid)) {
                                        found = 1;
                                        if ((pa->e_perm & want) == want)
                                                goto mask;
                                 }
                                break;
                         case ACL_GROUP:
-                               if (in_group_p(pa->e_gid)) {
+                               gid = kgid_into_mnt(mnt_userns, pa->e_gid);
+                               if (in_group_p(gid)) {
                                        found = 1;
                                        if ((pa->e_perm & want) == want)
                                                goto mask;
@@ -551,8 +558,22 @@ __posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
 }
 EXPORT_SYMBOL(__posix_acl_chmod);
 
+/**
+ * posix_acl_chmod - chmod a posix acl
+ *
+ * @mnt_userns:        user namespace of the mount @inode was found from
+ * @inode:     inode to check permissions on
+ * @mode:      the new mode of @inode
+ *
+ * If the inode has been found through an idmapped mount the user namespace of
+ * the vfsmount must be passed through @mnt_userns. This function will then
+ * take care to map the inode according to @mnt_userns before checking
+ * permissions. On non-idmapped mounts or if permission checking is to be
+ * performed on the raw inode simply passs init_user_ns.
+ */
 int
-posix_acl_chmod(struct inode *inode, umode_t mode)
+ posix_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode,
+                   umode_t mode)
 {
        struct posix_acl *acl;
        int ret = 0;
@@ -572,7 +593,7 @@ posix_acl_chmod(struct inode *inode, umode_t mode)
        ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode);
        if (ret)
                return ret;
-       ret = inode->i_op->set_acl(inode, acl, ACL_TYPE_ACCESS);
+       ret = inode->i_op->set_acl(mnt_userns, inode, acl, ACL_TYPE_ACCESS);
        posix_acl_release(acl);
        return ret;
 }
@@ -631,9 +652,10 @@ EXPORT_SYMBOL_GPL(posix_acl_create);
 
 /**
  * posix_acl_update_mode  -  update mode in set_acl
- * @inode: target inode
- * @mode_p: mode (pointer) for update
- * @acl: acl pointer
+ * @mnt_userns:        user namespace of the mount @inode was found from
+ * @inode:     target inode
+ * @mode_p:    mode (pointer) for update
+ * @acl:       acl pointer
  *
  * Update the file mode when setting an ACL: compute the new file permission
  * bits based on the ACL.  In addition, if the ACL is equivalent to the new
@@ -642,9 +664,16 @@ EXPORT_SYMBOL_GPL(posix_acl_create);
  * As with chmod, clear the setgid bit if the caller is not in the owning group
  * or capable of CAP_FSETID (see inode_change_ok).
  *
+ * If the inode has been found through an idmapped mount the user namespace of
+ * the vfsmount must be passed through @mnt_userns. This function will then
+ * take care to map the inode according to @mnt_userns before checking
+ * permissions. On non-idmapped mounts or if permission checking is to be
+ * performed on the raw inode simply passs init_user_ns.
+ *
  * Called from set_acl inode operations.
  */
-int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
+int posix_acl_update_mode(struct user_namespace *mnt_userns,
+                         struct inode *inode, umode_t *mode_p,
                          struct posix_acl **acl)
 {
        umode_t mode = inode->i_mode;
@@ -655,8 +684,8 @@ int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
                return error;
        if (error == 0)
                *acl = NULL;
-       if (!in_group_p(inode->i_gid) &&
-           !capable_wrt_inode_uidgid(inode, CAP_FSETID))
+       if (!in_group_p(i_gid_into_mnt(mnt_userns, inode)) &&
+           !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
                mode &= ~S_ISGID;
        *mode_p = mode;
        return 0;
@@ -668,7 +697,8 @@ EXPORT_SYMBOL(posix_acl_update_mode);
  */
 static void posix_acl_fix_xattr_userns(
        struct user_namespace *to, struct user_namespace *from,
-       void *value, size_t size)
+       struct user_namespace *mnt_userns,
+       void *value, size_t size, bool from_user)
 {
        struct posix_acl_xattr_header *header = value;
        struct posix_acl_xattr_entry *entry = (void *)(header + 1), *end;
@@ -693,10 +723,18 @@ static void posix_acl_fix_xattr_userns(
                switch(le16_to_cpu(entry->e_tag)) {
                case ACL_USER:
                        uid = make_kuid(from, le32_to_cpu(entry->e_id));
+                       if (from_user)
+                               uid = kuid_from_mnt(mnt_userns, uid);
+                       else
+                               uid = kuid_into_mnt(mnt_userns, uid);
                        entry->e_id = cpu_to_le32(from_kuid(to, uid));
                        break;
                case ACL_GROUP:
                        gid = make_kgid(from, le32_to_cpu(entry->e_id));
+                       if (from_user)
+                               gid = kgid_from_mnt(mnt_userns, gid);
+                       else
+                               gid = kgid_into_mnt(mnt_userns, gid);
                        entry->e_id = cpu_to_le32(from_kgid(to, gid));
                        break;
                default:
@@ -705,20 +743,24 @@ static void posix_acl_fix_xattr_userns(
        }
 }
 
-void posix_acl_fix_xattr_from_user(void *value, size_t size)
+void posix_acl_fix_xattr_from_user(struct user_namespace *mnt_userns,
+                                  void *value, size_t size)
 {
        struct user_namespace *user_ns = current_user_ns();
-       if (user_ns == &init_user_ns)
+       if ((user_ns == &init_user_ns) && (mnt_userns == &init_user_ns))
                return;
-       posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
+       posix_acl_fix_xattr_userns(&init_user_ns, user_ns, mnt_userns, value,
+                                  size, true);
 }
 
-void posix_acl_fix_xattr_to_user(void *value, size_t size)
+void posix_acl_fix_xattr_to_user(struct user_namespace *mnt_userns,
+                                void *value, size_t size)
 {
        struct user_namespace *user_ns = current_user_ns();
-       if (user_ns == &init_user_ns)
+       if ((user_ns == &init_user_ns) && (mnt_userns == &init_user_ns))
                return;
-       posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
+       posix_acl_fix_xattr_userns(user_ns, &init_user_ns, mnt_userns, value,
+                                  size, false);
 }
 
 /*
@@ -858,7 +900,8 @@ posix_acl_xattr_get(const struct xattr_handler *handler,
 }
 
 int
-set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)
+set_posix_acl(struct user_namespace *mnt_userns, struct inode *inode,
+             int type, struct posix_acl *acl)
 {
        if (!IS_POSIXACL(inode))
                return -EOPNOTSUPP;
@@ -867,7 +910,7 @@ set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)
 
        if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
                return acl ? -EACCES : 0;
-       if (!inode_owner_or_capable(inode))
+       if (!inode_owner_or_capable(mnt_userns, inode))
                return -EPERM;
 
        if (acl) {
@@ -875,15 +918,16 @@ set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)
                if (ret)
                        return ret;
        }
-       return inode->i_op->set_acl(inode, acl, type);
+       return inode->i_op->set_acl(mnt_userns, inode, acl, type);
 }
 EXPORT_SYMBOL(set_posix_acl);
 
 static int
 posix_acl_xattr_set(const struct xattr_handler *handler,
-                   struct dentry *unused, struct inode *inode,
-                   const char *name, const void *value,
-                   size_t size, int flags)
+                          struct user_namespace *mnt_userns,
+                          struct dentry *unused, struct inode *inode,
+                          const char *name, const void *value, size_t size,
+                          int flags)
 {
        struct posix_acl *acl = NULL;
        int ret;
@@ -893,7 +937,7 @@ posix_acl_xattr_set(const struct xattr_handler *handler,
                if (IS_ERR(acl))
                        return PTR_ERR(acl);
        }
-       ret = set_posix_acl(inode, handler->flags, acl);
+       ret = set_posix_acl(mnt_userns, inode, handler->flags, acl);
        posix_acl_release(acl);
        return ret;
 }
@@ -922,12 +966,13 @@ const struct xattr_handler posix_acl_default_xattr_handler = {
 };
 EXPORT_SYMBOL_GPL(posix_acl_default_xattr_handler);
 
-int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+int simple_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+                  struct posix_acl *acl, int type)
 {
        int error;
 
        if (type == ACL_TYPE_ACCESS) {
-               error = posix_acl_update_mode(inode,
+               error = posix_acl_update_mode(mnt_userns, inode,
                                &inode->i_mode, &acl);
                if (error)
                        return error;