]> git.wh0rd.org - dump.git/blame - restore/xattr.c
Fix restore of ACLs.
[dump.git] / restore / xattr.c
CommitLineData
cca7148b
SP
1/*
2 * Copyright (c) 1999-2004
3 * Stelian Pop <stelian@popies.net>, 1999-2004
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
16 *
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
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char rcsid[] =
b630baf0 32 "$Id: xattr.c,v 1.2 2005/06/08 09:34:40 stelian Exp $";
cca7148b
SP
33#endif /* not lint */
34
35#include <config.h>
36#include <compatlfs.h>
37#include <compaterr.h>
38#include <sys/types.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include <errno.h>
44#include <bsdcompat.h>
45#include <protocols/dumprestore.h>
46#include "restore.h"
47#include "extern.h"
48#include "pathnames.h"
49
50/*
51 * Data structures below taken from the kernel
52 */
53
54/* Maximum number of references to one attribute block */
55#define EXT2_XATTR_REFCOUNT_MAX 1024
56
57/* Name indexes */
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
65
66struct 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 */
72};
73
74struct ext3_xattr_ibody_header {
75 u_int32_t h_magic; /* magic number for identification */
76};
77
78struct 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 */
86};
87
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)
99
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)
103
104#define BFIRST(buffer) ENTRY(HDR(buffer)+1)
105#define IFIRST(buffer) ENTRY(((struct ext3_xattr_ibody_header *)(buffer))+1)
106
107#define FIRST_ENTRY(buffer) \
108 ((HDR(buffer)->h_magic == EXT2_XATTR_MAGIC2) ? \
109 IFIRST(buffer) : \
110 BFIRST(buffer))
111
112/*
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).
116 */
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))
121
122/*
123 * xattr syscalls do not exist yet in libc, get our own copy here,
124 * taken from libattr.
125 */
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
184#else
185# warning "Extended attribute syscalls undefined for this architecture"
186# define HAVE_XATTR_SYSCALLS 0
187#endif
188
189#if HAVE_XATTR_SYSCALLS
190# define SYSCALL(args...) syscall(args)
191#else
192# define SYSCALL(args...) ( errno = ENOSYS, -1 )
193#endif
194
195static int lsetxattr __P((const char *, const char *, void *, size_t, int));
196static ssize_t lgetxattr __P((const char *, const char *, void *, size_t));
197static ssize_t llistxattr __P((const char *, char *, size_t));
198static int xattr_cb_list __P((char *, char *, int, void *));
199static int xattr_cb_set __P((char *, char *, int, void *));
200static int xattr_cb_compare __P((char *, char *, int, void *));
201static int xattr_verify __P((char *));
202static int xattr_count __P((char *, int *));
203static int xattr_walk __P((char *, int (*)(char *, char *, int, void *), void *));
204
205static int
206lsetxattr(const char *path, const char *name, void *value, size_t size, int flags)
207{
208 return SYSCALL(__NR_lsetxattr, path, name, value, size, flags);
209}
210
211static ssize_t
212lgetxattr(const char *path, const char *name, void *value, size_t size)
213{
214 return SYSCALL(__NR_lgetxattr, path, name, value, size);
215}
216
217static ssize_t
218llistxattr(const char *path, char *list, size_t size)
219{
220 return SYSCALL(__NR_llistxattr, path, list, size);
221}
222
b630baf0
SP
223#define POSIX_ACL_XATTR_VERSION 0x0002
224
225#define ACL_UNDEFINED_ID (-1)
226
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)
233
234typedef struct {
235 u_int16_t e_tag;
236 u_int16_t e_perm;
237 u_int32_t e_id;
238} posix_acl_xattr_entry;
239
240typedef struct {
241 u_int32_t a_version;
242 posix_acl_xattr_entry a_entries[0];
243} posix_acl_xattr_header;
244
245static inline size_t
246posix_acl_xattr_size(int count)
247{
248 return (sizeof(posix_acl_xattr_header) +
249 (count * sizeof(posix_acl_xattr_entry)));
250}
251
252struct posix_acl_entry {
253 short e_tag;
254 unsigned short e_perm;
255 unsigned int e_id;
256};
257
258struct posix_acl {
259 unsigned int a_count;
260 struct posix_acl_entry a_entries[0];
261};
262
263#define EXT3_ACL_VERSION 0x0001
264
265typedef struct {
266 u_int16_t e_tag;
267 u_int16_t e_perm;
268 u_int32_t e_id;
269} ext3_acl_entry;
270
271typedef struct {
272 u_int16_t e_tag;
273 u_int16_t e_perm;
274} ext3_acl_entry_short;
275
276typedef struct {
277 u_int32_t a_version;
278} ext3_acl_header;
279
280static inline int ext3_acl_count(size_t size)
281{
282 ssize_t s;
283 size -= sizeof(ext3_acl_header);
284 s = size - 4 * sizeof(ext3_acl_entry_short);
285 if (s < 0) {
286 if (size % sizeof(ext3_acl_entry_short))
287 return -1;
288 return size / sizeof(ext3_acl_entry_short);
289 } else {
290 if (s % sizeof(ext3_acl_entry))
291 return -1;
292 return s / sizeof(ext3_acl_entry) + 4;
293 }
294}
295
296int
297posix_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;
300 int real_size, n;
301
302 real_size = posix_acl_xattr_size(acl->a_count);
303 if (!buffer)
304 return real_size;
305 if (real_size > size) {
306 fprintf(stderr, "ACL: not enough space to convert (%d %d)\n", real_size, size);
307 return -1;
308 }
309
310 ext_acl->a_version = POSIX_ACL_XATTR_VERSION;
311 if (Bcvt)
312 swabst("1i", (u_char *)ext_acl);
313
314 for (n=0; n < acl->a_count; n++, ext_entry++) {
315 ext_entry->e_tag = acl->a_entries[n].e_tag;
316 ext_entry->e_perm = acl->a_entries[n].e_perm;
317 ext_entry->e_id = acl->a_entries[n].e_id;
318 if (Bcvt)
319 swabst("2s1i", (u_char *)ext_entry);
320 }
321 return real_size;
322}
323
324static struct posix_acl *
325ext3_acl_from_disk(const void *value, size_t size)
326{
327 const char *end = (char *)value + size;
328 int n, count;
329 struct posix_acl *acl;
330
331 if (!value)
332 return NULL;
333 if (size < sizeof(ext3_acl_header)) {
334 fprintf(stderr, "ACL size too little\n");
335 return NULL;
336 }
337 if (Bcvt)
338 swabst("1i", (u_char *)value);
339 if (((ext3_acl_header *)value)->a_version != EXT3_ACL_VERSION) {
340 fprintf(stderr, "ACL version unknown\n");
341 return NULL;
342 }
343 value = (char *)value + sizeof(ext3_acl_header);
344 count = ext3_acl_count(size);
345 if (count < 0) {
346 fprintf(stderr, "ACL bad count\n");
347 return NULL;
348 }
349 if (count == 0)
350 return NULL;
351 acl = malloc(sizeof(struct posix_acl) + count * sizeof(struct posix_acl_entry));
352 if (!acl) {
353 fprintf(stderr, "ACL malloc failed\n");
354 return NULL;
355 }
356 acl->a_count = count;
357
358 for (n=0; n < count; n++) {
359 if (Bcvt)
360 swabst("2s1i", (u_char *)value);
361 ext3_acl_entry *entry = (ext3_acl_entry *)value;
362 if ((char *)value + sizeof(ext3_acl_entry_short) > end)
363 goto fail;
364 acl->a_entries[n].e_tag = entry->e_tag;
365 acl->a_entries[n].e_perm = entry->e_perm;
366 switch(acl->a_entries[n].e_tag) {
367 case ACL_USER_OBJ:
368 case ACL_GROUP_OBJ:
369 case ACL_MASK:
370 case ACL_OTHER:
371 value = (char *)value + sizeof(ext3_acl_entry_short);
372 acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
373 break;
374
375 case ACL_USER:
376 case ACL_GROUP:
377 value = (char *)value + sizeof(ext3_acl_entry);
378 if ((char *)value > end)
379 goto fail;
380 acl->a_entries[n].e_id = entry->e_id;
381 break;
382
383 default:
384 goto fail;
385 }
386 }
387 if (value != end)
388 goto fail;
389 return acl;
390
391fail:
392 fprintf(stderr, "ACL bad entry\n");
393 free(acl);
394 return NULL;
395}
396
cca7148b
SP
397/*
398 * Dump code starts here :)
399 */
400
401static int
402xattr_cb_list(char *name, char *value, int valuelen, void *private)
403{
404 value[valuelen] = '\0';
405 printf("EA: %s:%s\n", name, value);
406
407 return GOOD;
408}
409
410static int
411xattr_cb_set(char *name, char *value, int valuelen, void *private)
412{
413 char *path = (char *)private;
414
415 if (lsetxattr(path, name, value, valuelen, 0) < 0) {
416 warn("lsetxattr %s failed", path);
417 return FAIL;
418 }
419 return GOOD;
420}
421
422static int
423xattr_cb_compare(char *name, char *value, int valuelen, void *private)
424{
425 char *path = (char *)private;
426 char valuef[XATTR_MAXSIZE];
427 int valuesz;
428
429 valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE);
430 if (valuesz < 0) {
431 warn("%s: lgetxattr failed\n", path);
432 return FAIL;
433 }
434
435 if (valuesz != valuelen) {
436 fprintf(stderr, "%s: EA %s value changed\n", path, value);
437 return FAIL;
438 }
439
440 if (memcmp(value, valuef, valuelen)) {
441 fprintf(stderr, "%s: EA %s value changed\n", path, value);
442 return FAIL;
443 }
444
445 return GOOD;
446}
447
448static int
449xattr_verify(char *buffer)
450{
451 struct ext2_xattr_entry *entry;
452 char *end;
453
454 end = buffer + XATTR_MAXSIZE;
455
456 if (Bcvt)
457 swabst("4i", (u_char *)buffer);
458
459 if (HDR(buffer)->h_magic != EXT2_XATTR_MAGIC &&
460 HDR(buffer)->h_magic != EXT2_XATTR_MAGIC2) {
461 fprintf(stderr, "error in EA block 1\n");
462 fprintf(stderr, "magic = %x\n", HDR(buffer)->h_magic);
463
464 return FAIL;
465 }
466
467 /* check the on-disk data structure */
468 entry = FIRST_ENTRY(buffer);
469 if (Bcvt)
470 swabst("2b1s3i", (u_char *)entry);
471 while (!IS_LAST_ENTRY(entry)) {
472 struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);
473
474 if ((char *)next >= end) {
475 fprintf(stderr, "error in EA block\n");
476 return FAIL;
477 }
478 entry = next;
479 if (Bcvt)
480 swabst("2b1s3i", (u_char *)entry);
481 }
482 return GOOD;
483}
484
485static int
486xattr_count(char *buffer, int *count)
487{
488 struct ext2_xattr_entry *entry;
489 int result = 0;
490
491 /* list the attribute names */
492 for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
493 entry = EXT2_XATTR_NEXT(entry))
494 result++;
495
496 *count = result;
497 return GOOD;
498}
499
500static int
501xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, void *), void *private)
502{
503 struct ext2_xattr_entry *entry;
504
505 /* list the attribute names */
506 for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
507 entry = EXT2_XATTR_NEXT(entry)) {
508 char name[XATTR_MAXSIZE], value[XATTR_MAXSIZE];
509 int off;
b630baf0 510 int convertacl = 0;
cca7148b
SP
511
512 switch (entry->e_name_index) {
513 case EXT2_XATTR_INDEX_USER:
514 strcpy(name, "user.");
515 break;
516 case EXT2_XATTR_INDEX_POSIX_ACL_ACCESS:
b630baf0
SP
517 strcpy(name, "system.posix_acl_access");
518 convertacl = 1;
519 break;
cca7148b 520 case EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT:
b630baf0
SP
521 strcpy(name, "system.posix_acl_default");
522 convertacl = 1;
cca7148b
SP
523 break;
524 case EXT2_XATTR_INDEX_TRUSTED:
525 strcpy(name, "trusted.");
526 break;
527 case EXT2_XATTR_INDEX_LUSTRE:
528 strcpy(name, "lustre.");
529 break;
530 case EXT2_XATTR_INDEX_SECURITY:
531 strcpy(name, "security.");
532 break;
533 default:
534 fprintf(stderr, "Unknown EA index\n");
535 return FAIL;
536 }
537
538 off = strlen(name);
539 memcpy(name + off, entry->e_name, entry->e_name_len);
540 name[off + entry->e_name_len] = '\0';
541
cca7148b
SP
542 memcpy(value, buffer + VALUE_OFFSET(buffer, entry), entry->e_value_size);
543
b630baf0
SP
544 if (convertacl) {
545 struct posix_acl *acl;
546 int size;
547
548 acl = ext3_acl_from_disk(value, entry->e_value_size);
549 if (!acl)
550 return FAIL;
551 size = posix_acl_to_xattr(acl, value, XATTR_MAXSIZE);
552 if (size < 0)
553 return FAIL;
554 entry->e_value_size = size;
555 free(acl);
556 }
557
cca7148b
SP
558 if (xattr_cb(name, value, entry->e_value_size, private) != GOOD)
559 return FAIL;
560 }
561
562 return GOOD;
563}
564
565int
566xattr_compare(char *path, char *buffer)
567{
568 int countf, countt;
569 char *names = NULL, *end_names, *name;
570
571 countf = llistxattr(path, NULL, 0);
572 if (countf < 0) {
573 warn("%s: llistxattr failed", path);
574 return FAIL;
575 }
576
577 names = malloc(countf + 1);
578 if (!names) {
579 warn("%s: llistxattr failed", path);
580 return FAIL;
581 }
582
583 countf = llistxattr(path, names, countf);
584 if (countf < 0) {
585 warn("%s: llistxattr failed", path);
586 free(names);
587 return FAIL;
588 }
589
590 names[countf] = '\0';
591 end_names = names + countf;
592
593 countf = 0;
594 for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
595 if (!*name)
596 continue;
597 countf++;
598 }
599
600 free(names);
601
602 if (buffer) {
603 if (xattr_verify(buffer) == FAIL)
604 return FAIL;
605
606 if (xattr_count(buffer, &countt) == FAIL)
607 return FAIL;
608 }
609 else
610 countt = 0;
611
612 if (countf != countt) {
613 fprintf(stderr, "%s: EA count changed from %d to %d\n", path, countt, countf);
614 return FAIL;
615 }
616
617 if (!buffer)
618 return GOOD;
619
620 return xattr_walk(buffer, xattr_cb_compare, path);
621}
622
623int
624xattr_extract(char *path, char *buffer)
625{
626 if (dflag) {
627 fprintf(stderr, "xattr_extract(%s)\n", path);
628 xattr_walk(buffer, xattr_cb_list, NULL);
629 }
630
631 if (xattr_verify(buffer) == FAIL)
632 return FAIL;
633
634 return xattr_walk(buffer, xattr_cb_set, path);
635}
636