Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * linux/fs/ext2/acl.c
0004  *
0005  * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
0006  */
0007 
0008 #include <linux/init.h>
0009 #include <linux/sched.h>
0010 #include <linux/slab.h>
0011 #include <linux/fs.h>
0012 #include "ext2.h"
0013 #include "xattr.h"
0014 #include "acl.h"
0015 
0016 /*
0017  * Convert from filesystem to in-memory representation.
0018  */
0019 static struct posix_acl *
0020 ext2_acl_from_disk(const void *value, size_t size)
0021 {
0022     const char *end = (char *)value + size;
0023     int n, count;
0024     struct posix_acl *acl;
0025 
0026     if (!value)
0027         return NULL;
0028     if (size < sizeof(ext2_acl_header))
0029          return ERR_PTR(-EINVAL);
0030     if (((ext2_acl_header *)value)->a_version !=
0031         cpu_to_le32(EXT2_ACL_VERSION))
0032         return ERR_PTR(-EINVAL);
0033     value = (char *)value + sizeof(ext2_acl_header);
0034     count = ext2_acl_count(size);
0035     if (count < 0)
0036         return ERR_PTR(-EINVAL);
0037     if (count == 0)
0038         return NULL;
0039     acl = posix_acl_alloc(count, GFP_KERNEL);
0040     if (!acl)
0041         return ERR_PTR(-ENOMEM);
0042     for (n=0; n < count; n++) {
0043         ext2_acl_entry *entry =
0044             (ext2_acl_entry *)value;
0045         if ((char *)value + sizeof(ext2_acl_entry_short) > end)
0046             goto fail;
0047         acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
0048         acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
0049         switch(acl->a_entries[n].e_tag) {
0050             case ACL_USER_OBJ:
0051             case ACL_GROUP_OBJ:
0052             case ACL_MASK:
0053             case ACL_OTHER:
0054                 value = (char *)value +
0055                     sizeof(ext2_acl_entry_short);
0056                 break;
0057 
0058             case ACL_USER:
0059                 value = (char *)value + sizeof(ext2_acl_entry);
0060                 if ((char *)value > end)
0061                     goto fail;
0062                 acl->a_entries[n].e_uid =
0063                     make_kuid(&init_user_ns,
0064                           le32_to_cpu(entry->e_id));
0065                 break;
0066             case ACL_GROUP:
0067                 value = (char *)value + sizeof(ext2_acl_entry);
0068                 if ((char *)value > end)
0069                     goto fail;
0070                 acl->a_entries[n].e_gid =
0071                     make_kgid(&init_user_ns,
0072                           le32_to_cpu(entry->e_id));
0073                 break;
0074 
0075             default:
0076                 goto fail;
0077         }
0078     }
0079     if (value != end)
0080         goto fail;
0081     return acl;
0082 
0083 fail:
0084     posix_acl_release(acl);
0085     return ERR_PTR(-EINVAL);
0086 }
0087 
0088 /*
0089  * Convert from in-memory to filesystem representation.
0090  */
0091 static void *
0092 ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
0093 {
0094     ext2_acl_header *ext_acl;
0095     char *e;
0096     size_t n;
0097 
0098     *size = ext2_acl_size(acl->a_count);
0099     ext_acl = kmalloc(sizeof(ext2_acl_header) + acl->a_count *
0100             sizeof(ext2_acl_entry), GFP_KERNEL);
0101     if (!ext_acl)
0102         return ERR_PTR(-ENOMEM);
0103     ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION);
0104     e = (char *)ext_acl + sizeof(ext2_acl_header);
0105     for (n=0; n < acl->a_count; n++) {
0106         const struct posix_acl_entry *acl_e = &acl->a_entries[n];
0107         ext2_acl_entry *entry = (ext2_acl_entry *)e;
0108         entry->e_tag  = cpu_to_le16(acl_e->e_tag);
0109         entry->e_perm = cpu_to_le16(acl_e->e_perm);
0110         switch(acl_e->e_tag) {
0111             case ACL_USER:
0112                 entry->e_id = cpu_to_le32(
0113                     from_kuid(&init_user_ns, acl_e->e_uid));
0114                 e += sizeof(ext2_acl_entry);
0115                 break;
0116             case ACL_GROUP:
0117                 entry->e_id = cpu_to_le32(
0118                     from_kgid(&init_user_ns, acl_e->e_gid));
0119                 e += sizeof(ext2_acl_entry);
0120                 break;
0121 
0122             case ACL_USER_OBJ:
0123             case ACL_GROUP_OBJ:
0124             case ACL_MASK:
0125             case ACL_OTHER:
0126                 e += sizeof(ext2_acl_entry_short);
0127                 break;
0128 
0129             default:
0130                 goto fail;
0131         }
0132     }
0133     return (char *)ext_acl;
0134 
0135 fail:
0136     kfree(ext_acl);
0137     return ERR_PTR(-EINVAL);
0138 }
0139 
0140 /*
0141  * inode->i_mutex: don't care
0142  */
0143 struct posix_acl *
0144 ext2_get_acl(struct inode *inode, int type, bool rcu)
0145 {
0146     int name_index;
0147     char *value = NULL;
0148     struct posix_acl *acl;
0149     int retval;
0150 
0151     if (rcu)
0152         return ERR_PTR(-ECHILD);
0153 
0154     switch (type) {
0155     case ACL_TYPE_ACCESS:
0156         name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
0157         break;
0158     case ACL_TYPE_DEFAULT:
0159         name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
0160         break;
0161     default:
0162         BUG();
0163     }
0164     retval = ext2_xattr_get(inode, name_index, "", NULL, 0);
0165     if (retval > 0) {
0166         value = kmalloc(retval, GFP_KERNEL);
0167         if (!value)
0168             return ERR_PTR(-ENOMEM);
0169         retval = ext2_xattr_get(inode, name_index, "", value, retval);
0170     }
0171     if (retval > 0)
0172         acl = ext2_acl_from_disk(value, retval);
0173     else if (retval == -ENODATA || retval == -ENOSYS)
0174         acl = NULL;
0175     else
0176         acl = ERR_PTR(retval);
0177     kfree(value);
0178 
0179     return acl;
0180 }
0181 
0182 static int
0183 __ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
0184 {
0185     int name_index;
0186     void *value = NULL;
0187     size_t size = 0;
0188     int error;
0189 
0190     switch(type) {
0191         case ACL_TYPE_ACCESS:
0192             name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
0193             break;
0194 
0195         case ACL_TYPE_DEFAULT:
0196             name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
0197             if (!S_ISDIR(inode->i_mode))
0198                 return acl ? -EACCES : 0;
0199             break;
0200 
0201         default:
0202             return -EINVAL;
0203     }
0204     if (acl) {
0205         value = ext2_acl_to_disk(acl, &size);
0206         if (IS_ERR(value))
0207             return (int)PTR_ERR(value);
0208     }
0209 
0210     error = ext2_xattr_set(inode, name_index, "", value, size, 0);
0211 
0212     kfree(value);
0213     if (!error)
0214         set_cached_acl(inode, type, acl);
0215     return error;
0216 }
0217 
0218 /*
0219  * inode->i_mutex: down
0220  */
0221 int
0222 ext2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
0223          struct posix_acl *acl, int type)
0224 {
0225     int error;
0226     int update_mode = 0;
0227     umode_t mode = inode->i_mode;
0228 
0229     if (type == ACL_TYPE_ACCESS && acl) {
0230         error = posix_acl_update_mode(&init_user_ns, inode, &mode,
0231                           &acl);
0232         if (error)
0233             return error;
0234         update_mode = 1;
0235     }
0236     error = __ext2_set_acl(inode, acl, type);
0237     if (!error && update_mode) {
0238         inode->i_mode = mode;
0239         inode->i_ctime = current_time(inode);
0240         mark_inode_dirty(inode);
0241     }
0242     return error;
0243 }
0244 
0245 /*
0246  * Initialize the ACLs of a new inode. Called from ext2_new_inode.
0247  *
0248  * dir->i_mutex: down
0249  * inode->i_mutex: up (access to inode is still exclusive)
0250  */
0251 int
0252 ext2_init_acl(struct inode *inode, struct inode *dir)
0253 {
0254     struct posix_acl *default_acl, *acl;
0255     int error;
0256 
0257     error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
0258     if (error)
0259         return error;
0260 
0261     if (default_acl) {
0262         error = __ext2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
0263         posix_acl_release(default_acl);
0264     } else {
0265         inode->i_default_acl = NULL;
0266     }
0267     if (acl) {
0268         if (!error)
0269             error = __ext2_set_acl(inode, acl, ACL_TYPE_ACCESS);
0270         posix_acl_release(acl);
0271     } else {
0272         inode->i_acl = NULL;
0273     }
0274     return error;
0275 }