aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-devtools/e2fsprogs/e2fsprogs/misc-xattr-create-xattr-block.patch
blob: 5955b447fa84dd461a01e6d54708b22229af55c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
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.

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@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;
 }