139999a69SPedro F. Giffuni /*- 239999a69SPedro F. Giffuni * Copyright (c) 2017, Fedor Uporov 339999a69SPedro F. Giffuni * All rights reserved. 439999a69SPedro F. Giffuni * 539999a69SPedro F. Giffuni * Redistribution and use in source and binary forms, with or without 639999a69SPedro F. Giffuni * modification, are permitted provided that the following conditions 739999a69SPedro F. Giffuni * are met: 839999a69SPedro F. Giffuni * 1. Redistributions of source code must retain the above copyright 939999a69SPedro F. Giffuni * notice, this list of conditions and the following disclaimer. 1039999a69SPedro F. Giffuni * 2. Redistributions in binary form must reproduce the above copyright 1139999a69SPedro F. Giffuni * notice, this list of conditions and the following disclaimer in the 1239999a69SPedro F. Giffuni * documentation and/or other materials provided with the distribution. 1339999a69SPedro F. Giffuni * 1439999a69SPedro F. Giffuni * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1539999a69SPedro F. Giffuni * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1639999a69SPedro F. Giffuni * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1739999a69SPedro F. Giffuni * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1839999a69SPedro F. Giffuni * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1939999a69SPedro F. Giffuni * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2039999a69SPedro F. Giffuni * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2139999a69SPedro F. Giffuni * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2239999a69SPedro F. Giffuni * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2339999a69SPedro F. Giffuni * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2439999a69SPedro F. Giffuni * SUCH DAMAGE. 2539999a69SPedro F. Giffuni * 2639999a69SPedro F. Giffuni * $FreeBSD$ 2739999a69SPedro F. Giffuni */ 2839999a69SPedro F. Giffuni 2939999a69SPedro F. Giffuni #include <sys/param.h> 3039999a69SPedro F. Giffuni #include <sys/systm.h> 3139999a69SPedro F. Giffuni #include <sys/types.h> 3239999a69SPedro F. Giffuni #include <sys/stat.h> 3339999a69SPedro F. Giffuni #include <sys/kernel.h> 3439999a69SPedro F. Giffuni #include <sys/malloc.h> 3539999a69SPedro F. Giffuni #include <sys/vnode.h> 3639999a69SPedro F. Giffuni #include <sys/bio.h> 3739999a69SPedro F. Giffuni #include <sys/buf.h> 3839999a69SPedro F. Giffuni #include <sys/endian.h> 3939999a69SPedro F. Giffuni #include <sys/conf.h> 4039999a69SPedro F. Giffuni #include <sys/mount.h> 4139999a69SPedro F. Giffuni #include <sys/extattr.h> 4239999a69SPedro F. Giffuni 4339999a69SPedro F. Giffuni #include <fs/ext2fs/fs.h> 4439999a69SPedro F. Giffuni #include <fs/ext2fs/ext2fs.h> 4539999a69SPedro F. Giffuni #include <fs/ext2fs/inode.h> 4639999a69SPedro F. Giffuni #include <fs/ext2fs/ext2_acl.h> 4739999a69SPedro F. Giffuni #include <fs/ext2fs/ext2_extattr.h> 4839999a69SPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h> 4939999a69SPedro F. Giffuni #include <fs/ext2fs/ext2_dinode.h> 5039999a69SPedro F. Giffuni #include <fs/ext2fs/ext2_mount.h> 5139999a69SPedro F. Giffuni 5239999a69SPedro F. Giffuni void 5339999a69SPedro F. Giffuni ext2_sync_acl_from_inode(struct inode *ip, struct acl *acl) 5439999a69SPedro F. Giffuni { 5539999a69SPedro F. Giffuni struct acl_entry *acl_mask, *acl_group_obj; 5639999a69SPedro F. Giffuni int i; 5739999a69SPedro F. Giffuni 5839999a69SPedro F. Giffuni /* 5939999a69SPedro F. Giffuni * Update ACL_USER_OBJ, ACL_OTHER, but simply identify ACL_MASK 6039999a69SPedro F. Giffuni * and ACL_GROUP_OBJ for use after we know whether ACL_MASK is 6139999a69SPedro F. Giffuni * present. 6239999a69SPedro F. Giffuni */ 6339999a69SPedro F. Giffuni acl_mask = NULL; 6439999a69SPedro F. Giffuni acl_group_obj = NULL; 6539999a69SPedro F. Giffuni for (i = 0; i < acl->acl_cnt; i++) { 6639999a69SPedro F. Giffuni switch (acl->acl_entry[i].ae_tag) { 6739999a69SPedro F. Giffuni case ACL_USER_OBJ: 6839999a69SPedro F. Giffuni acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( 6939999a69SPedro F. Giffuni ACL_USER_OBJ, ip->i_mode); 7039999a69SPedro F. Giffuni acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 7139999a69SPedro F. Giffuni break; 7239999a69SPedro F. Giffuni 7339999a69SPedro F. Giffuni case ACL_GROUP_OBJ: 7439999a69SPedro F. Giffuni acl_group_obj = &acl->acl_entry[i]; 7539999a69SPedro F. Giffuni acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 7639999a69SPedro F. Giffuni break; 7739999a69SPedro F. Giffuni 7839999a69SPedro F. Giffuni case ACL_OTHER: 7939999a69SPedro F. Giffuni acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( 8039999a69SPedro F. Giffuni ACL_OTHER, ip->i_mode); 8139999a69SPedro F. Giffuni acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 8239999a69SPedro F. Giffuni break; 8339999a69SPedro F. Giffuni 8439999a69SPedro F. Giffuni case ACL_MASK: 8539999a69SPedro F. Giffuni acl_mask = &acl->acl_entry[i]; 8639999a69SPedro F. Giffuni acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 8739999a69SPedro F. Giffuni break; 8839999a69SPedro F. Giffuni 8939999a69SPedro F. Giffuni case ACL_USER: 9039999a69SPedro F. Giffuni case ACL_GROUP: 9139999a69SPedro F. Giffuni break; 9239999a69SPedro F. Giffuni 9339999a69SPedro F. Giffuni default: 9439999a69SPedro F. Giffuni panic("ext2_sync_acl_from_inode(): bad ae_tag"); 9539999a69SPedro F. Giffuni } 9639999a69SPedro F. Giffuni } 9739999a69SPedro F. Giffuni 9839999a69SPedro F. Giffuni if (acl_group_obj == NULL) 9939999a69SPedro F. Giffuni panic("ext2_sync_acl_from_inode(): no ACL_GROUP_OBJ"); 10039999a69SPedro F. Giffuni 10139999a69SPedro F. Giffuni if (acl_mask == NULL) { 10239999a69SPedro F. Giffuni /* 10339999a69SPedro F. Giffuni * There is no ACL_MASK, so update ACL_GROUP_OBJ. 10439999a69SPedro F. Giffuni */ 10539999a69SPedro F. Giffuni acl_group_obj->ae_perm = acl_posix1e_mode_to_perm( 10639999a69SPedro F. Giffuni ACL_GROUP_OBJ, ip->i_mode); 10739999a69SPedro F. Giffuni } else { 10839999a69SPedro F. Giffuni /* 10939999a69SPedro F. Giffuni * Update the ACL_MASK entry instead of ACL_GROUP_OBJ. 11039999a69SPedro F. Giffuni */ 11139999a69SPedro F. Giffuni acl_mask->ae_perm = acl_posix1e_mode_to_perm(ACL_GROUP_OBJ, 11239999a69SPedro F. Giffuni ip->i_mode); 11339999a69SPedro F. Giffuni } 11439999a69SPedro F. Giffuni } 11539999a69SPedro F. Giffuni 11639999a69SPedro F. Giffuni static void 11739999a69SPedro F. Giffuni ext2_sync_inode_from_acl(struct acl *acl, struct inode *ip) 11839999a69SPedro F. Giffuni { 11939999a69SPedro F. Giffuni 12039999a69SPedro F. Giffuni ip->i_mode &= ACL_PRESERVE_MASK; 12139999a69SPedro F. Giffuni ip->i_mode |= acl_posix1e_acl_to_mode(acl); 12239999a69SPedro F. Giffuni } 12339999a69SPedro F. Giffuni 12439999a69SPedro F. Giffuni /* 12539999a69SPedro F. Giffuni * Convert from filesystem to in-memory representation. 12639999a69SPedro F. Giffuni */ 12739999a69SPedro F. Giffuni static int 12839999a69SPedro F. Giffuni ext4_acl_from_disk(char *value, size_t size, struct acl *acl) 12939999a69SPedro F. Giffuni { 13039999a69SPedro F. Giffuni const char *end = value + size; 13139999a69SPedro F. Giffuni int n, count, s; 13239999a69SPedro F. Giffuni 13339999a69SPedro F. Giffuni if (((struct ext2_acl_header *)value)->a_version != EXT4_ACL_VERSION) 13439999a69SPedro F. Giffuni return (EINVAL); 13539999a69SPedro F. Giffuni 13639999a69SPedro F. Giffuni if (!value || size < sizeof(struct ext2_acl_header)) 13739999a69SPedro F. Giffuni return (EINVAL); 13839999a69SPedro F. Giffuni 13939999a69SPedro F. Giffuni s = size - sizeof(struct ext2_acl_header); 14039999a69SPedro F. Giffuni s -= 4 * sizeof(struct ext2_acl_entry_short); 14139999a69SPedro F. Giffuni if (s < 0) 14239999a69SPedro F. Giffuni if ((size - sizeof(struct ext2_acl_header)) % 14339999a69SPedro F. Giffuni sizeof(struct ext2_acl_entry_short)) 14439999a69SPedro F. Giffuni count = -1; 14539999a69SPedro F. Giffuni else 14639999a69SPedro F. Giffuni count = (size - sizeof(struct ext2_acl_header)) / 14739999a69SPedro F. Giffuni sizeof(struct ext2_acl_entry_short); 14839999a69SPedro F. Giffuni else 14939999a69SPedro F. Giffuni if (s % sizeof(struct ext2_acl_entry)) 15039999a69SPedro F. Giffuni count = -1; 15139999a69SPedro F. Giffuni else 15239999a69SPedro F. Giffuni count = s / sizeof(struct ext2_acl_entry) + 4; 15339999a69SPedro F. Giffuni 15439999a69SPedro F. Giffuni if (count <= 0 || count > acl->acl_maxcnt) 15539999a69SPedro F. Giffuni return (EINVAL); 15639999a69SPedro F. Giffuni 15739999a69SPedro F. Giffuni value = value + sizeof(struct ext2_acl_header); 15839999a69SPedro F. Giffuni 15939999a69SPedro F. Giffuni for (n = 0; n < count; n++) { 16039999a69SPedro F. Giffuni struct ext2_acl_entry *entry = (struct ext2_acl_entry *)value; 16139999a69SPedro F. Giffuni if ((char *)value + sizeof(struct ext2_acl_entry_short) > end) 16239999a69SPedro F. Giffuni return (EINVAL); 16339999a69SPedro F. Giffuni 16439999a69SPedro F. Giffuni acl->acl_entry[n].ae_tag = entry->ae_tag; 16539999a69SPedro F. Giffuni acl->acl_entry[n].ae_perm = entry->ae_perm; 16639999a69SPedro F. Giffuni 16739999a69SPedro F. Giffuni switch (acl->acl_entry[n].ae_tag) { 16839999a69SPedro F. Giffuni case ACL_USER_OBJ: 16939999a69SPedro F. Giffuni case ACL_GROUP_OBJ: 17039999a69SPedro F. Giffuni case ACL_MASK: 17139999a69SPedro F. Giffuni case ACL_OTHER: 17239999a69SPedro F. Giffuni value = (char *)value + sizeof(struct ext2_acl_entry_short); 17339999a69SPedro F. Giffuni break; 17439999a69SPedro F. Giffuni 17539999a69SPedro F. Giffuni case ACL_USER: 17639999a69SPedro F. Giffuni value = (char *)value + sizeof(struct ext2_acl_entry); 17739999a69SPedro F. Giffuni if ((char *)value > end) 17839999a69SPedro F. Giffuni return (EINVAL); 17939999a69SPedro F. Giffuni 18039999a69SPedro F. Giffuni acl->acl_entry[n].ae_id = entry->ae_id; 18139999a69SPedro F. Giffuni break; 18239999a69SPedro F. Giffuni 18339999a69SPedro F. Giffuni case ACL_GROUP: 18439999a69SPedro F. Giffuni value = (char *)value + sizeof(struct ext2_acl_entry); 18539999a69SPedro F. Giffuni if ((char *)value > end) 18639999a69SPedro F. Giffuni return (EINVAL); 18739999a69SPedro F. Giffuni 18839999a69SPedro F. Giffuni acl->acl_entry[n].ae_id = entry->ae_id; 18939999a69SPedro F. Giffuni break; 19039999a69SPedro F. Giffuni 19139999a69SPedro F. Giffuni default: 19239999a69SPedro F. Giffuni return (EINVAL); 19339999a69SPedro F. Giffuni } 19439999a69SPedro F. Giffuni } 19539999a69SPedro F. Giffuni 19639999a69SPedro F. Giffuni if (value != end) 19739999a69SPedro F. Giffuni return (EINVAL); 19839999a69SPedro F. Giffuni 19939999a69SPedro F. Giffuni acl->acl_cnt = count; 20039999a69SPedro F. Giffuni 20139999a69SPedro F. Giffuni return (0); 20239999a69SPedro F. Giffuni } 20339999a69SPedro F. Giffuni 20439999a69SPedro F. Giffuni static int 20539999a69SPedro F. Giffuni ext2_getacl_posix1e(struct vop_getacl_args *ap) 20639999a69SPedro F. Giffuni { 20739999a69SPedro F. Giffuni int attrnamespace; 20839999a69SPedro F. Giffuni const char *attrname; 20939999a69SPedro F. Giffuni char *value; 21039999a69SPedro F. Giffuni int len; 21139999a69SPedro F. Giffuni int error; 21239999a69SPedro F. Giffuni 21339999a69SPedro F. Giffuni switch (ap->a_type) { 21439999a69SPedro F. Giffuni case ACL_TYPE_DEFAULT: 21539999a69SPedro F. Giffuni attrnamespace = POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE; 21639999a69SPedro F. Giffuni attrname = POSIX1E_ACL_DEFAULT_EXTATTR_NAME; 21739999a69SPedro F. Giffuni break; 21839999a69SPedro F. Giffuni case ACL_TYPE_ACCESS: 21939999a69SPedro F. Giffuni attrnamespace = POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE; 22039999a69SPedro F. Giffuni attrname = POSIX1E_ACL_ACCESS_EXTATTR_NAME; 22139999a69SPedro F. Giffuni break; 22239999a69SPedro F. Giffuni default: 22339999a69SPedro F. Giffuni return (EINVAL); 22439999a69SPedro F. Giffuni } 22539999a69SPedro F. Giffuni 226*03222757SPedro F. Giffuni len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); 227*03222757SPedro F. Giffuni value = malloc(len, M_ACL, M_WAITOK); 228*03222757SPedro F. Giffuni if (!value) 229*03222757SPedro F. Giffuni return (ENOMEM); 230*03222757SPedro F. Giffuni 23139999a69SPedro F. Giffuni error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, attrnamespace, attrname, 23239999a69SPedro F. Giffuni &len, value, ap->a_td); 23339999a69SPedro F. Giffuni switch (error) { 23439999a69SPedro F. Giffuni case ENOATTR: 23539999a69SPedro F. Giffuni switch (ap->a_type) { 23639999a69SPedro F. Giffuni case ACL_TYPE_ACCESS: 23739999a69SPedro F. Giffuni ap->a_aclp->acl_cnt = 3; 23839999a69SPedro F. Giffuni ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ; 23939999a69SPedro F. Giffuni ap->a_aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID; 24039999a69SPedro F. Giffuni ap->a_aclp->acl_entry[0].ae_perm = ACL_PERM_NONE; 24139999a69SPedro F. Giffuni ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ; 24239999a69SPedro F. Giffuni ap->a_aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID; 24339999a69SPedro F. Giffuni ap->a_aclp->acl_entry[1].ae_perm = ACL_PERM_NONE; 24439999a69SPedro F. Giffuni ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER; 24539999a69SPedro F. Giffuni ap->a_aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID; 24639999a69SPedro F. Giffuni ap->a_aclp->acl_entry[2].ae_perm = ACL_PERM_NONE; 24739999a69SPedro F. Giffuni break; 24839999a69SPedro F. Giffuni 24939999a69SPedro F. Giffuni case ACL_TYPE_DEFAULT: 25039999a69SPedro F. Giffuni ap->a_aclp->acl_cnt = 0; 25139999a69SPedro F. Giffuni break; 25239999a69SPedro F. Giffuni } 25339999a69SPedro F. Giffuni case 0: 25439999a69SPedro F. Giffuni if (!error) { 25539999a69SPedro F. Giffuni error = ext4_acl_from_disk(value, len, ap->a_aclp); 25639999a69SPedro F. Giffuni if (error) 25739999a69SPedro F. Giffuni goto out; 25839999a69SPedro F. Giffuni } 25939999a69SPedro F. Giffuni 26039999a69SPedro F. Giffuni if (error == ENOATTR) 26139999a69SPedro F. Giffuni error = 0; 26239999a69SPedro F. Giffuni 26339999a69SPedro F. Giffuni if (ap->a_type == ACL_TYPE_ACCESS) 26439999a69SPedro F. Giffuni ext2_sync_acl_from_inode(VTOI(ap->a_vp), ap->a_aclp); 26539999a69SPedro F. Giffuni default: 26639999a69SPedro F. Giffuni break; 26739999a69SPedro F. Giffuni } 26839999a69SPedro F. Giffuni 26939999a69SPedro F. Giffuni out: 27039999a69SPedro F. Giffuni free(value, M_TEMP); 27139999a69SPedro F. Giffuni return (error); 27239999a69SPedro F. Giffuni } 27339999a69SPedro F. Giffuni 27439999a69SPedro F. Giffuni int 27539999a69SPedro F. Giffuni ext2_getacl(struct vop_getacl_args *ap) 27639999a69SPedro F. Giffuni { 27739999a69SPedro F. Giffuni 27839999a69SPedro F. Giffuni if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || 27939999a69SPedro F. Giffuni ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 1)) 28039999a69SPedro F. Giffuni return (EOPNOTSUPP); 28139999a69SPedro F. Giffuni 28239999a69SPedro F. Giffuni if (ap->a_type == ACL_TYPE_NFS4) 28339999a69SPedro F. Giffuni return (ENOTSUP); 28439999a69SPedro F. Giffuni 28539999a69SPedro F. Giffuni return (ext2_getacl_posix1e(ap)); 28639999a69SPedro F. Giffuni } 28739999a69SPedro F. Giffuni 28839999a69SPedro F. Giffuni /* 28939999a69SPedro F. Giffuni * Convert from in-memory to filesystem representation. 29039999a69SPedro F. Giffuni */ 29139999a69SPedro F. Giffuni static int 29239999a69SPedro F. Giffuni ext4_acl_to_disk(const struct acl *acl, size_t *size, char *value) 29339999a69SPedro F. Giffuni { 29439999a69SPedro F. Giffuni struct ext2_acl_header *ext_acl; 29539999a69SPedro F. Giffuni int disk_size; 29639999a69SPedro F. Giffuni char *e; 29739999a69SPedro F. Giffuni size_t n; 29839999a69SPedro F. Giffuni 29939999a69SPedro F. Giffuni if (acl->acl_cnt <= 4) 30039999a69SPedro F. Giffuni disk_size = sizeof(struct ext2_acl_header) + 30139999a69SPedro F. Giffuni acl->acl_cnt * sizeof(struct ext2_acl_entry_short); 30239999a69SPedro F. Giffuni else 30339999a69SPedro F. Giffuni disk_size = sizeof(struct ext2_acl_header) + 30439999a69SPedro F. Giffuni 4 * sizeof(struct ext2_acl_entry_short) + 30539999a69SPedro F. Giffuni (acl->acl_cnt - 4) * sizeof(struct ext2_acl_entry); 30639999a69SPedro F. Giffuni 30739999a69SPedro F. Giffuni if (disk_size > *size) 30839999a69SPedro F. Giffuni return (EINVAL); 30939999a69SPedro F. Giffuni 31039999a69SPedro F. Giffuni *size = disk_size; 31139999a69SPedro F. Giffuni ext_acl = (struct ext2_acl_header *)value; 31239999a69SPedro F. Giffuni 31339999a69SPedro F. Giffuni ext_acl->a_version = EXT4_ACL_VERSION; 31439999a69SPedro F. Giffuni e = (char *)ext_acl + sizeof(struct ext2_acl_header); 31539999a69SPedro F. Giffuni for (n = 0; n < acl->acl_cnt; n++) { 31639999a69SPedro F. Giffuni const struct acl_entry *acl_e = &acl->acl_entry[n]; 31739999a69SPedro F. Giffuni struct ext2_acl_entry *entry = (struct ext2_acl_entry *)e; 31839999a69SPedro F. Giffuni entry->ae_tag = acl_e->ae_tag; 31939999a69SPedro F. Giffuni entry->ae_perm = acl_e->ae_perm; 32039999a69SPedro F. Giffuni switch (acl_e->ae_tag) { 32139999a69SPedro F. Giffuni case ACL_USER: 32239999a69SPedro F. Giffuni entry->ae_id = acl_e->ae_id; 32339999a69SPedro F. Giffuni e += sizeof(struct ext2_acl_entry); 32439999a69SPedro F. Giffuni break; 32539999a69SPedro F. Giffuni 32639999a69SPedro F. Giffuni case ACL_GROUP: 32739999a69SPedro F. Giffuni entry->ae_id = acl_e->ae_id; 32839999a69SPedro F. Giffuni e += sizeof(struct ext2_acl_entry); 32939999a69SPedro F. Giffuni break; 33039999a69SPedro F. Giffuni 33139999a69SPedro F. Giffuni case ACL_USER_OBJ: 33239999a69SPedro F. Giffuni case ACL_GROUP_OBJ: 33339999a69SPedro F. Giffuni case ACL_MASK: 33439999a69SPedro F. Giffuni case ACL_OTHER: 33539999a69SPedro F. Giffuni e += sizeof(struct ext2_acl_entry_short); 33639999a69SPedro F. Giffuni break; 33739999a69SPedro F. Giffuni 33839999a69SPedro F. Giffuni default: 33939999a69SPedro F. Giffuni return (EINVAL); 34039999a69SPedro F. Giffuni } 34139999a69SPedro F. Giffuni } 34239999a69SPedro F. Giffuni 34339999a69SPedro F. Giffuni return (0); 34439999a69SPedro F. Giffuni } 34539999a69SPedro F. Giffuni 34639999a69SPedro F. Giffuni static int 34739999a69SPedro F. Giffuni ext2_setacl_posix1e(struct vop_setacl_args *ap) 34839999a69SPedro F. Giffuni { 34939999a69SPedro F. Giffuni struct inode *ip = VTOI(ap->a_vp); 35039999a69SPedro F. Giffuni char *value; 35139999a69SPedro F. Giffuni size_t len; 35239999a69SPedro F. Giffuni int error; 35339999a69SPedro F. Giffuni 35439999a69SPedro F. Giffuni if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 35539999a69SPedro F. Giffuni return (EINVAL); 35639999a69SPedro F. Giffuni 35739999a69SPedro F. Giffuni /* 35839999a69SPedro F. Giffuni * If this is a set operation rather than a delete operation, 35939999a69SPedro F. Giffuni * invoke VOP_ACLCHECK() on the passed ACL to determine if it is 36039999a69SPedro F. Giffuni * valid for the target. This will include a check on ap->a_type. 36139999a69SPedro F. Giffuni */ 36239999a69SPedro F. Giffuni if (ap->a_aclp != NULL) { 36339999a69SPedro F. Giffuni /* 36439999a69SPedro F. Giffuni * Set operation. 36539999a69SPedro F. Giffuni */ 36639999a69SPedro F. Giffuni error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, 36739999a69SPedro F. Giffuni ap->a_cred, ap->a_td); 36839999a69SPedro F. Giffuni if (error) 36939999a69SPedro F. Giffuni return (error); 37039999a69SPedro F. Giffuni } else { 37139999a69SPedro F. Giffuni /* 37239999a69SPedro F. Giffuni * Delete operation. 37339999a69SPedro F. Giffuni * POSIX.1e allows only deletion of the default ACL on a 37439999a69SPedro F. Giffuni * directory (ACL_TYPE_DEFAULT). 37539999a69SPedro F. Giffuni */ 37639999a69SPedro F. Giffuni if (ap->a_type != ACL_TYPE_DEFAULT) 37739999a69SPedro F. Giffuni return (EINVAL); 37839999a69SPedro F. Giffuni if (ap->a_vp->v_type != VDIR) 37939999a69SPedro F. Giffuni return (ENOTDIR); 38039999a69SPedro F. Giffuni } 38139999a69SPedro F. Giffuni 38239999a69SPedro F. Giffuni if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 38339999a69SPedro F. Giffuni return (EROFS); 38439999a69SPedro F. Giffuni 38539999a69SPedro F. Giffuni /* 38639999a69SPedro F. Giffuni * Authorize the ACL operation. 38739999a69SPedro F. Giffuni */ 38839999a69SPedro F. Giffuni if (ip->i_flags & (IMMUTABLE | APPEND)) 38939999a69SPedro F. Giffuni return (EPERM); 39039999a69SPedro F. Giffuni 39139999a69SPedro F. Giffuni /* 39239999a69SPedro F. Giffuni * Must hold VADMIN (be file owner) or have appropriate privilege. 39339999a69SPedro F. Giffuni */ 39439999a69SPedro F. Giffuni if ((error = VOP_ACCESS(ap->a_vp, VADMIN, ap->a_cred, ap->a_td))) 39539999a69SPedro F. Giffuni return (error); 39639999a69SPedro F. Giffuni 39739999a69SPedro F. Giffuni switch (ap->a_type) { 39839999a69SPedro F. Giffuni case ACL_TYPE_ACCESS: 39939999a69SPedro F. Giffuni len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); 40039999a69SPedro F. Giffuni value = malloc(len, M_ACL, M_WAITOK | M_ZERO); 40139999a69SPedro F. Giffuni error = ext4_acl_to_disk(ap->a_aclp, &len, value); 40239999a69SPedro F. Giffuni if (error == 0) 40339999a69SPedro F. Giffuni error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, 40439999a69SPedro F. Giffuni POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, 40539999a69SPedro F. Giffuni POSIX1E_ACL_ACCESS_EXTATTR_NAME, len, 40639999a69SPedro F. Giffuni value, ap->a_td); 40739999a69SPedro F. Giffuni 40839999a69SPedro F. Giffuni free(value, M_ACL); 40939999a69SPedro F. Giffuni break; 41039999a69SPedro F. Giffuni 41139999a69SPedro F. Giffuni case ACL_TYPE_DEFAULT: 41239999a69SPedro F. Giffuni if (ap->a_aclp == NULL) { 41339999a69SPedro F. Giffuni error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED, 41439999a69SPedro F. Giffuni POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 41539999a69SPedro F. Giffuni POSIX1E_ACL_DEFAULT_EXTATTR_NAME, ap->a_td); 41639999a69SPedro F. Giffuni 41739999a69SPedro F. Giffuni /* 41839999a69SPedro F. Giffuni * Attempting to delete a non-present default ACL 41939999a69SPedro F. Giffuni * will return success for portability purposes. 42039999a69SPedro F. Giffuni * (TRIX) 42139999a69SPedro F. Giffuni * 42239999a69SPedro F. Giffuni * XXX: Note that since we can't distinguish 42339999a69SPedro F. Giffuni * "that EA is not supported" from "that EA is not 42439999a69SPedro F. Giffuni * defined", the success case here overlaps the 42539999a69SPedro F. Giffuni * the ENOATTR->EOPNOTSUPP case below. 42639999a69SPedro F. Giffuni */ 42739999a69SPedro F. Giffuni if (error == ENOATTR) 42839999a69SPedro F. Giffuni error = 0; 42939999a69SPedro F. Giffuni } else { 43039999a69SPedro F. Giffuni len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); 43139999a69SPedro F. Giffuni value = malloc(len, M_ACL, M_WAITOK | M_ZERO); 43239999a69SPedro F. Giffuni error = ext4_acl_to_disk(ap->a_aclp, &len, value); 43339999a69SPedro F. Giffuni if (error == 0) 43439999a69SPedro F. Giffuni error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, 43539999a69SPedro F. Giffuni POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 43639999a69SPedro F. Giffuni POSIX1E_ACL_DEFAULT_EXTATTR_NAME, len, 43739999a69SPedro F. Giffuni value, ap->a_td); 43839999a69SPedro F. Giffuni 43939999a69SPedro F. Giffuni free(value, M_ACL); 44039999a69SPedro F. Giffuni } 44139999a69SPedro F. Giffuni break; 44239999a69SPedro F. Giffuni 44339999a69SPedro F. Giffuni default: 44439999a69SPedro F. Giffuni error = EINVAL; 44539999a69SPedro F. Giffuni } 44639999a69SPedro F. Giffuni 44739999a69SPedro F. Giffuni /* 44839999a69SPedro F. Giffuni * Map lack of attribute definition in UFS_EXTATTR into lack of 44939999a69SPedro F. Giffuni * support for ACLs on the filesystem. 45039999a69SPedro F. Giffuni */ 45139999a69SPedro F. Giffuni if (error == ENOATTR) 45239999a69SPedro F. Giffuni return (EOPNOTSUPP); 45339999a69SPedro F. Giffuni 45439999a69SPedro F. Giffuni if (error != 0) 45539999a69SPedro F. Giffuni return (error); 45639999a69SPedro F. Giffuni 45739999a69SPedro F. Giffuni if (ap->a_type == ACL_TYPE_ACCESS) { 45839999a69SPedro F. Giffuni /* 45939999a69SPedro F. Giffuni * Now that the EA is successfully updated, update the 46039999a69SPedro F. Giffuni * inode and mark it as changed. 46139999a69SPedro F. Giffuni */ 46239999a69SPedro F. Giffuni ext2_sync_inode_from_acl(ap->a_aclp, ip); 46339999a69SPedro F. Giffuni ip->i_flag |= IN_CHANGE; 46439999a69SPedro F. Giffuni error = ext2_update(ip->i_vnode, 1); 46539999a69SPedro F. Giffuni } 46639999a69SPedro F. Giffuni 46739999a69SPedro F. Giffuni VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB); 46839999a69SPedro F. Giffuni 46939999a69SPedro F. Giffuni return (error); 47039999a69SPedro F. Giffuni } 47139999a69SPedro F. Giffuni 47239999a69SPedro F. Giffuni int 47339999a69SPedro F. Giffuni ext2_setacl(struct vop_setacl_args *ap) 47439999a69SPedro F. Giffuni { 47539999a69SPedro F. Giffuni if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || 47639999a69SPedro F. Giffuni ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 1)) 47739999a69SPedro F. Giffuni return (EOPNOTSUPP); 47839999a69SPedro F. Giffuni 47939999a69SPedro F. Giffuni if (ap->a_type == ACL_TYPE_NFS4) 48039999a69SPedro F. Giffuni return (ENOTSUP); 48139999a69SPedro F. Giffuni 48239999a69SPedro F. Giffuni return (ext2_setacl_posix1e(ap)); 48339999a69SPedro F. Giffuni } 48439999a69SPedro F. Giffuni 48539999a69SPedro F. Giffuni /* 48639999a69SPedro F. Giffuni * Check the validity of an ACL for a file. 48739999a69SPedro F. Giffuni */ 48839999a69SPedro F. Giffuni int 48939999a69SPedro F. Giffuni ext2_aclcheck(struct vop_aclcheck_args *ap) 49039999a69SPedro F. Giffuni { 49139999a69SPedro F. Giffuni 49239999a69SPedro F. Giffuni if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || 49339999a69SPedro F. Giffuni ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 1)) 49439999a69SPedro F. Giffuni return (EOPNOTSUPP); 49539999a69SPedro F. Giffuni 49639999a69SPedro F. Giffuni if (ap->a_type == ACL_TYPE_NFS4) 49739999a69SPedro F. Giffuni return (ENOTSUP); 49839999a69SPedro F. Giffuni 49939999a69SPedro F. Giffuni if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 50039999a69SPedro F. Giffuni return (EINVAL); 50139999a69SPedro F. Giffuni 50239999a69SPedro F. Giffuni /* 50339999a69SPedro F. Giffuni * Verify we understand this type of ACL, and that it applies 50439999a69SPedro F. Giffuni * to this kind of object. 50539999a69SPedro F. Giffuni * Rely on the acl_posix1e_check() routine to verify the contents. 50639999a69SPedro F. Giffuni */ 50739999a69SPedro F. Giffuni switch (ap->a_type) { 50839999a69SPedro F. Giffuni case ACL_TYPE_ACCESS: 50939999a69SPedro F. Giffuni break; 51039999a69SPedro F. Giffuni 51139999a69SPedro F. Giffuni case ACL_TYPE_DEFAULT: 51239999a69SPedro F. Giffuni if (ap->a_vp->v_type != VDIR) 51339999a69SPedro F. Giffuni return (EINVAL); 51439999a69SPedro F. Giffuni break; 51539999a69SPedro F. Giffuni 51639999a69SPedro F. Giffuni default: 51739999a69SPedro F. Giffuni return (EINVAL); 51839999a69SPedro F. Giffuni } 51939999a69SPedro F. Giffuni 52039999a69SPedro F. Giffuni return (acl_posix1e_check(ap->a_aclp)); 52139999a69SPedro F. Giffuni } 522