From owner-svn-src-head@freebsd.org Sun May 28 15:39:13 2017 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 5A0E3D84F83; Sun, 28 May 2017 15:39:13 +0000 (UTC) (envelope-from pfg@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id F414F19C8; Sun, 28 May 2017 15:39:12 +0000 (UTC) (envelope-from pfg@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v4SFdCMe063162; Sun, 28 May 2017 15:39:12 GMT (envelope-from pfg@FreeBSD.org) Received: (from pfg@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v4SFdBKA063157; Sun, 28 May 2017 15:39:11 GMT (envelope-from pfg@FreeBSD.org) Message-Id: <201705281539.v4SFdBKA063157@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: pfg set sender to pfg@FreeBSD.org using -f From: "Pedro F. Giffuni" Date: Sun, 28 May 2017 15:39:11 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r319071 - in head/sys: conf fs/ext2fs modules/ext2fs X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 28 May 2017 15:39:13 -0000 Author: pfg Date: Sun May 28 15:39:11 2017 New Revision: 319071 URL: https://svnweb.freebsd.org/changeset/base/319071 Log: Support for linux ext2fs posix-draft ACLs. This is closely tied to the Extended Attribute implementation. Submitted by: Fedor Uporov Reviewed by: kevlo, pfg Differential Revision: https://reviews.freebsd.org/D10807 Added: head/sys/fs/ext2fs/ext2_acl.c (contents, props changed) head/sys/fs/ext2fs/ext2_acl.h (contents, props changed) Modified: head/sys/conf/files head/sys/fs/ext2fs/ext2_extattr.c head/sys/fs/ext2fs/ext2_vnops.c head/sys/modules/ext2fs/Makefile Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Sun May 28 12:14:33 2017 (r319070) +++ head/sys/conf/files Sun May 28 15:39:11 2017 (r319071) @@ -3525,6 +3525,7 @@ geom/virstor/binstream.c optional geom_v 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 Added: head/sys/fs/ext2fs/ext2_acl.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/fs/ext2fs/ext2_acl.c Sun May 28 15:39:11 2017 (r319071) @@ -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 Added: head/sys/fs/ext2fs/ext2_acl.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/fs/ext2fs/ext2_acl.h Sun May 28 15:39:11 2017 (r319071) @@ -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_ */ Modified: head/sys/fs/ext2fs/ext2_extattr.c ============================================================================== --- head/sys/fs/ext2fs/ext2_extattr.c Sun May 28 12:14:33 2017 (r319070) +++ head/sys/fs/ext2fs/ext2_extattr.c Sun May 28 15:39:11 2017 (r319071) @@ -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); + } + + /* + * XXX: Not all linux namespaces are mapped to bsd for now, + * return NULL, which will be converted to ENOTSUP on upper layer. + */ +#ifdef EXT2FS_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 @@ ext2_extattr_inode_list(struct inode *ip 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 @@ ext2_extattr_inode_list(struct inode *ip 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 @@ ext2_extattr_block_list(struct inode *ip 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 @@ ext2_extattr_block_list(struct inode *ip 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 @@ ext2_extattr_inode_get(struct inode *ip, 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 @@ ext2_extattr_inode_get(struct inode *ip, 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 @@ ext2_extattr_block_get(struct inode *ip, 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 @@ ext2_extattr_block_get(struct inode *ip, 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 @@ ext2_extattr_inode_delete(struct inode * 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 @@ ext2_extattr_inode_delete(struct inode * /* 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 @@ ext2_extattr_inode_delete(struct inode * 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 @@ ext2_extattr_block_delete(struct inode * 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 @@ ext2_extattr_block_delete(struct inode * /* 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 @@ ext2_extattr_block_delete(struct inode * 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 @@ static struct ext2fs_extattr_entry * 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 @@ ext2_extattr_inode_set(struct inode *ip, 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 @@ ext2_extattr_inode_set(struct inode *ip, /* Find if entry exist */ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***