From owner-svn-src-all@freebsd.org Sun Aug 20 01:08:25 2017 Return-Path: Delivered-To: svn-src-all@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 0F4FCDCFBBC; Sun, 20 Aug 2017 01:08:25 +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 A53096C681; Sun, 20 Aug 2017 01:08:24 +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 v7K18NnN021060; Sun, 20 Aug 2017 01:08:23 GMT (envelope-from pfg@FreeBSD.org) Received: (from pfg@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v7K18NYH021054; Sun, 20 Aug 2017 01:08:23 GMT (envelope-from pfg@FreeBSD.org) Message-Id: <201708200108.v7K18NYH021054@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: pfg set sender to pfg@FreeBSD.org using -f From: "Pedro F. Giffuni" Date: Sun, 20 Aug 2017 01:08:23 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r322711 - in stable/11/sys: conf fs/ext2fs modules/ext2fs X-SVN-Group: stable-11 X-SVN-Commit-Author: pfg X-SVN-Commit-Paths: in stable/11/sys: conf fs/ext2fs modules/ext2fs X-SVN-Commit-Revision: 322711 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 20 Aug 2017 01:08:25 -0000 Author: pfg Date: Sun Aug 20 01:08:23 2017 New Revision: 322711 URL: https://svnweb.freebsd.org/changeset/base/322711 Log: MFC r316341, r317779, r319071, r319077, r319557, r319558, r319827, r319829: ext2fs: add read-write support for Extended Attributes and linux ACLs. Extended attributes and their particular implementation in linux are different from FreeBSD so in this case we have started diverging from the UFS EA implementation, which would be the natural reference. Support for linux ext2fs posix-draft ACLs. Submitted by: Fedor Uporov Reviewed by: pfg Include fixes from: cem, pluknet Added: stable/11/sys/fs/ext2fs/ext2_acl.c - copied, changed from r319071, head/sys/fs/ext2fs/ext2_acl.c stable/11/sys/fs/ext2fs/ext2_acl.h - copied unchanged from r319071, head/sys/fs/ext2fs/ext2_acl.h stable/11/sys/fs/ext2fs/ext2_extattr.c - copied, changed from r316341, head/sys/fs/ext2fs/ext2_extattr.c stable/11/sys/fs/ext2fs/ext2_extattr.h - copied, changed from r316341, head/sys/fs/ext2fs/ext2_extattr.h Modified: stable/11/sys/conf/files stable/11/sys/fs/ext2fs/ext2_alloc.c stable/11/sys/fs/ext2fs/ext2_extern.h stable/11/sys/fs/ext2fs/ext2_inode.c stable/11/sys/fs/ext2fs/ext2_inode_cnv.c stable/11/sys/fs/ext2fs/ext2_vnops.c stable/11/sys/fs/ext2fs/ext2fs.h stable/11/sys/fs/ext2fs/inode.h stable/11/sys/modules/ext2fs/Makefile Directory Properties: stable/11/ (props changed) Modified: stable/11/sys/conf/files ============================================================================== --- stable/11/sys/conf/files Sun Aug 20 00:41:49 2017 (r322710) +++ stable/11/sys/conf/files Sun Aug 20 01:08:23 2017 (r322711) @@ -3329,9 +3329,11 @@ geom/virstor/binstream.c optional geom_virstor 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 +fs/ext2fs/ext2_extattr.c optional ext2fs fs/ext2fs/ext2_extents.c optional ext2fs fs/ext2fs/ext2_inode.c optional ext2fs fs/ext2fs/ext2_inode_cnv.c optional ext2fs Copied and modified: stable/11/sys/fs/ext2fs/ext2_acl.c (from r319071, head/sys/fs/ext2fs/ext2_acl.c) ============================================================================== --- head/sys/fs/ext2fs/ext2_acl.c Sun May 28 15:39:11 2017 (r319071, copy source) +++ stable/11/sys/fs/ext2fs/ext2_acl.c Sun Aug 20 01:08:23 2017 (r322711) @@ -49,6 +49,8 @@ #include #include +#ifdef UFS_ACL + void ext2_sync_acl_from_inode(struct inode *ip, struct acl *acl) { @@ -127,13 +129,18 @@ ext2_sync_inode_from_acl(struct acl *acl, struct inode static int ext4_acl_from_disk(char *value, size_t size, struct acl *acl) { - const char *end = value + size; + const char *end; int n, count, s; + if (value == NULL) + return (EINVAL); + + end = value + size; + if (((struct ext2_acl_header *)value)->a_version != EXT4_ACL_VERSION) return (EINVAL); - if (!value || size < sizeof(struct ext2_acl_header)) + if (size < sizeof(struct ext2_acl_header)) return (EINVAL); s = size - sizeof(struct ext2_acl_header); @@ -210,11 +217,6 @@ ext2_getacl_posix1e(struct vop_getacl_args *ap) 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; @@ -228,10 +230,14 @@ ext2_getacl_posix1e(struct vop_getacl_args *ap) return (EINVAL); } + len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); + value = malloc(len, M_ACL, M_WAITOK); + if (!value) + return (ENOMEM); + error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, attrnamespace, attrname, &len, value, ap->a_td); - switch (error) { - case ENOATTR: + if (error == ENOATTR) { switch (ap->a_type) { case ACL_TYPE_ACCESS: ap->a_aclp->acl_cnt = 3; @@ -250,22 +256,21 @@ ext2_getacl_posix1e(struct vop_getacl_args *ap) 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; - } + } else if (error != 0) + 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; + 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); + out: free(value, M_TEMP); return (error); @@ -276,7 +281,7 @@ 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)) + ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0)) return (EOPNOTSUPP); if (ap->a_type == ACL_TYPE_NFS4) @@ -473,7 +478,7 @@ 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)) + ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0)) return (EOPNOTSUPP); if (ap->a_type == ACL_TYPE_NFS4) @@ -490,7 +495,7 @@ 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)) + ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0)) return (EOPNOTSUPP); if (ap->a_type == ACL_TYPE_NFS4) @@ -518,4 +523,6 @@ ext2_aclcheck(struct vop_aclcheck_args *ap) } return (acl_posix1e_check(ap->a_aclp)); -} \ No newline at end of file +} + +#endif /* UFS_ACL */ Copied: stable/11/sys/fs/ext2fs/ext2_acl.h (from r319071, head/sys/fs/ext2fs/ext2_acl.h) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/11/sys/fs/ext2fs/ext2_acl.h Sun Aug 20 01:08:23 2017 (r322711, copy of r319071, head/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_ */ Modified: stable/11/sys/fs/ext2fs/ext2_alloc.c ============================================================================== --- stable/11/sys/fs/ext2fs/ext2_alloc.c Sun Aug 20 00:41:49 2017 (r322710) +++ stable/11/sys/fs/ext2fs/ext2_alloc.c Sun Aug 20 01:08:23 2017 (r322711) @@ -132,6 +132,25 @@ nospace: } /* + * Allocate EA's block for inode. + */ +daddr_t +ext2_allocfacl(struct inode *ip) +{ + struct m_ext2fs *fs; + daddr_t facl; + + fs = ip->i_e2fs; + + EXT2_LOCK(ip->i_ump); + facl = ext2_alloccg(ip, ino_to_cg(fs, ip->i_number), 0, fs->e2fs_bsize); + if (0 == facl) + EXT2_UNLOCK(ip->i_ump); + + return (facl); +} + +/* * Reallocate a sequence of blocks into a contiguous sequence of blocks. * * The vnode and an array of buffer pointers for a range of sequential Copied and modified: stable/11/sys/fs/ext2fs/ext2_extattr.c (from r316341, head/sys/fs/ext2fs/ext2_extattr.c) ============================================================================== --- head/sys/fs/ext2fs/ext2_extattr.c Sat Apr 1 01:00:36 2017 (r316341, copy source) +++ stable/11/sys/fs/ext2fs/ext2_extattr.c Sun Aug 20 01:08:23 2017 (r322711) @@ -44,24 +44,126 @@ #include #include #include +#include +static int +ext2_extattr_attrnamespace_to_bsd(int attrnamespace) +{ + 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 + + return (NULL); +} + static int -ext2_extattr_index_to_bsd(int index) +ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name) { - switch (index) { - case EXT4_XATTR_INDEX_USER: - return EXTATTR_NAMESPACE_USER; - case EXT4_XATTR_INDEX_SYSTEM: - return EXTATTR_NAMESPACE_SYSTEM; + if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE && + !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) + return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT); - default: - return EXTATTR_NAMESPACE_EMPTY; + 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) +{ + if (attrnamespace == EXTATTR_NAMESPACE_EMPTY) + return (EINVAL); + + if (strlen(attrname) == 0) + return (EINVAL); + + if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX) + return (ENAMETOOLONG); + + return (0); +} + +static int +ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end) +{ + struct ext2fs_extattr_entry *next; + + while (!EXT2_IS_LAST_ENTRY(entry)) { + next = EXT2_EXTATTR_NEXT(entry); + if ((char *)next >= end) + return (EIO); + + entry = next; + } + + return (0); +} + +int ext2_extattr_inode_list(struct inode *ip, int attrnamespace, struct uio *uio, size_t *size) { @@ -69,8 +171,8 @@ ext2_extattr_inode_list(struct inode *ip, int attrname struct buf *bp; struct ext2fs_extattr_dinode_header *header; struct ext2fs_extattr_entry *entry; - struct ext2fs_extattr_entry *next; - char *end; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -95,38 +197,43 @@ ext2_extattr_inode_list(struct inode *ip, int attrname return (0); } - /* Check attributes integrity */ - entry = EXT2_IFIRST(header); - end = (char *)dinode + EXT2_INODE_SIZE(fs); - while (!EXT2_IS_LAST_ENTRY(entry)) { - next = EXT2_EXTATTR_NEXT(entry); - if ((char *)next >= end) { - brelse(bp); - return (EIO); - } - - entry = next; + error = ext2_extattr_check(EXT2_IFIRST(header), + (char *)dinode + EXT2_INODE_SIZE(fs)); + if (error) { + brelse(bp); + return (error); } 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) + entry = EXT2_EXTATTR_NEXT(entry)) { + 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; } } brelse(bp); - return (0); + return (error); } int @@ -137,8 +244,8 @@ ext2_extattr_block_list(struct inode *ip, int attrname struct buf *bp; struct ext2fs_extattr_header *header; struct ext2fs_extattr_entry *entry; - struct ext2fs_extattr_entry *next; - char *end; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -157,38 +264,42 @@ ext2_extattr_block_list(struct inode *ip, int attrname return (EINVAL); } - /* Check attributes integrity */ - end = bp->b_data + bp->b_bufsize; - entry = EXT2_FIRST_ENTRY(bp); - while (!EXT2_IS_LAST_ENTRY(entry)) { - next = EXT2_EXTATTR_NEXT(entry); - if ((char *)next >= end) { - brelse(bp); - return (EIO); - } - - entry = next; + error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); + if (error) { + brelse(bp); + return (error); } 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; } } brelse(bp); - return (0); + return (error); } int @@ -199,8 +310,8 @@ ext2_extattr_inode_get(struct inode *ip, int attrnames struct buf *bp; struct ext2fs_extattr_dinode_header *header; struct ext2fs_extattr_entry *entry; - struct ext2fs_extattr_entry *next; - char *end; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -222,45 +333,47 @@ ext2_extattr_inode_get(struct inode *ip, int attrnames if (header->h_magic != EXTATTR_MAGIC) { brelse(bp); - return (0); + return (ENOATTR); } - /* Check attributes integrity */ - entry = EXT2_IFIRST(header); - end = (char *)dinode + EXT2_INODE_SIZE(fs); - while (!EXT2_IS_LAST_ENTRY(entry)) { - next = EXT2_EXTATTR_NEXT(entry); - if ((char *)next >= end) { - brelse(bp); - return (EIO); - } - - entry = next; + error = ext2_extattr_check(EXT2_IFIRST(header), + (char *)dinode + EXT2_INODE_SIZE(fs)); + if (error) { + brelse(bp); + return (error); } 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 { - error = uiomove(((char *)EXT2_IFIRST(header)) + entry->e_value_offs, - entry->e_value_size, uio); - if (error) { - brelse(bp); - return (error); - } + error = uiomove(((char *)EXT2_IFIRST(header)) + + entry->e_value_offs, entry->e_value_size, uio); } + + brelse(bp); + return (error); } } brelse(bp); - return (0); + return (ENOATTR); } int @@ -271,8 +384,8 @@ ext2_extattr_block_get(struct inode *ip, int attrnames struct buf *bp; struct ext2fs_extattr_header *header; struct ext2fs_extattr_entry *entry; - struct ext2fs_extattr_entry *next; - char *end; + const char *attr_name; + int name_len; int error; fs = ip->i_e2fs; @@ -291,40 +404,822 @@ ext2_extattr_block_get(struct inode *ip, int attrnames return (EINVAL); } - /* Check attributes integrity */ - end = bp->b_data + bp->b_bufsize; - entry = EXT2_FIRST_ENTRY(bp); - while (!EXT2_IS_LAST_ENTRY(entry)) { - next = EXT2_EXTATTR_NEXT(entry); - if ((char *)next >= end) { - brelse(bp); - return (EIO); - } - - entry = next; + error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); + if (error) { + brelse(bp); + return (error); } 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 { error = uiomove(bp->b_data + entry->e_value_offs, entry->e_value_size, uio); - if (error) { - brelse(bp); - return (error); - } } + + brelse(bp); + return (error); } } brelse(bp); + + return (ENOATTR); +} + +static uint16_t +ext2_extattr_delete_value(char *off, + struct ext2fs_extattr_entry *first_entry, + struct ext2fs_extattr_entry *entry, char *end) +{ + uint16_t min_offs; + struct ext2fs_extattr_entry *next; + + min_offs = end - off; + next = first_entry; + while (!EXT2_IS_LAST_ENTRY(next)) { + if (min_offs > next->e_value_offs && next->e_value_offs > 0) + min_offs = next->e_value_offs; + + next = EXT2_EXTATTR_NEXT(next); + } + + if (entry->e_value_size == 0) + return (min_offs); + + memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size), + off + min_offs, entry->e_value_offs - min_offs); + + /* Adjust all value offsets */ + next = first_entry; + while (!EXT2_IS_LAST_ENTRY(next)) + { + if (next->e_value_offs > 0 && + next->e_value_offs < entry->e_value_offs) + next->e_value_offs += + EXT2_EXTATTR_SIZE(entry->e_value_size); + + next = EXT2_EXTATTR_NEXT(next); + } + + min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size); + + return (min_offs); +} + +static void +ext2_extattr_delete_entry(char *off, + struct ext2fs_extattr_entry *first_entry, + struct ext2fs_extattr_entry *entry, char *end) +{ + char *pad; + struct ext2fs_extattr_entry *next; + + /* Clean entry value */ + ext2_extattr_delete_value(off, first_entry, entry, end); + + /* Clean the entry */ + next = first_entry; + while (!EXT2_IS_LAST_ENTRY(next)) + next = EXT2_EXTATTR_NEXT(next); + + pad = (char*)next + sizeof(uint32_t); + + memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len), + pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len))); +} + +int +ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name) +{ + struct m_ext2fs *fs; + 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; + + if ((error = bread(ip->i_devvp, + fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), + (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { + brelse(bp); + return (error); + } + + struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) + ((char *)bp->b_data + + EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); + + /* Check attributes magic value */ + header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + + E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); + + if (header->h_magic != EXTATTR_MAGIC) { + brelse(bp); + return (ENOATTR); + } + + error = ext2_extattr_check(EXT2_IFIRST(header), + (char *)dinode + EXT2_INODE_SIZE(fs)); + if (error) { + brelse(bp); + return (error); + } + + /* If I am last entry, just make magic zero */ + entry = EXT2_IFIRST(header); + 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)); + } + } + + for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); + entry = EXT2_EXTATTR_NEXT(entry)) { + 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 (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)); + + return (bwrite(bp)); + } + } + + brelse(bp); + + return (ENOATTR); +} + +static int +ext2_extattr_block_clone(struct inode *ip, struct buf **bpp) +{ + struct m_ext2fs *fs; + struct buf *sbp; + struct buf *cbp; + struct ext2fs_extattr_header *header; + uint64_t facl; + + fs = ip->i_e2fs; + sbp = *bpp; + + header = EXT2_HDR(sbp); + if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1) + return (EINVAL); + + facl = ext2_allocfacl(ip); + if (!facl) + return (ENOSPC); + + cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0); + if (!cbp) { + ext2_blkfree(ip, facl, fs->e2fs_bsize); + return (EIO); + } + + memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize); + header->h_refcount--; + bwrite(sbp); + + ip->i_facl = facl; + ext2_update(ip->i_vnode, 1); + + header = EXT2_HDR(cbp); + header->h_refcount = 1; + + *bpp = cbp; + + return (0); +} + +int +ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name) +{ + struct m_ext2fs *fs; + 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; + + error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), + fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + + /* Check attributes magic value */ + header = EXT2_HDR(bp); + if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { + brelse(bp); + return (EINVAL); + } + + error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); + if (error) { + brelse(bp); + return (error); + } + + if (header->h_refcount > 1) { + error = ext2_extattr_block_clone(ip, &bp); + if (error) { + brelse(bp); + return (error); + } + } + + /* 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)) && + (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; + error = ext2_update(ip->i_vnode, 1); + + brelse(bp); + return (error); + } + } + + for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); + entry = EXT2_EXTATTR_NEXT(entry)) { + 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 (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); + + return (bwrite(bp)); + } + } + + brelse(bp); + + return (ENOATTR); +} + +static struct ext2fs_extattr_entry * +allocate_entry(const char *name, int attrnamespace, uint16_t offs, + uint32_t size, uint32_t hash) +{ + const char *attr_name; + int name_len; + struct ext2fs_extattr_entry *entry; + + 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_attrnamespace_to_linux(attrnamespace, name); + entry->e_value_offs = offs; + entry->e_value_block = 0; + entry->e_value_size = size; + entry->e_hash = hash; + memcpy(entry->e_name, name, name_len); + + return (entry); +} + +static void +free_entry(struct ext2fs_extattr_entry *entry) +{ + + free(entry, M_TEMP); +} + +static int +ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry, + struct ext2fs_extattr_entry *exist_entry, int header_size, + int name_len, int new_size) +{ + struct ext2fs_extattr_entry *entry; + int size; + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***