1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2ac27a0ecSDave Kleikamp /* 3617ba13bSMingming Cao * linux/fs/ext4/acl.c 4ac27a0ecSDave Kleikamp * 5ac27a0ecSDave Kleikamp * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> 6ac27a0ecSDave Kleikamp */ 7ac27a0ecSDave Kleikamp 8b8cb5a54STahsin Erdogan #include <linux/quotaops.h> 93dcf5451SChristoph Hellwig #include "ext4_jbd2.h" 103dcf5451SChristoph Hellwig #include "ext4.h" 11ac27a0ecSDave Kleikamp #include "xattr.h" 12ac27a0ecSDave Kleikamp #include "acl.h" 13ac27a0ecSDave Kleikamp 14ac27a0ecSDave Kleikamp /* 15ac27a0ecSDave Kleikamp * Convert from filesystem to in-memory representation. 16ac27a0ecSDave Kleikamp */ 17ac27a0ecSDave Kleikamp static struct posix_acl * 18617ba13bSMingming Cao ext4_acl_from_disk(const void *value, size_t size) 19ac27a0ecSDave Kleikamp { 20ac27a0ecSDave Kleikamp const char *end = (char *)value + size; 21ac27a0ecSDave Kleikamp int n, count; 22ac27a0ecSDave Kleikamp struct posix_acl *acl; 23ac27a0ecSDave Kleikamp 24ac27a0ecSDave Kleikamp if (!value) 25ac27a0ecSDave Kleikamp return NULL; 26617ba13bSMingming Cao if (size < sizeof(ext4_acl_header)) 27ac27a0ecSDave Kleikamp return ERR_PTR(-EINVAL); 28617ba13bSMingming Cao if (((ext4_acl_header *)value)->a_version != 29617ba13bSMingming Cao cpu_to_le32(EXT4_ACL_VERSION)) 30ac27a0ecSDave Kleikamp return ERR_PTR(-EINVAL); 31617ba13bSMingming Cao value = (char *)value + sizeof(ext4_acl_header); 32617ba13bSMingming Cao count = ext4_acl_count(size); 33ac27a0ecSDave Kleikamp if (count < 0) 34ac27a0ecSDave Kleikamp return ERR_PTR(-EINVAL); 35ac27a0ecSDave Kleikamp if (count == 0) 36ac27a0ecSDave Kleikamp return NULL; 37216553c4SJosef Bacik acl = posix_acl_alloc(count, GFP_NOFS); 38ac27a0ecSDave Kleikamp if (!acl) 39ac27a0ecSDave Kleikamp return ERR_PTR(-ENOMEM); 40ac27a0ecSDave Kleikamp for (n = 0; n < count; n++) { 41617ba13bSMingming Cao ext4_acl_entry *entry = 42617ba13bSMingming Cao (ext4_acl_entry *)value; 43617ba13bSMingming Cao if ((char *)value + sizeof(ext4_acl_entry_short) > end) 44ac27a0ecSDave Kleikamp goto fail; 45ac27a0ecSDave Kleikamp acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); 46ac27a0ecSDave Kleikamp acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); 472b2d6d01STheodore Ts'o 48ac27a0ecSDave Kleikamp switch (acl->a_entries[n].e_tag) { 49ac27a0ecSDave Kleikamp case ACL_USER_OBJ: 50ac27a0ecSDave Kleikamp case ACL_GROUP_OBJ: 51ac27a0ecSDave Kleikamp case ACL_MASK: 52ac27a0ecSDave Kleikamp case ACL_OTHER: 53ac27a0ecSDave Kleikamp value = (char *)value + 54617ba13bSMingming Cao sizeof(ext4_acl_entry_short); 55ac27a0ecSDave Kleikamp break; 56ac27a0ecSDave Kleikamp 57ac27a0ecSDave Kleikamp case ACL_USER: 58af84df93SEric W. Biederman value = (char *)value + sizeof(ext4_acl_entry); 59af84df93SEric W. Biederman if ((char *)value > end) 60af84df93SEric W. Biederman goto fail; 61af84df93SEric W. Biederman acl->a_entries[n].e_uid = 62af84df93SEric W. Biederman make_kuid(&init_user_ns, 63af84df93SEric W. Biederman le32_to_cpu(entry->e_id)); 64af84df93SEric W. Biederman break; 65ac27a0ecSDave Kleikamp case ACL_GROUP: 66617ba13bSMingming Cao value = (char *)value + sizeof(ext4_acl_entry); 67ac27a0ecSDave Kleikamp if ((char *)value > end) 68ac27a0ecSDave Kleikamp goto fail; 69af84df93SEric W. Biederman acl->a_entries[n].e_gid = 70af84df93SEric W. Biederman make_kgid(&init_user_ns, 71af84df93SEric W. Biederman le32_to_cpu(entry->e_id)); 72ac27a0ecSDave Kleikamp break; 73ac27a0ecSDave Kleikamp 74ac27a0ecSDave Kleikamp default: 75ac27a0ecSDave Kleikamp goto fail; 76ac27a0ecSDave Kleikamp } 77ac27a0ecSDave Kleikamp } 78ac27a0ecSDave Kleikamp if (value != end) 79ac27a0ecSDave Kleikamp goto fail; 80ac27a0ecSDave Kleikamp return acl; 81ac27a0ecSDave Kleikamp 82ac27a0ecSDave Kleikamp fail: 83ac27a0ecSDave Kleikamp posix_acl_release(acl); 84ac27a0ecSDave Kleikamp return ERR_PTR(-EINVAL); 85ac27a0ecSDave Kleikamp } 86ac27a0ecSDave Kleikamp 87ac27a0ecSDave Kleikamp /* 88ac27a0ecSDave Kleikamp * Convert from in-memory to filesystem representation. 89ac27a0ecSDave Kleikamp */ 90ac27a0ecSDave Kleikamp static void * 91617ba13bSMingming Cao ext4_acl_to_disk(const struct posix_acl *acl, size_t *size) 92ac27a0ecSDave Kleikamp { 93617ba13bSMingming Cao ext4_acl_header *ext_acl; 94ac27a0ecSDave Kleikamp char *e; 95ac27a0ecSDave Kleikamp size_t n; 96ac27a0ecSDave Kleikamp 97617ba13bSMingming Cao *size = ext4_acl_size(acl->a_count); 98617ba13bSMingming Cao ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count * 99216553c4SJosef Bacik sizeof(ext4_acl_entry), GFP_NOFS); 100ac27a0ecSDave Kleikamp if (!ext_acl) 101ac27a0ecSDave Kleikamp return ERR_PTR(-ENOMEM); 102617ba13bSMingming Cao ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION); 103617ba13bSMingming Cao e = (char *)ext_acl + sizeof(ext4_acl_header); 104ac27a0ecSDave Kleikamp for (n = 0; n < acl->a_count; n++) { 105af84df93SEric W. Biederman const struct posix_acl_entry *acl_e = &acl->a_entries[n]; 106617ba13bSMingming Cao ext4_acl_entry *entry = (ext4_acl_entry *)e; 107af84df93SEric W. Biederman entry->e_tag = cpu_to_le16(acl_e->e_tag); 108af84df93SEric W. Biederman entry->e_perm = cpu_to_le16(acl_e->e_perm); 109af84df93SEric W. Biederman switch (acl_e->e_tag) { 110ac27a0ecSDave Kleikamp case ACL_USER: 111af84df93SEric W. Biederman entry->e_id = cpu_to_le32( 112af84df93SEric W. Biederman from_kuid(&init_user_ns, acl_e->e_uid)); 113af84df93SEric W. Biederman e += sizeof(ext4_acl_entry); 114af84df93SEric W. Biederman break; 115ac27a0ecSDave Kleikamp case ACL_GROUP: 116af84df93SEric W. Biederman entry->e_id = cpu_to_le32( 117af84df93SEric W. Biederman from_kgid(&init_user_ns, acl_e->e_gid)); 118617ba13bSMingming Cao e += sizeof(ext4_acl_entry); 119ac27a0ecSDave Kleikamp break; 120ac27a0ecSDave Kleikamp 121ac27a0ecSDave Kleikamp case ACL_USER_OBJ: 122ac27a0ecSDave Kleikamp case ACL_GROUP_OBJ: 123ac27a0ecSDave Kleikamp case ACL_MASK: 124ac27a0ecSDave Kleikamp case ACL_OTHER: 125617ba13bSMingming Cao e += sizeof(ext4_acl_entry_short); 126ac27a0ecSDave Kleikamp break; 127ac27a0ecSDave Kleikamp 128ac27a0ecSDave Kleikamp default: 129ac27a0ecSDave Kleikamp goto fail; 130ac27a0ecSDave Kleikamp } 131ac27a0ecSDave Kleikamp } 132ac27a0ecSDave Kleikamp return (char *)ext_acl; 133ac27a0ecSDave Kleikamp 134ac27a0ecSDave Kleikamp fail: 135ac27a0ecSDave Kleikamp kfree(ext_acl); 136ac27a0ecSDave Kleikamp return ERR_PTR(-EINVAL); 137ac27a0ecSDave Kleikamp } 138ac27a0ecSDave Kleikamp 139ac27a0ecSDave Kleikamp /* 140ac27a0ecSDave Kleikamp * Inode operation get_posix_acl(). 141ac27a0ecSDave Kleikamp * 142f340b3d9Shongnanli * inode->i_rwsem: don't care 143ac27a0ecSDave Kleikamp */ 1444e34e719SChristoph Hellwig struct posix_acl * 1450cad6246SMiklos Szeredi ext4_get_acl(struct inode *inode, int type, bool rcu) 146ac27a0ecSDave Kleikamp { 147ac27a0ecSDave Kleikamp int name_index; 148ac27a0ecSDave Kleikamp char *value = NULL; 149ac27a0ecSDave Kleikamp struct posix_acl *acl; 150ac27a0ecSDave Kleikamp int retval; 151ac27a0ecSDave Kleikamp 1520cad6246SMiklos Szeredi if (rcu) 1530cad6246SMiklos Szeredi return ERR_PTR(-ECHILD); 1540cad6246SMiklos Szeredi 155ac27a0ecSDave Kleikamp switch (type) { 156ac27a0ecSDave Kleikamp case ACL_TYPE_ACCESS: 157617ba13bSMingming Cao name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; 158ac27a0ecSDave Kleikamp break; 159ac27a0ecSDave Kleikamp case ACL_TYPE_DEFAULT: 160617ba13bSMingming Cao name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT; 161ac27a0ecSDave Kleikamp break; 162ac27a0ecSDave Kleikamp default: 163073aaa1bSAl Viro BUG(); 164ac27a0ecSDave Kleikamp } 165617ba13bSMingming Cao retval = ext4_xattr_get(inode, name_index, "", NULL, 0); 166ac27a0ecSDave Kleikamp if (retval > 0) { 167216553c4SJosef Bacik value = kmalloc(retval, GFP_NOFS); 168ac27a0ecSDave Kleikamp if (!value) 169ac27a0ecSDave Kleikamp return ERR_PTR(-ENOMEM); 170617ba13bSMingming Cao retval = ext4_xattr_get(inode, name_index, "", value, retval); 171ac27a0ecSDave Kleikamp } 172ac27a0ecSDave Kleikamp if (retval > 0) 173617ba13bSMingming Cao acl = ext4_acl_from_disk(value, retval); 174ac27a0ecSDave Kleikamp else if (retval == -ENODATA || retval == -ENOSYS) 175ac27a0ecSDave Kleikamp acl = NULL; 176ac27a0ecSDave Kleikamp else 177ac27a0ecSDave Kleikamp acl = ERR_PTR(retval); 178ac27a0ecSDave Kleikamp kfree(value); 179ac27a0ecSDave Kleikamp 180ac27a0ecSDave Kleikamp return acl; 181ac27a0ecSDave Kleikamp } 182ac27a0ecSDave Kleikamp 183ac27a0ecSDave Kleikamp /* 184ac27a0ecSDave Kleikamp * Set the access or default ACL of an inode. 185ac27a0ecSDave Kleikamp * 186f340b3d9Shongnanli * inode->i_rwsem: down unless called from ext4_new_inode 187ac27a0ecSDave Kleikamp */ 188ac27a0ecSDave Kleikamp static int 18964e178a7SChristoph Hellwig __ext4_set_acl(handle_t *handle, struct inode *inode, int type, 190af65207cSTahsin Erdogan struct posix_acl *acl, int xattr_flags) 191ac27a0ecSDave Kleikamp { 192ac27a0ecSDave Kleikamp int name_index; 193ac27a0ecSDave Kleikamp void *value = NULL; 194ac27a0ecSDave Kleikamp size_t size = 0; 195ac27a0ecSDave Kleikamp int error; 196ac27a0ecSDave Kleikamp 197ac27a0ecSDave Kleikamp switch (type) { 198ac27a0ecSDave Kleikamp case ACL_TYPE_ACCESS: 199617ba13bSMingming Cao name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; 200ac27a0ecSDave Kleikamp break; 201ac27a0ecSDave Kleikamp 202ac27a0ecSDave Kleikamp case ACL_TYPE_DEFAULT: 203617ba13bSMingming Cao name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT; 204ac27a0ecSDave Kleikamp if (!S_ISDIR(inode->i_mode)) 205ac27a0ecSDave Kleikamp return acl ? -EACCES : 0; 206ac27a0ecSDave Kleikamp break; 207ac27a0ecSDave Kleikamp 208ac27a0ecSDave Kleikamp default: 209ac27a0ecSDave Kleikamp return -EINVAL; 210ac27a0ecSDave Kleikamp } 211ac27a0ecSDave Kleikamp if (acl) { 212617ba13bSMingming Cao value = ext4_acl_to_disk(acl, &size); 213ac27a0ecSDave Kleikamp if (IS_ERR(value)) 214ac27a0ecSDave Kleikamp return (int)PTR_ERR(value); 215ac27a0ecSDave Kleikamp } 216ac27a0ecSDave Kleikamp 217617ba13bSMingming Cao error = ext4_xattr_set_handle(handle, inode, name_index, "", 218af65207cSTahsin Erdogan value, size, xattr_flags); 219ac27a0ecSDave Kleikamp 220ac27a0ecSDave Kleikamp kfree(value); 2216b6aeffcSCarlos Guerrero Álvarez if (!error) 222073aaa1bSAl Viro set_cached_acl(inode, type, acl); 223ac27a0ecSDave Kleikamp 224ac27a0ecSDave Kleikamp return error; 225ac27a0ecSDave Kleikamp } 226ac27a0ecSDave Kleikamp 22764e178a7SChristoph Hellwig int 22813e83a49SChristian Brauner ext4_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 229549c7297SChristian Brauner struct posix_acl *acl, int type) 23064e178a7SChristoph Hellwig { 23164e178a7SChristoph Hellwig handle_t *handle; 232c1a5d5f6STahsin Erdogan int error, credits, retries = 0; 233c1a5d5f6STahsin Erdogan size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0; 234138060baSChristian Brauner struct inode *inode = d_inode(dentry); 235a3bb2d55SJan Kara umode_t mode = inode->i_mode; 236a3bb2d55SJan Kara int update_mode = 0; 23764e178a7SChristoph Hellwig 238b8cb5a54STahsin Erdogan error = dquot_initialize(inode); 239b8cb5a54STahsin Erdogan if (error) 240b8cb5a54STahsin Erdogan return error; 24164e178a7SChristoph Hellwig retry: 242af65207cSTahsin Erdogan error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */, 243af65207cSTahsin Erdogan &credits); 244dec214d0STahsin Erdogan if (error) 245dec214d0STahsin Erdogan return error; 246dec214d0STahsin Erdogan 247c1a5d5f6STahsin Erdogan handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits); 24864e178a7SChristoph Hellwig if (IS_ERR(handle)) 24964e178a7SChristoph Hellwig return PTR_ERR(handle); 25064e178a7SChristoph Hellwig 251a3bb2d55SJan Kara if ((type == ACL_TYPE_ACCESS) && acl) { 252*700b7940SChristian Brauner error = posix_acl_update_mode(idmap, inode, &mode, &acl); 253a3bb2d55SJan Kara if (error) 254a3bb2d55SJan Kara goto out_stop; 2550a1e8258SChengguang Xu if (mode != inode->i_mode) 256a3bb2d55SJan Kara update_mode = 1; 257a3bb2d55SJan Kara } 258a3bb2d55SJan Kara 259af65207cSTahsin Erdogan error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */); 260a3bb2d55SJan Kara if (!error && update_mode) { 261a3bb2d55SJan Kara inode->i_mode = mode; 262a3bb2d55SJan Kara inode->i_ctime = current_time(inode); 2634209ae12SHarshad Shirwadkar error = ext4_mark_inode_dirty(handle, inode); 264a3bb2d55SJan Kara } 265a3bb2d55SJan Kara out_stop: 26664e178a7SChristoph Hellwig ext4_journal_stop(handle); 26764e178a7SChristoph Hellwig if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) 26864e178a7SChristoph Hellwig goto retry; 26964e178a7SChristoph Hellwig return error; 27064e178a7SChristoph Hellwig } 27164e178a7SChristoph Hellwig 272ac27a0ecSDave Kleikamp /* 273617ba13bSMingming Cao * Initialize the ACLs of a new inode. Called from ext4_new_inode. 274ac27a0ecSDave Kleikamp * 275f340b3d9Shongnanli * dir->i_rwsem: down 276f340b3d9Shongnanli * inode->i_rwsem: up (access to inode is still exclusive) 277ac27a0ecSDave Kleikamp */ 278ac27a0ecSDave Kleikamp int 279617ba13bSMingming Cao ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) 280ac27a0ecSDave Kleikamp { 28164e178a7SChristoph Hellwig struct posix_acl *default_acl, *acl; 282ac27a0ecSDave Kleikamp int error; 283ac27a0ecSDave Kleikamp 28464e178a7SChristoph Hellwig error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 285bc26ab5fSAl Viro if (error) 286bc26ab5fSAl Viro return error; 28764e178a7SChristoph Hellwig 28864e178a7SChristoph Hellwig if (default_acl) { 28964e178a7SChristoph Hellwig error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT, 290af65207cSTahsin Erdogan default_acl, XATTR_CREATE); 29164e178a7SChristoph Hellwig posix_acl_release(default_acl); 2926fd94178SChengguang Xu } else { 2936fd94178SChengguang Xu inode->i_default_acl = NULL; 294ac27a0ecSDave Kleikamp } 29564e178a7SChristoph Hellwig if (acl) { 29664e178a7SChristoph Hellwig if (!error) 29764e178a7SChristoph Hellwig error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, 298af65207cSTahsin Erdogan acl, XATTR_CREATE); 299bc26ab5fSAl Viro posix_acl_release(acl); 3006fd94178SChengguang Xu } else { 3016fd94178SChengguang Xu inode->i_acl = NULL; 30264e178a7SChristoph Hellwig } 303ac27a0ecSDave Kleikamp return error; 304ac27a0ecSDave Kleikamp } 305