2 * Copyright (c) 1999-2004
3 * Stelian Pop <stelian@popies.net>, 1999-2004
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 static const char rcsid[] =
32 "$Id: xattr.c,v 1.3 2005/06/08 13:24:12 stelian Exp $";
36 #include <compatlfs.h>
37 #include <compaterr.h>
38 #include <sys/types.h>
44 #include <bsdcompat.h>
45 #include <protocols/dumprestore.h>
48 #include "pathnames.h"
51 * Data structures below taken from the kernel
54 /* Maximum number of references to one attribute block */
55 #define EXT2_XATTR_REFCOUNT_MAX 1024
58 #define EXT2_XATTR_INDEX_MAX 10
59 #define EXT2_XATTR_INDEX_USER 1
60 #define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS 2
61 #define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT 3
62 #define EXT2_XATTR_INDEX_TRUSTED 4
63 #define EXT2_XATTR_INDEX_LUSTRE 5
64 #define EXT2_XATTR_INDEX_SECURITY 6
66 struct ext2_xattr_header {
67 u_int32_t h_magic; /* magic number for identification */
68 u_int32_t h_refcount; /* reference count */
69 u_int32_t h_blocks; /* number of disk blocks used */
70 u_int32_t h_hash; /* hash value of all attributes */
71 u_int32_t h_reserved[4]; /* zero right now */
74 struct ext3_xattr_ibody_header {
75 u_int32_t h_magic; /* magic number for identification */
78 struct ext2_xattr_entry {
79 u_char e_name_len; /* length of name */
80 u_char e_name_index; /* attribute name index */
81 u_int16_t e_value_offs; /* offset in disk block of value */
82 u_int32_t e_value_block; /* disk block attribute is stored on (n/i) */
83 u_int32_t e_value_size; /* size of attribute value */
84 u_int32_t e_hash; /* hash value of name and value */
85 char e_name[0]; /* attribute name */
88 #define EXT2_XATTR_PAD_BITS 2
89 #define EXT2_XATTR_PAD (1<<EXT2_XATTR_PAD_BITS)
90 #define EXT2_XATTR_ROUND (EXT2_XATTR_PAD-1)
91 #define EXT2_XATTR_LEN(name_len) \
92 (((name_len) + EXT2_XATTR_ROUND + \
93 sizeof(struct ext2_xattr_entry)) & ~EXT2_XATTR_ROUND)
94 #define EXT2_XATTR_NEXT(entry) \
95 ( (struct ext2_xattr_entry *)( \
96 (char *)(entry) + EXT2_XATTR_LEN((entry)->e_name_len)) )
97 #define EXT3_XATTR_SIZE(size) \
98 (((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND)
100 #define HDR(buffer) ((struct ext2_xattr_header *)(buffer))
101 #define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr))
102 #define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
104 #define BFIRST(buffer) ENTRY(HDR(buffer)+1)
105 #define IFIRST(buffer) ENTRY(((struct ext3_xattr_ibody_header *)(buffer))+1)
107 #define FIRST_ENTRY(buffer) \
108 ((HDR(buffer)->h_magic == EXT2_XATTR_MAGIC2) ? \
113 * On-block xattr value offsets start at the beginning of the block, but
114 * on-inode xattr value offsets start after the initial header
115 * (ext3_xattr_ibody_header).
117 #define VALUE_OFFSET(buffer, entry) \
118 (((HDR(buffer)->h_magic == EXT2_XATTR_MAGIC2) ? \
119 (entry)->e_value_offs + sizeof(struct ext3_xattr_ibody_header) : \
120 (entry)->e_value_offs))
123 * xattr syscalls do not exist yet in libc, get our own copy here,
124 * taken from libattr.
126 #if defined (__i386__)
127 # define HAVE_XATTR_SYSCALLS 1
128 # define __NR_lsetxattr 227
129 # define __NR_lgetxattr 230
130 # define __NR_llistxattr 233
131 #elif defined (__sparc__)
132 # define HAVE_XATTR_SYSCALLS 1
133 # define __NR_lsetxattr 170
134 # define __NR_lgetxattr 173
135 # define __NR_llistxattr 179
136 #elif defined (__ia64__)
137 # define HAVE_XATTR_SYSCALLS 1
138 # define __NR_lsetxattr 1218
139 # define __NR_lgetxattr 1221
140 # define __NR_llistxattr 1224
141 #elif defined (__powerpc__)
142 # define HAVE_XATTR_SYSCALLS 1
143 # define __NR_lsetxattr 210
144 # define __NR_lgetxattr 213
145 # define __NR_llistxattr 216
146 #elif defined (__x86_64__)
147 # define HAVE_XATTR_SYSCALLS 1
148 # define __NR_lsetxattr 189
149 # define __NR_lgetxattr 192
150 # define __NR_llistxattr 195
151 #elif defined (__s390__)
152 # define HAVE_XATTR_SYSCALLS 1
153 # define __NR_lsetxattr 225
154 # define __NR_lgetxattr 228
155 # define __NR_llistxattr 231
156 #elif defined (__arm__)
157 # define HAVE_XATTR_SYSCALLS 1
158 # define __NR_SYSCALL_BASE 0x900000
159 # define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
160 # define __NR_lgetxattr (__NR_SYSCALL_BASE+230)
161 # define __NR_llistxattr (__NR_SYSCALL_BASE+233)
162 #elif defined (__mips64__)
163 # define HAVE_XATTR_SYSCALLS 1
164 # define __NR_Linux 5000
165 # define __NR_lsetxattr (__NR_Linux + 218)
166 # define __NR_lgetxattr (__NR_Linux + 221)
167 # define __NR_llistxattr (__NR_Linux + 224)
168 #elif defined (__mips__)
169 # define HAVE_XATTR_SYSCALLS 1
170 # define __NR_Linux 4000
171 # define __NR_lsetxattr (__NR_Linux + 225)
172 # define __NR_lgetxattr (__NR_Linux + 228)
173 # define __NR_llistxattr (__NR_Linux + 231)
174 #elif defined (__alpha__)
175 # define HAVE_XATTR_SYSCALLS 1
176 # define __NR_lsetxattr 383
177 # define __NR_lgetxattr 386
178 # define __NR_llistxattr 389
179 #elif defined (__mc68000__)
180 # define HAVE_XATTR_SYSCALLS 1
181 # define __NR_lsetxattr 224
182 # define __NR_lgetxattr 227
183 # define __NR_llistxattr 230
185 # warning "Extended attribute syscalls undefined for this architecture"
186 # define HAVE_XATTR_SYSCALLS 0
189 #if HAVE_XATTR_SYSCALLS
190 # define SYSCALL(args...) syscall(args)
192 # define SYSCALL(args...) ( errno = ENOSYS, -1 )
195 static int lsetxattr __P((const char *, const char *, void *, size_t, int));
196 static ssize_t lgetxattr __P((const char *, const char *, void *, size_t));
197 static ssize_t llistxattr __P((const char *, char *, size_t));
198 static int xattr_cb_list __P((char *, char *, int, void *));
199 static int xattr_cb_set __P((char *, char *, int, void *));
200 static int xattr_cb_compare __P((char *, char *, int, void *));
201 static int xattr_verify __P((char *));
202 static int xattr_count __P((char *, int *));
203 static int xattr_walk __P((char *, int (*)(char *, char *, int, void *), void *));
206 lsetxattr(const char *path, const char *name, void *value, size_t size, int flags)
208 return SYSCALL(__NR_lsetxattr, path, name, value, size, flags);
212 lgetxattr(const char *path, const char *name, void *value, size_t size)
214 return SYSCALL(__NR_lgetxattr, path, name, value, size);
218 llistxattr(const char *path, char *list, size_t size)
220 return SYSCALL(__NR_llistxattr, path, list, size);
223 #define POSIX_ACL_XATTR_VERSION 0x0002
225 #define ACL_UNDEFINED_ID (-1)
227 #define ACL_USER_OBJ (0x01)
228 #define ACL_USER (0x02)
229 #define ACL_GROUP_OBJ (0x04)
230 #define ACL_GROUP (0x08)
231 #define ACL_MASK (0x10)
232 #define ACL_OTHER (0x20)
238 } posix_acl_xattr_entry;
242 posix_acl_xattr_entry a_entries[0];
243 } posix_acl_xattr_header;
246 posix_acl_xattr_size(int count)
248 return (sizeof(posix_acl_xattr_header) +
249 (count * sizeof(posix_acl_xattr_entry)));
252 struct posix_acl_entry {
254 unsigned short e_perm;
259 unsigned int a_count;
260 struct posix_acl_entry a_entries[0];
263 #define EXT3_ACL_VERSION 0x0001
274 } ext3_acl_entry_short;
280 static inline int ext3_acl_count(size_t size)
283 size -= sizeof(ext3_acl_header);
284 s = size - 4 * sizeof(ext3_acl_entry_short);
286 if (size % sizeof(ext3_acl_entry_short))
288 return size / sizeof(ext3_acl_entry_short);
290 if (s % sizeof(ext3_acl_entry))
292 return s / sizeof(ext3_acl_entry) + 4;
297 posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size) {
298 posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer;
299 posix_acl_xattr_entry *ext_entry = ext_acl->a_entries;
302 real_size = posix_acl_xattr_size(acl->a_count);
305 if (real_size > size) {
306 fprintf(stderr, "ACL: not enough space to convert (%d %d)\n", real_size, size);
310 ext_acl->a_version = POSIX_ACL_XATTR_VERSION;
311 #if BYTE_ORDER == BIG_ENDIAN
312 swabst("1i", (u_char *)ext_acl);
315 for (n=0; n < acl->a_count; n++, ext_entry++) {
316 ext_entry->e_tag = acl->a_entries[n].e_tag;
317 ext_entry->e_perm = acl->a_entries[n].e_perm;
318 ext_entry->e_id = acl->a_entries[n].e_id;
319 #if BYTE_ORDER == BIG_ENDIAN
320 swabst("2s1i", (u_char *)ext_entry);
326 static struct posix_acl *
327 ext3_acl_from_disk(const void *value, size_t size)
329 const char *end = (char *)value + size;
331 struct posix_acl *acl;
335 if (size < sizeof(ext3_acl_header)) {
336 fprintf(stderr, "ACL size too little\n");
339 #if BYTE_ORDER == BIG_ENDIAN
340 swabst("1i", (u_char *)value);
342 if (((ext3_acl_header *)value)->a_version != EXT3_ACL_VERSION) {
343 fprintf(stderr, "ACL version unknown\n");
346 value = (char *)value + sizeof(ext3_acl_header);
347 count = ext3_acl_count(size);
349 fprintf(stderr, "ACL bad count\n");
354 acl = malloc(sizeof(struct posix_acl) + count * sizeof(struct posix_acl_entry));
356 fprintf(stderr, "ACL malloc failed\n");
359 acl->a_count = count;
361 for (n=0; n < count; n++) {
362 ext3_acl_entry *entry = (ext3_acl_entry *)value;
363 #if BYTE_ORDER == BIG_ENDIAN
364 swabst("2s", (u_char *)entry);
366 if ((char *)value + sizeof(ext3_acl_entry_short) > end)
368 acl->a_entries[n].e_tag = entry->e_tag;
369 acl->a_entries[n].e_perm = entry->e_perm;
370 switch(acl->a_entries[n].e_tag) {
375 value = (char *)value + sizeof(ext3_acl_entry_short);
376 acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
381 #if BYTE_ORDER == BIG_ENDIAN
382 swabst("4b1i", (u_char *)entry);
384 value = (char *)value + sizeof(ext3_acl_entry);
385 if ((char *)value > end)
387 acl->a_entries[n].e_id = entry->e_id;
399 fprintf(stderr, "ACL bad entry\n");
405 * Dump code starts here :)
409 xattr_cb_list(char *name, char *value, int valuelen, void *private)
411 value[valuelen] = '\0';
412 printf("EA: %s:%s\n", name, value);
418 xattr_cb_set(char *name, char *value, int valuelen, void *private)
420 char *path = (char *)private;
422 if (lsetxattr(path, name, value, valuelen, 0) < 0) {
423 warn("lsetxattr %s failed", path);
430 xattr_cb_compare(char *name, char *value, int valuelen, void *private)
432 char *path = (char *)private;
433 char valuef[XATTR_MAXSIZE];
436 valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE);
438 warn("%s: lgetxattr failed\n", path);
442 if (valuesz != valuelen) {
443 fprintf(stderr, "%s: EA %s value changed\n", path, value);
447 if (memcmp(value, valuef, valuelen)) {
448 fprintf(stderr, "%s: EA %s value changed\n", path, value);
456 xattr_verify(char *buffer)
458 struct ext2_xattr_entry *entry;
461 end = buffer + XATTR_MAXSIZE;
463 #if BYTE_ORDER == BIG_ENDIAN
464 swabst("4i", (u_char *)buffer);
467 if (HDR(buffer)->h_magic != EXT2_XATTR_MAGIC &&
468 HDR(buffer)->h_magic != EXT2_XATTR_MAGIC2) {
469 fprintf(stderr, "error in EA block 1\n");
470 fprintf(stderr, "magic = %x\n", HDR(buffer)->h_magic);
475 /* check the on-disk data structure */
476 entry = FIRST_ENTRY(buffer);
477 #if BYTE_ORDER == BIG_ENDIAN
478 swabst("2b1s3i", (u_char *)entry);
480 while (!IS_LAST_ENTRY(entry)) {
481 struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);
483 if ((char *)next >= end) {
484 fprintf(stderr, "error in EA block\n");
488 #if BYTE_ORDER == BIG_ENDIAN
489 swabst("2b1s3i", (u_char *)entry);
496 xattr_count(char *buffer, int *count)
498 struct ext2_xattr_entry *entry;
501 /* list the attribute names */
502 for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
503 entry = EXT2_XATTR_NEXT(entry))
511 xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, void *), void *private)
513 struct ext2_xattr_entry *entry;
515 /* list the attribute names */
516 for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
517 entry = EXT2_XATTR_NEXT(entry)) {
518 char name[XATTR_MAXSIZE], value[XATTR_MAXSIZE];
522 switch (entry->e_name_index) {
523 case EXT2_XATTR_INDEX_USER:
524 strcpy(name, "user.");
526 case EXT2_XATTR_INDEX_POSIX_ACL_ACCESS:
527 strcpy(name, "system.posix_acl_access");
530 case EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT:
531 strcpy(name, "system.posix_acl_default");
534 case EXT2_XATTR_INDEX_TRUSTED:
535 strcpy(name, "trusted.");
537 case EXT2_XATTR_INDEX_LUSTRE:
538 strcpy(name, "lustre.");
540 case EXT2_XATTR_INDEX_SECURITY:
541 strcpy(name, "security.");
544 fprintf(stderr, "Unknown EA index\n");
549 memcpy(name + off, entry->e_name, entry->e_name_len);
550 name[off + entry->e_name_len] = '\0';
552 memcpy(value, buffer + VALUE_OFFSET(buffer, entry), entry->e_value_size);
555 struct posix_acl *acl;
558 acl = ext3_acl_from_disk(value, entry->e_value_size);
561 size = posix_acl_to_xattr(acl, value, XATTR_MAXSIZE);
564 entry->e_value_size = size;
568 if (xattr_cb(name, value, entry->e_value_size, private) != GOOD)
576 xattr_compare(char *path, char *buffer)
579 char *names = NULL, *end_names, *name;
581 countf = llistxattr(path, NULL, 0);
583 warn("%s: llistxattr failed", path);
587 names = malloc(countf + 1);
589 warn("%s: llistxattr failed", path);
593 countf = llistxattr(path, names, countf);
595 warn("%s: llistxattr failed", path);
600 names[countf] = '\0';
601 end_names = names + countf;
604 for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
613 if (xattr_verify(buffer) == FAIL)
616 if (xattr_count(buffer, &countt) == FAIL)
622 if (countf != countt) {
623 fprintf(stderr, "%s: EA count changed from %d to %d\n", path, countt, countf);
630 return xattr_walk(buffer, xattr_cb_compare, path);
634 xattr_extract(char *path, char *buffer)
637 fprintf(stderr, "xattr_extract(%s)\n", path);
638 xattr_walk(buffer, xattr_cb_list, NULL);
641 if (xattr_verify(buffer) == FAIL)
644 return xattr_walk(buffer, xattr_cb_set, path);