svn commit: r319071 - in head/sys: conf fs/ext2fs modules/ext2fs

Pedro F. Giffuni pfg at FreeBSD.org
Sun May 28 15:39:13 UTC 2017


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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/vnode.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/endian.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/extattr.h>
+
+#include <fs/ext2fs/fs.h>
+#include <fs/ext2fs/ext2fs.h>
+#include <fs/ext2fs/inode.h>
+#include <fs/ext2fs/ext2_acl.h>
+#include <fs/ext2fs/ext2_extattr.h>
+#include <fs/ext2fs/ext2_extern.h>
+#include <fs/ext2fs/ext2_dinode.h>
+#include <fs/ext2fs/ext2_mount.h>
+
+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 <fs/ext2fs/ext2_extattr.h>
 #include <fs/ext2fs/ext2_extern.h>
 
-
 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 ***


More information about the svn-src-all mailing list