[yocto] [meta-selinux][RFC 08/10] e2fsprogs: Add patch to copy xattr block from source file.

Philip Tricca flihp at twobit.us
Sat Jun 6 17:37:06 PDT 2015


Signed-off-by: Philip Tricca <flihp at twobit.us>
---
 .../e2fsprogs/misc-xattr-create-xattr-block.patch  | 351 +++++++++++++++++++++
 .../e2fsprogs/e2fsprogs_1.42.9.bbappend            |   1 +
 2 files changed, 352 insertions(+)
 create mode 100644 recipes-devtools/e2fsprogs/e2fsprogs/misc-xattr-create-xattr-block.patch

diff --git a/recipes-devtools/e2fsprogs/e2fsprogs/misc-xattr-create-xattr-block.patch b/recipes-devtools/e2fsprogs/e2fsprogs/misc-xattr-create-xattr-block.patch
new file mode 100644
index 0000000..b843f1b
--- /dev/null
+++ b/recipes-devtools/e2fsprogs/e2fsprogs/misc-xattr-create-xattr-block.patch
@@ -0,0 +1,351 @@
+To build the xattr disk block we process the output from listxattr and
+lgetxattr from the source file system object. This data is formated in a disk
+block according to the format specified in the kernel ext2 file system driver.
+See the comment block at the beginning of fs/ext2/xattr.c for details, a brief
+description follows but the kernel source file is authoritative.
+
+Creating the xattr disk block is pretty painful. Each block has an obligatory
+header. After this header is a series of structures each describing an entry
+(aka an attribute). This requires iterating over all entries for a file and
+writing a data structure into the block for each. The value for each entry (the
+actual data) is written to the block as well but these are written simply as
+padded data. Unlike entries which grow down into the block beginning
+immediately after the xattr header, values are written to the end of the xattr
+block growing back towards the entries.
+
+Currently we only process attributes with the 'security.' prefix as our use
+case is SELinux labels and IMA. Additional prefixes can likely be supported with
+minimal effort but none have been tested.
+
+Once the xattr block has been created it is written to disk. The xattr block is
+associated with the appropriate file system object through the i_file_acl inode
+member and the inode is updated on disk.
+
+Signed-off-by: Philip Tricca <flihp at twobit.us>
+
+Index: e2fsprogs-1.42.9/misc/xattr.c
+===================================================================
+--- e2fsprogs-1.42.9.orig/misc/xattr.c
++++ e2fsprogs-1.42.9/misc/xattr.c
+@@ -1,6 +1,23 @@
+ #include "xattr.h"
+ 
++#include <attr/xattr.h>
++#include <ctype.h>
++#include <errno.h>
++#include <ext2fs/ext2_ext_attr.h>
++#include <linux/xattr.h>
++#include <stdint.h>
+ #include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <unistd.h>
++
++#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
++#define HEADER(ptr) ((struct ext2_ext_attr_header *)(ptr))
++#define ENTRY(ptr)  ((struct ext2_ext_attr_entry *)(ptr))
++#define FIRST_ENTRY(ptr) ENTRY(HEADER(ptr) + 1)
++#define VALUE(hdr, ent) (((char*)hdr) + (ent->e_value_offs))
+ 
+ #ifdef XATTR_DEBUG
+ #define XATTR_STDERR(fmt, args...) fprintf (stderr, fmt, ##args)
+@@ -8,6 +25,28 @@
+ #define XATTR_STDERR(fmt, args...) do {} while (0)
+ #endif
+ 
++/* structure for mapping xattr name prefix data */
++typedef struct xattr_prefix {
++	int index;
++	char *name;
++	size_t length;
++} xattr_prefix_t;
++
++xattr_prefix_t xattr_prefixes [] = {
++/* Only interested in security prefix. Can support others though.
++	{
++		.index = EXT2_XATTR_INDEX_USER,
++		.name = XATTR_USER_PREFIX,
++		.length = XATTR_USER_PREFIX_LEN,
++	},
++*/
++	{
++		.index = EXT2_XATTR_INDEX_SECURITY,
++		.name = XATTR_SECURITY_PREFIX,
++		.length = XATTR_SECURITY_PREFIX_LEN,
++	},
++	{ 0 },
++};
+ 
+ /* Free remaining resources after all files have been processed. */
+ void
+@@ -16,6 +55,211 @@ xattr_cleanup ()
+ 	XATTR_STDERR ("Cleaning up resources from xattrs.\n");
+ }
+ 
++/* Get value for named xattr from file at path.
++ * Returns pointer to allocated block for value and length in length param.
++ * If no value, return NULL pointer and length of 0.
++ * On error return NULL pointer and length set to -1.
++ */
++static char*
++xattr_get_value (const char *path, const char *name, ssize_t *length)
++{
++	char *value = NULL;
++
++	*length = lgetxattr (path, name, NULL, 0);
++	if (*length == -1) {
++		com_err (__func__, errno, "lgetattr");
++		goto out;
++	}
++	if (*length == 0) {
++		fprintf (stderr, "xattr %s has value length 0\n", name);
++		goto out;
++	}
++	value = calloc (1, *length);
++	if (value == NULL) {
++		com_err (__func__, errno, "calloc");
++		goto out;
++	}
++	*length = lgetxattr (path, name, value, *length);
++	if (*length == -1) {
++		com_err (__func__, errno, "lgetattr");
++		goto value_err;
++	}
++out:
++	return value;
++
++value_err:
++	if (value)
++		free (value);
++	return NULL;
++}
++
++/* Get all attribute names for file at path. Return pointer to allocated memory
++ * block holding all names and the length through parameter size.
++ * If no xattrs: return NULL and set size to 0
++ * If error: return NULL and set size to -1
++ */
++static char*
++xattr_get_names (const char *path, ssize_t *size)
++{
++	char *names = NULL;
++
++	*size = llistxattr (path, NULL, 0);
++	if (*size == -1) {
++		com_err (__func__, errno, "llistxattr");
++		goto out;
++	}
++	if (*size == 0) {
++		/* no xattrs */
++		goto out;
++	}
++	names = calloc (1, *size);
++	if (names == NULL) {
++		com_err (__func__, errno, "calloc");
++		goto out;
++	}
++	*size = llistxattr (path, names, *size);
++	if (*size == -1) {
++		com_err (__func__, errno, "llistxattr");
++		goto cleanup;
++	}
++	if (*size == 0) {
++		fprintf (stdout, "Conflicting data, no xattrs for file: %s\n", path);
++		goto cleanup;
++	}
++out:
++	return names;
++
++cleanup:
++	if (names)
++		free (names);
++	return NULL;
++}
++
++/* return pointer to next string in xattr name block, don't go beyond length
++ */
++static inline char*
++next_name (char *name, size_t length)
++{
++	int i = 0;
++
++	for (i = 0; i < length; ++i)
++		if (name [i] == '\0') {
++			++i;
++			break;
++		}
++
++	return name + i;
++}
++
++/* Find entry in xattr_table with matching prefix. */
++static const xattr_prefix_t*
++xattr_find_prefix (char *name, size_t length)
++{
++	int i = 0;
++
++	XATTR_STDERR ("find_attr_data: searching for prefix from xattr name: %s\n", name);
++	for (i = 0; xattr_prefixes[i].index != 0; ++i) {
++		if (!strncmp (name, xattr_prefixes[i].name, MIN (length, xattr_prefixes[i].length))) {
++			XATTR_STDERR ("found match in table with index: %d\n", xattr_prefixes[i].index);
++			return &xattr_prefixes[i];
++		}
++	}
++	return NULL;
++}
++
++/* Query file at path for attributes. build up structure the file system
++ * expects of an extended attribute disk block (header parameter).
++ *
++ * The main loop walks through the xattr names one at a time. It gets the value
++ * for each named xattr and copies the data into the xattr block pointed to by
++ * the header parameter. To do this the loop also tracks the location of the
++ * associated entry and value. Values start at the end of the buffer and grow
++ * back towards the header while the entries start immediately after the header
++ * and grow towards the end of the block.
++ *
++ * See the comment block at the beginning of the xattr.c file in the ext2 file
++ * system code in the kernel: fs/ext2/xattr.c
++ * Assume the xattr block pointed to by header parameter is initialized to 0s.
++ */
++static int
++xattr_build_block (const char *path,
++		struct ext2_ext_attr_header **header,
++		size_t header_length)
++{
++	struct ext2_ext_attr_entry *entry = NULL;
++	char *names = NULL, *value = NULL, *name_curr = NULL;
++	ssize_t names_length = 0, value_length = 0;
++	size_t name_length = 0, value_index = 0, len_rem = 0;
++	const xattr_prefix_t *prefix = NULL;
++	int ret = 0;
++
++	XATTR_STDERR ("xattr_build_block for file: %s\n", path);
++	*header = NULL;
++	names = xattr_get_names (path, &names_length);
++	if (names == NULL) {
++		// no xattrs for file @ path or error
++		ret = names_length;
++		goto out;
++	}
++	*header = calloc (1, header_length);
++	if (*header == NULL) {
++		com_err (__func__, errno, "calloc");
++		goto out;
++	}
++	(*header)->h_magic = EXT2_EXT_ATTR_MAGIC;
++	(*header)->h_blocks = 1;
++	/* Values start at end of buffer. NOTE: It must be moved before a value can
++	 * be stored.
++	 */
++	value_index = header_length;
++	for (name_curr = names, entry = FIRST_ENTRY(*header), len_rem = names_length;
++		name_curr < names + names_length;
++		len_rem = names_length - (name_curr - names),
++			name_curr = next_name (name_curr, len_rem),
++			entry = EXT2_EXT_ATTR_NEXT(entry))
++	{
++		XATTR_STDERR ("xattr_build_block: processing xattr with name %s\n", name_curr);
++		name_length = strnlen (name_curr, len_rem);
++		prefix = xattr_find_prefix (name_curr, name_length);
++		if (!prefix) {
++			fprintf (stderr, "Don't currently handle xattr: %s\n", name_curr);
++			continue;
++		}
++		value = xattr_get_value (path, name_curr, &value_length);
++		if (value == NULL) {
++			// no xattr value or error
++			fprintf (stderr, "failed to get value, skipping\n");
++			goto next;
++		}
++		/* setup offsets and lengths for name and value */
++		entry->e_name_len = name_length - prefix->length;
++		entry->e_name_index = prefix->index;
++		/* Can't know these till we know the length of the value. */
++		entry->e_value_offs = value_index -= EXT2_EXT_ATTR_SIZE(value_length);
++		entry->e_value_size = value_length;
++		/* Check to be sure entry name and value don't overlap before copy. */
++		if (EXT2_EXT_ATTR_NAME(entry) + entry->e_name_len > VALUE(*header, entry)) {
++			fprintf (stderr, "xattr entry name and value overlap! Too much xattr data.");
++			ret = -1;
++			goto out;
++		}
++		/* copy name and value data then calculate the hash */
++		memcpy (EXT2_EXT_ATTR_NAME(entry),
++			name_curr + prefix->length,
++			entry->e_name_len);
++		memcpy (VALUE(*header, entry), value, entry->e_value_size);
++		entry->e_hash = ext2fs_ext_attr_hash_entry (entry, VALUE(*header, entry));
++next:
++		if (value)
++			free (value);
++	}
++	XATTR_STDERR ("xattr_build_block: done building xattr buffer\n");
++out:
++	if (names)
++		free (names);
++	return ret;
++}
++
+ /* This is the entry point to the xattr module. This function copies the xattrs
+  * from the file at 'path' to the file system object at 'ino'.
+  *
+@@ -28,7 +272,56 @@ errcode_t
+ set_inode_xattr (ext2_filsys fs, ext2_ino_t ino, const char *path)
+ {
+ 	errcode_t ret = 0;
++	char *buf = NULL;
++	struct ext2_inode inode = { 0 };
++	blk_t block = 0;
++	struct ext2_ext_attr_header *header = NULL;
++	uint32_t newcount = 0;
+ 
+ 	XATTR_STDERR ("Copying xattrs from %s to inode 0x%x.\n", path, ino);
++	/* Populate the xattr block for the file at path */
++	if (ret = xattr_build_block (path, &header, fs->blocksize)) {
++		goto out;
++	}
++	if (header == NULL) {
++		XATTR_STDERR ("set_inode_xattrs: no xattrs for %s\n", path);
++		goto out;
++	}
++	if (ret = ext2fs_read_inode (fs, ino, &inode)) {
++		com_err(__func__, ret, "ext2fs_read_inode");
++		goto out;
++	}
++	if (ret = ext2fs_alloc_block (fs, 0, NULL, &block)) {
++		com_err(__func__, ret, "ext2fs_alloc_block: returned %d", ret);
++		goto out;
++	}
++	ext2fs_mark_block_bitmap2 (fs->block_map, block);
++	XATTR_STDERR ("writing xattr block 0x%x to disk:\n", block);
++	if (ret = ext2fs_write_ext_attr (fs, block, header)) {
++		com_err(__func__, ret, "ext2fs_write_ext_attr: returned %d", ret);
++		goto out;
++	}
++	/* point inode for current file to xattr block, update block count and
++	 * write inode to disk
++	 */
++	inode.i_file_acl = block;
++	if (ret = ext2fs_adjust_ea_refcount2(fs,
++					block,
++					(char*)header,
++					1,
++					&newcount))
++	{
++		com_err(__func__, ret, "ext2fs_adjust_ea_refcount");
++		goto out;
++	}
++	if (ret = ext2fs_iblk_add_blocks (fs, &inode, 1)) {
++		com_err(__func__, ret, "ext2fs_iblk_add_blocks failed");
++		goto out;
++	}
++	if (ret = ext2fs_write_inode (fs, ino, &inode))
++		com_err(__func__, ret, "ext2fs_write_inode: returned %d", ret);
++out:
++	if (header)
++		free (header);
+ 	return ret;
+ }
diff --git a/recipes-devtools/e2fsprogs/e2fsprogs_1.42.9.bbappend b/recipes-devtools/e2fsprogs/e2fsprogs_1.42.9.bbappend
index 1bb9a36..d2cefbf 100644
--- a/recipes-devtools/e2fsprogs/e2fsprogs_1.42.9.bbappend
+++ b/recipes-devtools/e2fsprogs/e2fsprogs_1.42.9.bbappend
@@ -6,4 +6,5 @@ SRC_URI += " \
     file://misc-mke2fs.c-label-root-dir-and-cleanup.patch \
     file://misc-create_inode.c-label-fs-objects.patch \
     file://lib-ext2fs-ext2_ext_attr.h-add-xattr-index.patch \
+    file://misc-xattr-create-xattr-block.patch \
 "
-- 
2.1.4




More information about the yocto mailing list