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 * 142ac27a0ecSDave Kleikamp * inode->i_mutex: don't care 143ac27a0ecSDave Kleikamp */ 1444e34e719SChristoph Hellwig struct posix_acl * 145617ba13bSMingming Cao ext4_get_acl(struct inode *inode, int type) 146ac27a0ecSDave Kleikamp { 147ac27a0ecSDave Kleikamp int name_index; 148ac27a0ecSDave Kleikamp char *value = NULL; 149ac27a0ecSDave Kleikamp struct posix_acl *acl; 150ac27a0ecSDave Kleikamp int retval; 151ac27a0ecSDave Kleikamp 152ac27a0ecSDave Kleikamp switch (type) { 153ac27a0ecSDave Kleikamp case ACL_TYPE_ACCESS: 154617ba13bSMingming Cao name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; 155ac27a0ecSDave Kleikamp break; 156ac27a0ecSDave Kleikamp case ACL_TYPE_DEFAULT: 157617ba13bSMingming Cao name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT; 158ac27a0ecSDave Kleikamp break; 159ac27a0ecSDave Kleikamp default: 160073aaa1bSAl Viro BUG(); 161ac27a0ecSDave Kleikamp } 162617ba13bSMingming Cao retval = ext4_xattr_get(inode, name_index, "", NULL, 0); 163ac27a0ecSDave Kleikamp if (retval > 0) { 164216553c4SJosef Bacik value = kmalloc(retval, GFP_NOFS); 165ac27a0ecSDave Kleikamp if (!value) 166ac27a0ecSDave Kleikamp return ERR_PTR(-ENOMEM); 167617ba13bSMingming Cao retval = ext4_xattr_get(inode, name_index, "", value, retval); 168ac27a0ecSDave Kleikamp } 169ac27a0ecSDave Kleikamp if (retval > 0) 170617ba13bSMingming Cao acl = ext4_acl_from_disk(value, retval); 171ac27a0ecSDave Kleikamp else if (retval == -ENODATA || retval == -ENOSYS) 172ac27a0ecSDave Kleikamp acl = NULL; 173ac27a0ecSDave Kleikamp else 174ac27a0ecSDave Kleikamp acl = ERR_PTR(retval); 175ac27a0ecSDave Kleikamp kfree(value); 176ac27a0ecSDave Kleikamp 177ac27a0ecSDave Kleikamp return acl; 178ac27a0ecSDave Kleikamp } 179ac27a0ecSDave Kleikamp 180ac27a0ecSDave Kleikamp /* 181ac27a0ecSDave Kleikamp * Set the access or default ACL of an inode. 182ac27a0ecSDave Kleikamp * 183617ba13bSMingming Cao * inode->i_mutex: down unless called from ext4_new_inode 184ac27a0ecSDave Kleikamp */ 185ac27a0ecSDave Kleikamp static int 18664e178a7SChristoph Hellwig __ext4_set_acl(handle_t *handle, struct inode *inode, int type, 187af65207cSTahsin Erdogan struct posix_acl *acl, int xattr_flags) 188ac27a0ecSDave Kleikamp { 189ac27a0ecSDave Kleikamp int name_index; 190ac27a0ecSDave Kleikamp void *value = NULL; 191ac27a0ecSDave Kleikamp size_t size = 0; 192ac27a0ecSDave Kleikamp int error; 193ac27a0ecSDave Kleikamp 194ac27a0ecSDave Kleikamp switch (type) { 195ac27a0ecSDave Kleikamp case ACL_TYPE_ACCESS: 196617ba13bSMingming Cao name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; 197ac27a0ecSDave Kleikamp break; 198ac27a0ecSDave Kleikamp 199ac27a0ecSDave Kleikamp case ACL_TYPE_DEFAULT: 200617ba13bSMingming Cao name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT; 201ac27a0ecSDave Kleikamp if (!S_ISDIR(inode->i_mode)) 202ac27a0ecSDave Kleikamp return acl ? -EACCES : 0; 203ac27a0ecSDave Kleikamp break; 204ac27a0ecSDave Kleikamp 205ac27a0ecSDave Kleikamp default: 206ac27a0ecSDave Kleikamp return -EINVAL; 207ac27a0ecSDave Kleikamp } 208ac27a0ecSDave Kleikamp if (acl) { 209617ba13bSMingming Cao value = ext4_acl_to_disk(acl, &size); 210ac27a0ecSDave Kleikamp if (IS_ERR(value)) 211ac27a0ecSDave Kleikamp return (int)PTR_ERR(value); 212ac27a0ecSDave Kleikamp } 213ac27a0ecSDave Kleikamp 214617ba13bSMingming Cao error = ext4_xattr_set_handle(handle, inode, name_index, "", 215af65207cSTahsin Erdogan value, size, xattr_flags); 216ac27a0ecSDave Kleikamp 217ac27a0ecSDave Kleikamp kfree(value); 218397e4341SErnesto A. Fernández if (!error) { 219073aaa1bSAl Viro set_cached_acl(inode, type, acl); 220397e4341SErnesto A. Fernández } 221ac27a0ecSDave Kleikamp 222ac27a0ecSDave Kleikamp return error; 223ac27a0ecSDave Kleikamp } 224ac27a0ecSDave Kleikamp 22564e178a7SChristoph Hellwig int 22664e178a7SChristoph Hellwig ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type) 22764e178a7SChristoph Hellwig { 22864e178a7SChristoph Hellwig handle_t *handle; 229c1a5d5f6STahsin Erdogan int error, credits, retries = 0; 230c1a5d5f6STahsin Erdogan size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0; 231a3bb2d55SJan Kara umode_t mode = inode->i_mode; 232a3bb2d55SJan Kara int update_mode = 0; 23364e178a7SChristoph Hellwig 234b8cb5a54STahsin Erdogan error = dquot_initialize(inode); 235b8cb5a54STahsin Erdogan if (error) 236b8cb5a54STahsin Erdogan return error; 23764e178a7SChristoph Hellwig retry: 238af65207cSTahsin Erdogan error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */, 239af65207cSTahsin Erdogan &credits); 240dec214d0STahsin Erdogan if (error) 241dec214d0STahsin Erdogan return error; 242dec214d0STahsin Erdogan 243c1a5d5f6STahsin Erdogan handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits); 24464e178a7SChristoph Hellwig if (IS_ERR(handle)) 24564e178a7SChristoph Hellwig return PTR_ERR(handle); 24664e178a7SChristoph Hellwig 247a3bb2d55SJan Kara if ((type == ACL_TYPE_ACCESS) && acl) { 248a3bb2d55SJan Kara error = posix_acl_update_mode(inode, &mode, &acl); 249a3bb2d55SJan Kara if (error) 250a3bb2d55SJan Kara goto out_stop; 2510a1e8258SChengguang Xu if (mode != inode->i_mode) 252a3bb2d55SJan Kara update_mode = 1; 253a3bb2d55SJan Kara } 254a3bb2d55SJan Kara 255af65207cSTahsin Erdogan error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */); 256a3bb2d55SJan Kara if (!error && update_mode) { 257a3bb2d55SJan Kara inode->i_mode = mode; 258a3bb2d55SJan Kara inode->i_ctime = current_time(inode); 259a3bb2d55SJan Kara ext4_mark_inode_dirty(handle, inode); 260a3bb2d55SJan Kara } 261a3bb2d55SJan Kara out_stop: 26264e178a7SChristoph Hellwig ext4_journal_stop(handle); 26364e178a7SChristoph Hellwig if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) 26464e178a7SChristoph Hellwig goto retry; 26564e178a7SChristoph Hellwig return error; 26664e178a7SChristoph Hellwig } 26764e178a7SChristoph Hellwig 268ac27a0ecSDave Kleikamp /* 269617ba13bSMingming Cao * Initialize the ACLs of a new inode. Called from ext4_new_inode. 270ac27a0ecSDave Kleikamp * 271ac27a0ecSDave Kleikamp * dir->i_mutex: down 272ac27a0ecSDave Kleikamp * inode->i_mutex: up (access to inode is still exclusive) 273ac27a0ecSDave Kleikamp */ 274ac27a0ecSDave Kleikamp int 275617ba13bSMingming Cao ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) 276ac27a0ecSDave Kleikamp { 27764e178a7SChristoph Hellwig struct posix_acl *default_acl, *acl; 278ac27a0ecSDave Kleikamp int error; 279ac27a0ecSDave Kleikamp 28064e178a7SChristoph Hellwig error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 281bc26ab5fSAl Viro if (error) 282bc26ab5fSAl Viro return error; 28364e178a7SChristoph Hellwig 28464e178a7SChristoph Hellwig if (default_acl) { 28564e178a7SChristoph Hellwig error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT, 286af65207cSTahsin Erdogan default_acl, XATTR_CREATE); 28764e178a7SChristoph Hellwig posix_acl_release(default_acl); 2886fd94178SChengguang Xu } else { 2896fd94178SChengguang Xu inode->i_default_acl = NULL; 290ac27a0ecSDave Kleikamp } 29164e178a7SChristoph Hellwig if (acl) { 29264e178a7SChristoph Hellwig if (!error) 29364e178a7SChristoph Hellwig error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, 294af65207cSTahsin Erdogan acl, XATTR_CREATE); 295bc26ab5fSAl Viro posix_acl_release(acl); 2966fd94178SChengguang Xu } else { 2976fd94178SChengguang Xu inode->i_acl = NULL; 29864e178a7SChristoph Hellwig } 299ac27a0ecSDave Kleikamp return error; 300ac27a0ecSDave Kleikamp } 301