LCOV - code coverage report
Current view: top level - fs/ext4 - acl.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-achx @ Mon Jul 31 20:08:12 PDT 2023 Lines: 137 159 86.2 %
Date: 2023-07-31 20:08:12 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * linux/fs/ext4/acl.c
       4             :  *
       5             :  * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
       6             :  */
       7             : 
       8             : #include <linux/quotaops.h>
       9             : #include "ext4_jbd2.h"
      10             : #include "ext4.h"
      11             : #include "xattr.h"
      12             : #include "acl.h"
      13             : 
      14             : /*
      15             :  * Convert from filesystem to in-memory representation.
      16             :  */
      17             : static struct posix_acl *
      18           3 : ext4_acl_from_disk(const void *value, size_t size)
      19             : {
      20           3 :         const char *end = (char *)value + size;
      21           3 :         int n, count;
      22           3 :         struct posix_acl *acl;
      23             : 
      24           3 :         if (!value)
      25             :                 return NULL;
      26           3 :         if (size < sizeof(ext4_acl_header))
      27             :                  return ERR_PTR(-EINVAL);
      28           3 :         if (((ext4_acl_header *)value)->a_version !=
      29             :             cpu_to_le32(EXT4_ACL_VERSION))
      30             :                 return ERR_PTR(-EINVAL);
      31           3 :         value = (char *)value + sizeof(ext4_acl_header);
      32           3 :         count = ext4_acl_count(size);
      33           3 :         if (count < 0)
      34             :                 return ERR_PTR(-EINVAL);
      35           3 :         if (count == 0)
      36             :                 return NULL;
      37           3 :         acl = posix_acl_alloc(count, GFP_NOFS);
      38           3 :         if (!acl)
      39             :                 return ERR_PTR(-ENOMEM);
      40          20 :         for (n = 0; n < count; n++) {
      41          17 :                 ext4_acl_entry *entry =
      42             :                         (ext4_acl_entry *)value;
      43          17 :                 if ((char *)value + sizeof(ext4_acl_entry_short) > end)
      44           0 :                         goto fail;
      45          17 :                 acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
      46          17 :                 acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
      47             : 
      48          17 :                 switch (acl->a_entries[n].e_tag) {
      49             :                 case ACL_USER_OBJ:
      50             :                 case ACL_GROUP_OBJ:
      51             :                 case ACL_MASK:
      52             :                 case ACL_OTHER:
      53             :                         value = (char *)value +
      54             :                                 sizeof(ext4_acl_entry_short);
      55             :                         break;
      56             : 
      57           3 :                 case ACL_USER:
      58           3 :                         value = (char *)value + sizeof(ext4_acl_entry);
      59           3 :                         if ((char *)value > end)
      60           0 :                                 goto fail;
      61           3 :                         acl->a_entries[n].e_uid =
      62           3 :                                 make_kuid(&init_user_ns,
      63           3 :                                           le32_to_cpu(entry->e_id));
      64           3 :                         break;
      65           2 :                 case ACL_GROUP:
      66           2 :                         value = (char *)value + sizeof(ext4_acl_entry);
      67           2 :                         if ((char *)value > end)
      68           0 :                                 goto fail;
      69           2 :                         acl->a_entries[n].e_gid =
      70           2 :                                 make_kgid(&init_user_ns,
      71           2 :                                           le32_to_cpu(entry->e_id));
      72           2 :                         break;
      73             : 
      74           0 :                 default:
      75           0 :                         goto fail;
      76             :                 }
      77             :         }
      78           3 :         if (value != end)
      79           0 :                 goto fail;
      80             :         return acl;
      81             : 
      82           0 : fail:
      83           0 :         posix_acl_release(acl);
      84           0 :         return ERR_PTR(-EINVAL);
      85             : }
      86             : 
      87             : /*
      88             :  * Convert from in-memory to filesystem representation.
      89             :  */
      90             : static void *
      91        2569 : ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
      92             : {
      93        2569 :         ext4_acl_header *ext_acl;
      94        2569 :         char *e;
      95        2569 :         size_t n;
      96             : 
      97        2569 :         *size = ext4_acl_size(acl->a_count);
      98        2569 :         ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
      99             :                         sizeof(ext4_acl_entry), GFP_NOFS);
     100        2569 :         if (!ext_acl)
     101             :                 return ERR_PTR(-ENOMEM);
     102        2569 :         ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
     103        2569 :         e = (char *)ext_acl + sizeof(ext4_acl_header);
     104       15365 :         for (n = 0; n < acl->a_count; n++) {
     105       12796 :                 const struct posix_acl_entry *acl_e = &acl->a_entries[n];
     106       12796 :                 ext4_acl_entry *entry = (ext4_acl_entry *)e;
     107       12796 :                 entry->e_tag  = cpu_to_le16(acl_e->e_tag);
     108       12796 :                 entry->e_perm = cpu_to_le16(acl_e->e_perm);
     109       12796 :                 switch (acl_e->e_tag) {
     110        2519 :                 case ACL_USER:
     111        2519 :                         entry->e_id = cpu_to_le32(
     112             :                                 from_kuid(&init_user_ns, acl_e->e_uid));
     113        2519 :                         e += sizeof(ext4_acl_entry);
     114        2519 :                         break;
     115           5 :                 case ACL_GROUP:
     116           5 :                         entry->e_id = cpu_to_le32(
     117             :                                 from_kgid(&init_user_ns, acl_e->e_gid));
     118           5 :                         e += sizeof(ext4_acl_entry);
     119           5 :                         break;
     120             : 
     121       10272 :                 case ACL_USER_OBJ:
     122             :                 case ACL_GROUP_OBJ:
     123             :                 case ACL_MASK:
     124             :                 case ACL_OTHER:
     125       10272 :                         e += sizeof(ext4_acl_entry_short);
     126       10272 :                         break;
     127             : 
     128           0 :                 default:
     129           0 :                         goto fail;
     130             :                 }
     131             :         }
     132             :         return (char *)ext_acl;
     133             : 
     134             : fail:
     135           0 :         kfree(ext_acl);
     136           0 :         return ERR_PTR(-EINVAL);
     137             : }
     138             : 
     139             : /*
     140             :  * Inode operation get_posix_acl().
     141             :  *
     142             :  * inode->i_rwsem: don't care
     143             :  */
     144             : struct posix_acl *
     145       13815 : ext4_get_acl(struct inode *inode, int type, bool rcu)
     146             : {
     147       13815 :         int name_index;
     148       13815 :         char *value = NULL;
     149       13815 :         struct posix_acl *acl;
     150       13815 :         int retval;
     151             : 
     152       13815 :         if (rcu)
     153             :                 return ERR_PTR(-ECHILD);
     154             : 
     155       13815 :         switch (type) {
     156             :         case ACL_TYPE_ACCESS:
     157             :                 name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
     158             :                 break;
     159        1132 :         case ACL_TYPE_DEFAULT:
     160        1132 :                 name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
     161        1132 :                 break;
     162           0 :         default:
     163           0 :                 BUG();
     164             :         }
     165       13815 :         retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
     166       13815 :         if (retval > 0) {
     167           3 :                 value = kmalloc(retval, GFP_NOFS);
     168           3 :                 if (!value)
     169             :                         return ERR_PTR(-ENOMEM);
     170           3 :                 retval = ext4_xattr_get(inode, name_index, "", value, retval);
     171             :         }
     172       13815 :         if (retval > 0)
     173           3 :                 acl = ext4_acl_from_disk(value, retval);
     174       13812 :         else if (retval == -ENODATA || retval == -ENOSYS)
     175             :                 acl = NULL;
     176             :         else
     177           0 :                 acl = ERR_PTR(retval);
     178       13815 :         kfree(value);
     179             : 
     180       13815 :         return acl;
     181             : }
     182             : 
     183             : /*
     184             :  * Set the access or default ACL of an inode.
     185             :  *
     186             :  * inode->i_rwsem: down unless called from ext4_new_inode
     187             :  */
     188             : static int
     189      926268 : __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
     190             :              struct posix_acl *acl, int xattr_flags)
     191             : {
     192      926268 :         int name_index;
     193      926268 :         void *value = NULL;
     194      926268 :         size_t size = 0;
     195      926268 :         int error;
     196             : 
     197      926268 :         switch (type) {
     198             :         case ACL_TYPE_ACCESS:
     199             :                 name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
     200             :                 break;
     201             : 
     202       10921 :         case ACL_TYPE_DEFAULT:
     203       10921 :                 name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
     204       10921 :                 if (!S_ISDIR(inode->i_mode))
     205           0 :                         return acl ? -EACCES : 0;
     206             :                 break;
     207             : 
     208             :         default:
     209             :                 return -EINVAL;
     210             :         }
     211      926268 :         if (acl) {
     212        2569 :                 value = ext4_acl_to_disk(acl, &size);
     213        2569 :                 if (IS_ERR(value))
     214           0 :                         return (int)PTR_ERR(value);
     215             :         }
     216             : 
     217      926268 :         error = ext4_xattr_set_handle(handle, inode, name_index, "",
     218             :                                       value, size, xattr_flags);
     219             : 
     220      926291 :         kfree(value);
     221      926288 :         if (!error)
     222      926287 :                 set_cached_acl(inode, type, acl);
     223             : 
     224             :         return error;
     225             : }
     226             : 
     227             : int
     228      923748 : ext4_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
     229             :              struct posix_acl *acl, int type)
     230             : {
     231      923748 :         handle_t *handle;
     232      923748 :         int error, credits, retries = 0;
     233      923748 :         size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;
     234      923748 :         struct inode *inode = d_inode(dentry);
     235      923748 :         umode_t mode = inode->i_mode;
     236      923748 :         int update_mode = 0;
     237             : 
     238      923748 :         error = dquot_initialize(inode);
     239      923745 :         if (error)
     240             :                 return error;
     241      923745 : retry:
     242      923745 :         error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
     243             :                                        &credits);
     244      923740 :         if (error)
     245           0 :                 return error;
     246             : 
     247      923740 :         handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
     248      923745 :         if (IS_ERR(handle))
     249           0 :                 return PTR_ERR(handle);
     250             : 
     251      923745 :         if ((type == ACL_TYPE_ACCESS) && acl) {
     252      910817 :                 error = posix_acl_update_mode(idmap, inode, &mode, &acl);
     253      910816 :                 if (error)
     254           0 :                         goto out_stop;
     255      910816 :                 if (mode != inode->i_mode)
     256      910802 :                         update_mode = 1;
     257             :         }
     258             : 
     259      923744 :         error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
     260      923739 :         if (!error && update_mode) {
     261      910804 :                 inode->i_mode = mode;
     262      910804 :                 inode->i_ctime = current_time(inode);
     263      910801 :                 error = ext4_mark_inode_dirty(handle, inode);
     264             :         }
     265       12935 : out_stop:
     266      923740 :         ext4_journal_stop(handle);
     267      923759 :         if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
     268           0 :                 goto retry;
     269             :         return error;
     270             : }
     271             : 
     272             : /*
     273             :  * Initialize the ACLs of a new inode. Called from ext4_new_inode.
     274             :  *
     275             :  * dir->i_rwsem: down
     276             :  * inode->i_rwsem: up (access to inode is still exclusive)
     277             :  */
     278             : int
     279     2785984 : ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
     280             : {
     281     2785984 :         struct posix_acl *default_acl, *acl;
     282     2785984 :         int error;
     283             : 
     284     2785984 :         error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
     285     2776499 :         if (error)
     286             :                 return error;
     287             : 
     288     2776499 :         if (default_acl) {
     289         222 :                 error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
     290             :                                        default_acl, XATTR_CREATE);
     291         222 :                 posix_acl_release(default_acl);
     292             :         } else {
     293     2776277 :                 inode->i_default_acl = NULL;
     294             :         }
     295     2776400 :         if (acl) {
     296        2317 :                 if (!error)
     297        2317 :                         error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
     298             :                                                acl, XATTR_CREATE);
     299        2317 :                 posix_acl_release(acl);
     300             :         } else {
     301     2774083 :                 inode->i_acl = NULL;
     302             :         }
     303             :         return error;
     304             : }

Generated by: LCOV version 1.14