Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 20 Aug 2017 01:08:23 +0000 (UTC)
From:      "Pedro F. Giffuni" <pfg@FreeBSD.org>
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
Message-ID:  <201708200108.v7K18NYH021054@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <fs/ext2fs/ext2_dinode.h>
 #include <fs/ext2fs/ext2_mount.h>
 
+#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 <fs/ext2fs/ext2_dinode.h>
 #include <fs/ext2fs/ext2_mount.h>
 #include <fs/ext2fs/ext2_extattr.h>
+#include <fs/ext2fs/ext2_extern.h>
 
+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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201708200108.v7K18NYH021054>