Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3525,6 +3525,7 @@ geom/virstor/g_virstor.c optional geom_virstor geom/virstor/g_virstor_md.c optional geom_virstor geom/zero/g_zero.c optional geom_zero +fs/ext2fs/ext2_acl.c optional ext2fs fs/ext2fs/ext2_alloc.c optional ext2fs fs/ext2fs/ext2_balloc.c optional ext2fs fs/ext2fs/ext2_bmap.c optional ext2fs Index: sys/fs/ext2fs/ext2_acl.h =================================================================== --- /dev/null +++ sys/fs/ext2fs/ext2_acl.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2017, Fedor Uporov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FS_EXT2FS_EXT2_ACL_H_ +#define _FS_EXT2FS_EXT2_ACL_H_ + +#define EXT4_ACL_VERSION 0x0001 + +struct ext2_acl_entry { + int16_t ae_tag; + int16_t ae_perm; + int32_t ae_id; +}; + +struct ext2_acl_entry_short { + int16_t ae_tag; + int16_t ae_perm; +}; + +struct ext2_acl_header { + int32_t a_version; +}; + +void ext2_sync_acl_from_inode(struct inode *ip, struct acl *acl); + +int ext2_getacl(struct vop_getacl_args *); +int ext2_setacl(struct vop_setacl_args *); +int ext2_aclcheck(struct vop_aclcheck_args *); + +#endif /* !_FS_EXT2FS_EXT2_ACL_H_ */ Index: sys/fs/ext2fs/ext2_acl.c =================================================================== --- /dev/null +++ sys/fs/ext2fs/ext2_acl.c @@ -0,0 +1,521 @@ +/*- + * Copyright (c) 2017, Fedor Uporov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +void +ext2_sync_acl_from_inode(struct inode *ip, struct acl *acl) +{ + struct acl_entry *acl_mask, *acl_group_obj; + int i; + + /* + * Update ACL_USER_OBJ, ACL_OTHER, but simply identify ACL_MASK + * and ACL_GROUP_OBJ for use after we know whether ACL_MASK is + * present. + */ + acl_mask = NULL; + acl_group_obj = NULL; + for (i = 0; i < acl->acl_cnt; i++) { + switch (acl->acl_entry[i].ae_tag) { + case ACL_USER_OBJ: + acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( + ACL_USER_OBJ, ip->i_mode); + acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; + break; + + case ACL_GROUP_OBJ: + acl_group_obj = &acl->acl_entry[i]; + acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; + break; + + case ACL_OTHER: + acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( + ACL_OTHER, ip->i_mode); + acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; + break; + + case ACL_MASK: + acl_mask = &acl->acl_entry[i]; + acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + break; + + default: + panic("ext2_sync_acl_from_inode(): bad ae_tag"); + } + } + + if (acl_group_obj == NULL) + panic("ext2_sync_acl_from_inode(): no ACL_GROUP_OBJ"); + + if (acl_mask == NULL) { + /* + * There is no ACL_MASK, so update ACL_GROUP_OBJ. + */ + acl_group_obj->ae_perm = acl_posix1e_mode_to_perm( + ACL_GROUP_OBJ, ip->i_mode); + } else { + /* + * Update the ACL_MASK entry instead of ACL_GROUP_OBJ. + */ + acl_mask->ae_perm = acl_posix1e_mode_to_perm(ACL_GROUP_OBJ, + ip->i_mode); + } +} + +static void +ext2_sync_inode_from_acl(struct acl *acl, struct inode *ip) +{ + + ip->i_mode &= ACL_PRESERVE_MASK; + ip->i_mode |= acl_posix1e_acl_to_mode(acl); +} + +/* + * Convert from filesystem to in-memory representation. + */ +static int +ext4_acl_from_disk(char *value, size_t size, struct acl *acl) +{ + const char *end = value + size; + int n, count, s; + + if (((struct ext2_acl_header *)value)->a_version != EXT4_ACL_VERSION) + return (EINVAL); + + if (!value || size < sizeof(struct ext2_acl_header)) + return (EINVAL); + + s = size - sizeof(struct ext2_acl_header); + s -= 4 * sizeof(struct ext2_acl_entry_short); + if (s < 0) + if ((size - sizeof(struct ext2_acl_header)) % + sizeof(struct ext2_acl_entry_short)) + count = -1; + else + count = (size - sizeof(struct ext2_acl_header)) / + sizeof(struct ext2_acl_entry_short); + else + if (s % sizeof(struct ext2_acl_entry)) + count = -1; + else + count = s / sizeof(struct ext2_acl_entry) + 4; + + if (count <= 0 || count > acl->acl_maxcnt) + return (EINVAL); + + value = value + sizeof(struct ext2_acl_header); + + for (n = 0; n < count; n++) { + struct ext2_acl_entry *entry = (struct ext2_acl_entry *)value; + if ((char *)value + sizeof(struct ext2_acl_entry_short) > end) + return (EINVAL); + + acl->acl_entry[n].ae_tag = entry->ae_tag; + acl->acl_entry[n].ae_perm = entry->ae_perm; + + switch (acl->acl_entry[n].ae_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value = (char *)value + sizeof(struct ext2_acl_entry_short); + break; + + case ACL_USER: + value = (char *)value + sizeof(struct ext2_acl_entry); + if ((char *)value > end) + return (EINVAL); + + acl->acl_entry[n].ae_id = entry->ae_id; + break; + + case ACL_GROUP: + value = (char *)value + sizeof(struct ext2_acl_entry); + if ((char *)value > end) + return (EINVAL); + + acl->acl_entry[n].ae_id = entry->ae_id; + break; + + default: + return (EINVAL); + } + } + + if (value != end) + return (EINVAL); + + acl->acl_cnt = count; + + return (0); +} + +static int +ext2_getacl_posix1e(struct vop_getacl_args *ap) +{ + int attrnamespace; + const char *attrname; + char *value; + int len; + int error; + + len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); + value = malloc(len, M_ACL, M_WAITOK); + if (!value) + return (ENOMEM); + + switch (ap->a_type) { + case ACL_TYPE_DEFAULT: + attrnamespace = POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE; + attrname = POSIX1E_ACL_DEFAULT_EXTATTR_NAME; + break; + case ACL_TYPE_ACCESS: + attrnamespace = POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE; + attrname = POSIX1E_ACL_ACCESS_EXTATTR_NAME; + break; + default: + return (EINVAL); + } + + error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, attrnamespace, attrname, + &len, value, ap->a_td); + switch (error) { + case ENOATTR: + switch (ap->a_type) { + case ACL_TYPE_ACCESS: + ap->a_aclp->acl_cnt = 3; + ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ; + ap->a_aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID; + ap->a_aclp->acl_entry[0].ae_perm = ACL_PERM_NONE; + ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ; + ap->a_aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID; + ap->a_aclp->acl_entry[1].ae_perm = ACL_PERM_NONE; + ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER; + ap->a_aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID; + ap->a_aclp->acl_entry[2].ae_perm = ACL_PERM_NONE; + break; + + case ACL_TYPE_DEFAULT: + ap->a_aclp->acl_cnt = 0; + break; + } + case 0: + if (!error) { + error = ext4_acl_from_disk(value, len, ap->a_aclp); + if (error) + goto out; + } + + if (error == ENOATTR) + error = 0; + + if (ap->a_type == ACL_TYPE_ACCESS) + ext2_sync_acl_from_inode(VTOI(ap->a_vp), ap->a_aclp); + default: + break; + } + +out: + free(value, M_TEMP); + return (error); +} + +int +ext2_getacl(struct vop_getacl_args *ap) +{ + + if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || + ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 1)) + return (EOPNOTSUPP); + + if (ap->a_type == ACL_TYPE_NFS4) + return (ENOTSUP); + + return (ext2_getacl_posix1e(ap)); +} + +/* + * Convert from in-memory to filesystem representation. + */ +static int +ext4_acl_to_disk(const struct acl *acl, size_t *size, char *value) +{ + struct ext2_acl_header *ext_acl; + int disk_size; + char *e; + size_t n; + + if (acl->acl_cnt <= 4) + disk_size = sizeof(struct ext2_acl_header) + + acl->acl_cnt * sizeof(struct ext2_acl_entry_short); + else + disk_size = sizeof(struct ext2_acl_header) + + 4 * sizeof(struct ext2_acl_entry_short) + + (acl->acl_cnt - 4) * sizeof(struct ext2_acl_entry); + + if (disk_size > *size) + return (EINVAL); + + *size = disk_size; + ext_acl = (struct ext2_acl_header *)value; + + ext_acl->a_version = EXT4_ACL_VERSION; + e = (char *)ext_acl + sizeof(struct ext2_acl_header); + for (n = 0; n < acl->acl_cnt; n++) { + const struct acl_entry *acl_e = &acl->acl_entry[n]; + struct ext2_acl_entry *entry = (struct ext2_acl_entry *)e; + entry->ae_tag = acl_e->ae_tag; + entry->ae_perm = acl_e->ae_perm; + switch (acl_e->ae_tag) { + case ACL_USER: + entry->ae_id = acl_e->ae_id; + e += sizeof(struct ext2_acl_entry); + break; + + case ACL_GROUP: + entry->ae_id = acl_e->ae_id; + e += sizeof(struct ext2_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(struct ext2_acl_entry_short); + break; + + default: + return (EINVAL); + } + } + + return (0); +} + +static int +ext2_setacl_posix1e(struct vop_setacl_args *ap) +{ + struct inode *ip = VTOI(ap->a_vp); + char *value; + size_t len; + int error; + + if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) + return (EINVAL); + + /* + * If this is a set operation rather than a delete operation, + * invoke VOP_ACLCHECK() on the passed ACL to determine if it is + * valid for the target. This will include a check on ap->a_type. + */ + if (ap->a_aclp != NULL) { + /* + * Set operation. + */ + error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, + ap->a_cred, ap->a_td); + if (error) + return (error); + } else { + /* + * Delete operation. + * POSIX.1e allows only deletion of the default ACL on a + * directory (ACL_TYPE_DEFAULT). + */ + if (ap->a_type != ACL_TYPE_DEFAULT) + return (EINVAL); + if (ap->a_vp->v_type != VDIR) + return (ENOTDIR); + } + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + + /* + * Authorize the ACL operation. + */ + if (ip->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); + + /* + * Must hold VADMIN (be file owner) or have appropriate privilege. + */ + if ((error = VOP_ACCESS(ap->a_vp, VADMIN, ap->a_cred, ap->a_td))) + return (error); + + switch (ap->a_type) { + case ACL_TYPE_ACCESS: + len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); + value = malloc(len, M_ACL, M_WAITOK | M_ZERO); + error = ext4_acl_to_disk(ap->a_aclp, &len, value); + if (error == 0) + error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, + POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, + POSIX1E_ACL_ACCESS_EXTATTR_NAME, len, + value, ap->a_td); + + free(value, M_ACL); + break; + + case ACL_TYPE_DEFAULT: + if (ap->a_aclp == NULL) { + error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED, + POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, + POSIX1E_ACL_DEFAULT_EXTATTR_NAME, ap->a_td); + + /* + * Attempting to delete a non-present default ACL + * will return success for portability purposes. + * (TRIX) + * + * XXX: Note that since we can't distinguish + * "that EA is not supported" from "that EA is not + * defined", the success case here overlaps the + * the ENOATTR->EOPNOTSUPP case below. + */ + if (error == ENOATTR) + error = 0; + } else { + len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); + value = malloc(len, M_ACL, M_WAITOK | M_ZERO); + error = ext4_acl_to_disk(ap->a_aclp, &len, value); + if (error == 0) + error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, + POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, + POSIX1E_ACL_DEFAULT_EXTATTR_NAME, len, + value, ap->a_td); + + free(value, M_ACL); + } + break; + + default: + error = EINVAL; + } + + /* + * Map lack of attribute definition in UFS_EXTATTR into lack of + * support for ACLs on the filesystem. + */ + if (error == ENOATTR) + return (EOPNOTSUPP); + + if (error != 0) + return (error); + + if (ap->a_type == ACL_TYPE_ACCESS) { + /* + * Now that the EA is successfully updated, update the + * inode and mark it as changed. + */ + ext2_sync_inode_from_acl(ap->a_aclp, ip); + ip->i_flag |= IN_CHANGE; + error = ext2_update(ip->i_vnode, 1); + } + + VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB); + + return (error); +} + +int +ext2_setacl(struct vop_setacl_args *ap) +{ + if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || + ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 1)) + return (EOPNOTSUPP); + + if (ap->a_type == ACL_TYPE_NFS4) + return (ENOTSUP); + + return (ext2_setacl_posix1e(ap)); +} + +/* + * Check the validity of an ACL for a file. + */ +int +ext2_aclcheck(struct vop_aclcheck_args *ap) +{ + + if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || + ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 1)) + return (EOPNOTSUPP); + + if (ap->a_type == ACL_TYPE_NFS4) + return (ENOTSUP); + + if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) + return (EINVAL); + + /* + * Verify we understand this type of ACL, and that it applies + * to this kind of object. + * Rely on the acl_posix1e_check() routine to verify the contents. + */ + switch (ap->a_type) { + case ACL_TYPE_ACCESS: + break; + + case ACL_TYPE_DEFAULT: + if (ap->a_vp->v_type != VDIR) + return (EINVAL); + break; + + default: + return (EINVAL); + } + + return (acl_posix1e_check(ap->a_aclp)); +} \ No newline at end of file Index: sys/fs/ext2fs/ext2_extattr.c =================================================================== --- sys/fs/ext2fs/ext2_extattr.c +++ sys/fs/ext2fs/ext2_extattr.c @@ -46,35 +46,92 @@ #include #include - static int -ext2_extattr_index_to_bsd(int index) +ext2_extattr_attrnamespace_to_bsd(int attrnamespace) { - switch (index) { - case EXT4_XATTR_INDEX_SYSTEM: - return (EXTATTR_NAMESPACE_SYSTEM); - case EXT4_XATTR_INDEX_USER: - return (EXTATTR_NAMESPACE_USER); + switch (attrnamespace) { + case EXT4_XATTR_INDEX_SYSTEM: + return (EXTATTR_NAMESPACE_SYSTEM); + + case EXT4_XATTR_INDEX_USER: + return (EXTATTR_NAMESPACE_USER); + + case EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT: + return (POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE); + + case EXT4_XATTR_INDEX_POSIX_ACL_ACCESS: + return (POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE); } return (EXTATTR_NAMESPACE_EMPTY); } +static const char * +ext2_extattr_name_to_bsd(int attrnamespace, const char *name, int* name_len) +{ + + if (attrnamespace == EXT4_XATTR_INDEX_SYSTEM) + return (name); + else if (attrnamespace == EXT4_XATTR_INDEX_USER) + return (name); + else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT) { + *name_len = strlen(POSIX1E_ACL_DEFAULT_EXTATTR_NAME); + return (POSIX1E_ACL_DEFAULT_EXTATTR_NAME); + } else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_ACCESS) { + *name_len = strlen(POSIX1E_ACL_ACCESS_EXTATTR_NAME); + return (POSIX1E_ACL_ACCESS_EXTATTR_NAME); + } + + /* + * Not all linux namespaces are mapped to bsd for now, + * return NULL, which will be converted to ENOTSUP on upper layer + */ +#ifdef DEBUG + printf("can not convert ext2fs name to bsd: namespace=%d\n", attrnamespace); +#endif /* DEBUG */ + + return (NULL); +} + static int -ext2_extattr_index_to_linux(int index) +ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name) { - switch (index) { - case EXTATTR_NAMESPACE_SYSTEM: - return (EXT4_XATTR_INDEX_SYSTEM); - case EXTATTR_NAMESPACE_USER: - return (EXT4_XATTR_INDEX_USER); + if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE && + !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) + return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT); + + if (attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE && + !strcmp(name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) + return (EXT4_XATTR_INDEX_POSIX_ACL_ACCESS); + + switch (attrnamespace) { + case EXTATTR_NAMESPACE_SYSTEM: + return (EXT4_XATTR_INDEX_SYSTEM); + + case EXTATTR_NAMESPACE_USER: + return (EXT4_XATTR_INDEX_USER); } + /* + * In this case namespace conversion should be unique, + * so this point is unreachable. + */ return (-1); } +static const char * +ext2_extattr_name_to_linux(int attrnamespace, const char *name) +{ + + if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE || + attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE) + return (""); + else + return (name); +} + int ext2_extattr_valid_attrname(int attrnamespace, const char *attrname) { @@ -114,6 +171,8 @@ struct buf *bp; struct ext2fs_extattr_dinode_header *header; struct ext2fs_extattr_entry *entry; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -147,17 +206,26 @@ for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); entry = EXT2_EXTATTR_NEXT(entry)) { - if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) + if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) != + attrnamespace) continue; + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + if (uio == NULL) - *size += entry->e_name_len + 1; + *size += name_len + 1; else { - char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK); - attr_name[0] = entry->e_name_len; - memcpy(&attr_name[1], entry->e_name, entry->e_name_len); - error = uiomove(attr_name, entry->e_name_len + 1, uio); - free(attr_name, M_TEMP); + char *name = malloc(name_len + 1, M_TEMP, M_WAITOK); + name[0] = name_len; + memcpy(&name[1], attr_name, name_len); + error = uiomove(name, name_len + 1, uio); + free(name, M_TEMP); if (error) break; } @@ -176,6 +244,8 @@ struct buf *bp; struct ext2fs_extattr_header *header; struct ext2fs_extattr_entry *entry; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -202,17 +272,26 @@ for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); entry = EXT2_EXTATTR_NEXT(entry)) { - if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) + if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) != + attrnamespace) continue; + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + if (uio == NULL) - *size += entry->e_name_len + 1; + *size += name_len + 1; else { - char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK); - attr_name[0] = entry->e_name_len; - memcpy(&attr_name[1], entry->e_name, entry->e_name_len); - error = uiomove(attr_name, entry->e_name_len + 1, uio); - free(attr_name, M_TEMP); + char *name = malloc(name_len + 1, M_TEMP, M_WAITOK); + name[0] = name_len; + memcpy(&name[1], attr_name, name_len); + error = uiomove(name, name_len + 1, uio); + free(name, M_TEMP); if (error) break; } @@ -231,6 +310,8 @@ struct buf *bp; struct ext2fs_extattr_dinode_header *header; struct ext2fs_extattr_entry *entry; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -264,11 +345,20 @@ for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); entry = EXT2_EXTATTR_NEXT(entry)) { - if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) + if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) != + attrnamespace) continue; - if (strlen(name) == entry->e_name_len && - 0 == strncmp(entry->e_name, name, entry->e_name_len)) { + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + + if (strlen(name) == name_len && + 0 == strncmp(attr_name, name, name_len)) { if (uio == NULL) *size += entry->e_value_size; else { @@ -294,6 +384,8 @@ struct buf *bp; struct ext2fs_extattr_header *header; struct ext2fs_extattr_entry *entry; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -320,11 +412,20 @@ for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); entry = EXT2_EXTATTR_NEXT(entry)) { - if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) + if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) != + attrnamespace) continue; - if (strlen(name) == entry->e_name_len && - 0 == strncmp(entry->e_name, name, entry->e_name_len)) { + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + + if (strlen(name) == name_len && + 0 == strncmp(attr_name, name, name_len)) { if (uio == NULL) *size += entry->e_value_size; else { @@ -411,6 +512,8 @@ struct buf *bp; struct ext2fs_extattr_dinode_header *header; struct ext2fs_extattr_entry *entry; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -444,9 +547,20 @@ /* If I am last entry, just make magic zero */ entry = EXT2_IFIRST(header); - if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) { - if (strlen(name) == entry->e_name_len && - 0 == strncmp(entry->e_name, name, entry->e_name_len)) { + if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) && + (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) == + attrnamespace)) { + + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + + if (strlen(name) == name_len && + 0 == strncmp(attr_name, name, name_len)) { memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header)); return (bwrite(bp)); @@ -455,11 +569,20 @@ for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); entry = EXT2_EXTATTR_NEXT(entry)) { - if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) + if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) != + attrnamespace) continue; - if (strlen(name) == entry->e_name_len && - 0 == strncmp(entry->e_name, name, entry->e_name_len)) { + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + + if (strlen(name) == name_len && + 0 == strncmp(attr_name, name, name_len)) { ext2_extattr_delete_entry((char *)EXT2_IFIRST(header), EXT2_IFIRST(header), entry, (char *)dinode + EXT2_INODE_SIZE(fs)); @@ -521,6 +644,8 @@ struct buf *bp; struct ext2fs_extattr_header *header; struct ext2fs_extattr_entry *entry; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -555,9 +680,20 @@ /* If I am last entry, clean me and free the block */ entry = EXT2_FIRST_ENTRY(bp); - if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) { - if (strlen(name) == entry->e_name_len && - 0 == strncmp(entry->e_name, name, entry->e_name_len)) { + if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) && + (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) == + attrnamespace)) { + + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + + if (strlen(name) == name_len && + 0 == strncmp(attr_name, name, name_len)) { ip->i_blocks -= btodb(fs->e2fs_bsize); ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize); ip->i_facl = 0; @@ -570,11 +706,20 @@ for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); entry = EXT2_EXTATTR_NEXT(entry)) { - if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) + if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) != + attrnamespace) continue; - if (strlen(name) == entry->e_name_len && - 0 == strncmp(entry->e_name, name, entry->e_name_len)) { + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + + if (strlen(name) == name_len && + 0 == strncmp(attr_name, name, name_len)) { ext2_extattr_delete_entry(bp->b_data, EXT2_FIRST_ENTRY(bp), entry, bp->b_data + bp->b_bufsize); @@ -592,15 +737,18 @@ allocate_entry(const char *name, int attrnamespace, uint16_t offs, uint32_t size, uint32_t hash) { - size_t name_len; + const char *attr_name; + int name_len; struct ext2fs_extattr_entry *entry; - name_len = strlen(name); + attr_name = ext2_extattr_name_to_linux(attrnamespace, name); + name_len = strlen(attr_name); + entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len, M_TEMP, M_WAITOK); entry->e_name_len = name_len; - entry->e_name_index = ext2_extattr_index_to_linux(attrnamespace); + entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name); entry->e_value_offs = offs; entry->e_value_block = 0; entry->e_value_size = size; @@ -727,6 +875,8 @@ struct buf *bp; struct ext2fs_extattr_dinode_header *header; struct ext2fs_extattr_entry *entry; + const char *attr_name; + int name_len; size_t size = 0, max_size; int error; @@ -762,11 +912,20 @@ /* Find if entry exist */ for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); entry = EXT2_EXTATTR_NEXT(entry)) { - if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) + if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) != + attrnamespace) continue; - if (strlen(name) == entry->e_name_len && - 0 == strncmp(entry->e_name, name, entry->e_name_len)) + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + + if (strlen(name) == name_len && + 0 == strncmp(attr_name, name, name_len)) break; } @@ -876,6 +1035,8 @@ struct buf *bp; struct ext2fs_extattr_header *header; struct ext2fs_extattr_entry *entry; + const char *attr_name; + int name_len; size_t size; int error; @@ -916,11 +1077,20 @@ /* Find if entry exist */ for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); entry = EXT2_EXTATTR_NEXT(entry)) { - if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) + if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) != + attrnamespace) continue; - if (strlen(name) == entry->e_name_len && - 0 == strncmp(entry->e_name, name, entry->e_name_len)) + name_len = entry->e_name_len; + attr_name = ext2_extattr_name_to_bsd(entry->e_name_index, + entry->e_name, &name_len); + if (!attr_name) { + brelse(bp); + return (ENOTSUP); + } + + if (strlen(name) == name_len && + 0 == strncmp(attr_name, name, name_len)) break; } @@ -961,7 +1131,8 @@ } size = ext2_extattr_get_size(NULL, NULL, - sizeof(struct ext2fs_extattr_header), strlen(name), uio->uio_resid); + sizeof(struct ext2fs_extattr_header), + strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid); if (size > fs->e2fs_bsize) return (ENOSPC); Index: sys/fs/ext2fs/ext2_vnops.c =================================================================== --- sys/fs/ext2fs/ext2_vnops.c +++ sys/fs/ext2fs/ext2_vnops.c @@ -81,6 +81,7 @@ #include #include +#include #include #include #include @@ -163,6 +164,9 @@ .vop_getextattr = ext2_getextattr, .vop_listextattr = ext2_listextattr, .vop_setextattr = ext2_setextattr, + .vop_getacl = ext2_getacl, + .vop_setacl = ext2_setacl, + .vop_aclcheck = ext2_aclcheck, .vop_vptofh = ext2_vptofh, }; @@ -1083,6 +1087,150 @@ return (error); } +static int +ext2_do_posix1e_acl_inheritance_dir(struct vnode *dvp, struct vnode *tvp, + mode_t dmode, struct ucred *cred, struct thread *td) +{ + int error; + struct inode *ip = VTOI(tvp); + struct acl *dacl, *acl; + + acl = acl_alloc(M_WAITOK); + dacl = acl_alloc(M_WAITOK); + + /* + * Retrieve default ACL from parent, if any. + */ + error = VOP_GETACL(dvp, ACL_TYPE_DEFAULT, acl, cred, td); + switch (error) { + case 0: + /* + * Retrieved a default ACL, so merge mode and ACL if + * necessary. If the ACL is empty, fall through to + * the "not defined or available" case. + */ + if (acl->acl_cnt != 0) { + dmode = acl_posix1e_newfilemode(dmode, acl); + ip->i_mode = dmode; + *dacl = *acl; + ext2_sync_acl_from_inode(ip, acl); + break; + } + /* FALLTHROUGH */ + + case EOPNOTSUPP: + /* + * Just use the mode as-is. + */ + ip->i_mode = dmode; + error = 0; + goto out; + + default: + goto out; + } + + error = VOP_SETACL(tvp, ACL_TYPE_ACCESS, acl, cred, td); + if (error == 0) + error = VOP_SETACL(tvp, ACL_TYPE_DEFAULT, dacl, cred, td); + switch (error) { + case 0: + break; + + case EOPNOTSUPP: + /* + * XXX: This should not happen, as EOPNOTSUPP above + * was supposed to free acl. + */ +#ifdef DEBUG + printf("ext2_mkdir: VOP_GETACL() but no VOP_SETACL()\n"); +#endif /* DEBUG */ + break; + + default: + goto out; + } + +out: + acl_free(acl); + acl_free(dacl); + + return (error); +} + +static int +ext2_do_posix1e_acl_inheritance_file(struct vnode *dvp, struct vnode *tvp, + mode_t mode, struct ucred *cred, struct thread *td) +{ + int error; + struct inode *ip = VTOI(tvp); + struct acl *acl; + + acl = acl_alloc(M_WAITOK); + + /* + * Retrieve default ACL for parent, if any. + */ + error = VOP_GETACL(dvp, ACL_TYPE_DEFAULT, acl, cred, td); + switch (error) { + case 0: + /* + * Retrieved a default ACL, so merge mode and ACL if + * necessary. + */ + if (acl->acl_cnt != 0) { + /* + * Two possible ways for default ACL to not + * be present. First, the EA can be + * undefined, or second, the default ACL can + * be blank. If it's blank, fall through to + * the it's not defined case. + */ + mode = acl_posix1e_newfilemode(mode, acl); + ip->i_mode = mode; + ext2_sync_acl_from_inode(ip, acl); + break; + } + /* FALLTHROUGH */ + + case EOPNOTSUPP: + /* + * Just use the mode as-is. + */ + ip->i_mode = mode; + error = 0; + goto out; + + default: + goto out; + } + + error = VOP_SETACL(tvp, ACL_TYPE_ACCESS, acl, cred, td); + switch (error) { + case 0: + break; + + case EOPNOTSUPP: + /* + * XXX: This should not happen, as EOPNOTSUPP above was + * supposed to free acl. + */ + printf("ufs_do_posix1e_acl_inheritance_file: VOP_GETACL() " + "but no VOP_SETACL()\n"); + /* panic("ufs_do_posix1e_acl_inheritance_file: VOP_GETACL() " + "but no VOP_SETACL()"); */ + break; + + default: + goto out; + } + +out: + acl_free(acl); + + return (error); +} + /* * Mkdir system call */ @@ -1192,6 +1340,13 @@ ip->i_flag |= IN_CHANGE; } + if (dvp->v_mount->mnt_flag & MNT_ACLS) { + error = ext2_do_posix1e_acl_inheritance_dir(dvp, tvp, dmode, + cnp->cn_cred, cnp->cn_thread); + if (error) + goto bad; + } + /* Directory set up, now install its entry in the parent directory. */ error = ext2_direnter(ip, dvp, cnp); if (error) { @@ -1446,6 +1601,18 @@ case _PC_NO_TRUNC: *ap->a_retval = 1; break; + case _PC_ACL_EXTENDED: + if (ap->a_vp->v_mount->mnt_flag & MNT_ACLS) + *ap->a_retval = 1; + else + *ap->a_retval = 0; + break; + case _PC_ACL_PATH_MAX: + if (ap->a_vp->v_mount->mnt_flag & MNT_ACLS) + *ap->a_retval = ACL_MAX_ENTRIES; + else + *ap->a_retval = 3; + break; case _PC_MIN_HOLE_SIZE: *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize; break; @@ -1513,6 +1680,8 @@ if (error) return (error); + error = ENOATTR; + if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE) { error = ext2_extattr_inode_delete(ip, ap->a_attrnamespace, ap->a_name); if (error != ENOATTR) @@ -1552,6 +1721,8 @@ if (ap->a_size != NULL) *ap->a_size = 0; + error = ENOATTR; + if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE) { error = ext2_extattr_inode_get(ip, ap->a_attrnamespace, ap->a_name, ap->a_uio, ap->a_size); @@ -1755,6 +1926,14 @@ error = ext2_update(tvp, !DOINGASYNC(tvp)); if (error) goto bad; + + if (dvp->v_mount->mnt_flag & MNT_ACLS) { + error = ext2_do_posix1e_acl_inheritance_file(dvp, tvp, mode, + cnp->cn_cred, cnp->cn_thread); + if (error) + goto bad; + } + error = ext2_direnter(ip, dvp, cnp); if (error) goto bad; Index: sys/modules/ext2fs/Makefile =================================================================== --- sys/modules/ext2fs/Makefile +++ sys/modules/ext2fs/Makefile @@ -3,8 +3,8 @@ .PATH: ${SRCTOP}/sys/fs/ext2fs KMOD= ext2fs SRCS= opt_ddb.h opt_directio.h opt_quota.h opt_suiddir.h vnode_if.h \ - ext2_alloc.c ext2_balloc.c ext2_bmap.c ext2_extattr.c ext2_extents.c \ - ext2_hash.c ext2_htree.c ext2_inode.c ext2_inode_cnv.c ext2_lookup.c \ - ext2_subr.c ext2_vfsops.c ext2_vnops.c + ext2_acl.c ext2_alloc.c ext2_balloc.c ext2_bmap.c ext2_extattr.c \ + ext2_extents.c ext2_hash.c ext2_htree.c ext2_inode.c ext2_inode_cnv.c \ + ext2_lookup.c ext2_subr.c ext2_vfsops.c ext2_vnops.c .include