Back to home page

OSCL-LXR

 
 

    


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