]> git.wh0rd.org - dump.git/blame - restore/xattr.c
Fix SELinux xattr handling.
[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[] =
7f7d329e 32 "$Id: xattr.c,v 1.4 2007/02/22 20:12:50 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>
7f7d329e
SP
46#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */
47# include <selinux/selinux.h>
48#endif
cca7148b
SP
49#include "restore.h"
50#include "extern.h"
51#include "pathnames.h"
52
53/*
54 * Data structures below taken from the kernel
55 */
56
57/* Maximum number of references to one attribute block */
58#define EXT2_XATTR_REFCOUNT_MAX 1024
59
60/* Name indexes */
61#define EXT2_XATTR_INDEX_MAX 10
62#define EXT2_XATTR_INDEX_USER 1
63#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS 2
64#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT 3
65#define EXT2_XATTR_INDEX_TRUSTED 4
66#define EXT2_XATTR_INDEX_LUSTRE 5
67#define EXT2_XATTR_INDEX_SECURITY 6
68
69struct ext2_xattr_header {
70 u_int32_t h_magic; /* magic number for identification */
71 u_int32_t h_refcount; /* reference count */
72 u_int32_t h_blocks; /* number of disk blocks used */
73 u_int32_t h_hash; /* hash value of all attributes */
74 u_int32_t h_reserved[4]; /* zero right now */
75};
76
77struct ext3_xattr_ibody_header {
78 u_int32_t h_magic; /* magic number for identification */
79};
80
81struct ext2_xattr_entry {
82 u_char e_name_len; /* length of name */
83 u_char e_name_index; /* attribute name index */
84 u_int16_t e_value_offs; /* offset in disk block of value */
85 u_int32_t e_value_block; /* disk block attribute is stored on (n/i) */
86 u_int32_t e_value_size; /* size of attribute value */
87 u_int32_t e_hash; /* hash value of name and value */
88 char e_name[0]; /* attribute name */
89};
90
91#define EXT2_XATTR_PAD_BITS 2
92#define EXT2_XATTR_PAD (1<<EXT2_XATTR_PAD_BITS)
93#define EXT2_XATTR_ROUND (EXT2_XATTR_PAD-1)
94#define EXT2_XATTR_LEN(name_len) \
95 (((name_len) + EXT2_XATTR_ROUND + \
96 sizeof(struct ext2_xattr_entry)) & ~EXT2_XATTR_ROUND)
97#define EXT2_XATTR_NEXT(entry) \
98 ( (struct ext2_xattr_entry *)( \
99 (char *)(entry) + EXT2_XATTR_LEN((entry)->e_name_len)) )
100#define EXT3_XATTR_SIZE(size) \
101 (((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND)
102
103#define HDR(buffer) ((struct ext2_xattr_header *)(buffer))
104#define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr))
105#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
106
107#define BFIRST(buffer) ENTRY(HDR(buffer)+1)
108#define IFIRST(buffer) ENTRY(((struct ext3_xattr_ibody_header *)(buffer))+1)
109
110#define FIRST_ENTRY(buffer) \
111 ((HDR(buffer)->h_magic == EXT2_XATTR_MAGIC2) ? \
112 IFIRST(buffer) : \
113 BFIRST(buffer))
114
115/*
116 * On-block xattr value offsets start at the beginning of the block, but
117 * on-inode xattr value offsets start after the initial header
118 * (ext3_xattr_ibody_header).
119 */
120#define VALUE_OFFSET(buffer, entry) \
121 (((HDR(buffer)->h_magic == EXT2_XATTR_MAGIC2) ? \
122 (entry)->e_value_offs + sizeof(struct ext3_xattr_ibody_header) : \
123 (entry)->e_value_offs))
124
125/*
126 * xattr syscalls do not exist yet in libc, get our own copy here,
127 * taken from libattr.
128 */
129#if defined (__i386__)
130# define HAVE_XATTR_SYSCALLS 1
131# define __NR_lsetxattr 227
132# define __NR_lgetxattr 230
133# define __NR_llistxattr 233
134#elif defined (__sparc__)
135# define HAVE_XATTR_SYSCALLS 1
136# define __NR_lsetxattr 170
137# define __NR_lgetxattr 173
138# define __NR_llistxattr 179
139#elif defined (__ia64__)
140# define HAVE_XATTR_SYSCALLS 1
141# define __NR_lsetxattr 1218
142# define __NR_lgetxattr 1221
143# define __NR_llistxattr 1224
144#elif defined (__powerpc__)
145# define HAVE_XATTR_SYSCALLS 1
146# define __NR_lsetxattr 210
147# define __NR_lgetxattr 213
148# define __NR_llistxattr 216
149#elif defined (__x86_64__)
150# define HAVE_XATTR_SYSCALLS 1
151# define __NR_lsetxattr 189
152# define __NR_lgetxattr 192
153# define __NR_llistxattr 195
154#elif defined (__s390__)
155# define HAVE_XATTR_SYSCALLS 1
156# define __NR_lsetxattr 225
157# define __NR_lgetxattr 228
158# define __NR_llistxattr 231
159#elif defined (__arm__)
160# define HAVE_XATTR_SYSCALLS 1
161# define __NR_SYSCALL_BASE 0x900000
162# define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
163# define __NR_lgetxattr (__NR_SYSCALL_BASE+230)
164# define __NR_llistxattr (__NR_SYSCALL_BASE+233)
165#elif defined (__mips64__)
166# define HAVE_XATTR_SYSCALLS 1
167# define __NR_Linux 5000
168# define __NR_lsetxattr (__NR_Linux + 218)
169# define __NR_lgetxattr (__NR_Linux + 221)
170# define __NR_llistxattr (__NR_Linux + 224)
171#elif defined (__mips__)
172# define HAVE_XATTR_SYSCALLS 1
173# define __NR_Linux 4000
174# define __NR_lsetxattr (__NR_Linux + 225)
175# define __NR_lgetxattr (__NR_Linux + 228)
176# define __NR_llistxattr (__NR_Linux + 231)
177#elif defined (__alpha__)
178# define HAVE_XATTR_SYSCALLS 1
179# define __NR_lsetxattr 383
180# define __NR_lgetxattr 386
181# define __NR_llistxattr 389
182#elif defined (__mc68000__)
183# define HAVE_XATTR_SYSCALLS 1
184# define __NR_lsetxattr 224
185# define __NR_lgetxattr 227
186# define __NR_llistxattr 230
187#else
188# warning "Extended attribute syscalls undefined for this architecture"
189# define HAVE_XATTR_SYSCALLS 0
190#endif
191
192#if HAVE_XATTR_SYSCALLS
193# define SYSCALL(args...) syscall(args)
194#else
195# define SYSCALL(args...) ( errno = ENOSYS, -1 )
196#endif
197
198static int lsetxattr __P((const char *, const char *, void *, size_t, int));
199static ssize_t lgetxattr __P((const char *, const char *, void *, size_t));
200static ssize_t llistxattr __P((const char *, char *, size_t));
7f7d329e
SP
201static int xattr_cb_list __P((char *, char *, int, int, void *));
202static int xattr_cb_set __P((char *, char *, int, int, void *));
203static int xattr_cb_compare __P((char *, char *, int, int, void *));
cca7148b
SP
204static int xattr_verify __P((char *));
205static int xattr_count __P((char *, int *));
7f7d329e 206static int xattr_walk __P((char *, int (*)(char *, char *, int, int, void *), void *));
cca7148b
SP
207
208static int
209lsetxattr(const char *path, const char *name, void *value, size_t size, int flags)
210{
211 return SYSCALL(__NR_lsetxattr, path, name, value, size, flags);
212}
213
214static ssize_t
215lgetxattr(const char *path, const char *name, void *value, size_t size)
216{
217 return SYSCALL(__NR_lgetxattr, path, name, value, size);
218}
219
220static ssize_t
221llistxattr(const char *path, char *list, size_t size)
222{
223 return SYSCALL(__NR_llistxattr, path, list, size);
224}
225
b630baf0
SP
226#define POSIX_ACL_XATTR_VERSION 0x0002
227
228#define ACL_UNDEFINED_ID (-1)
229
230#define ACL_USER_OBJ (0x01)
231#define ACL_USER (0x02)
232#define ACL_GROUP_OBJ (0x04)
233#define ACL_GROUP (0x08)
234#define ACL_MASK (0x10)
235#define ACL_OTHER (0x20)
236
237typedef struct {
238 u_int16_t e_tag;
239 u_int16_t e_perm;
240 u_int32_t e_id;
241} posix_acl_xattr_entry;
242
243typedef struct {
244 u_int32_t a_version;
245 posix_acl_xattr_entry a_entries[0];
246} posix_acl_xattr_header;
247
248static inline size_t
249posix_acl_xattr_size(int count)
250{
251 return (sizeof(posix_acl_xattr_header) +
252 (count * sizeof(posix_acl_xattr_entry)));
253}
254
255struct posix_acl_entry {
256 short e_tag;
257 unsigned short e_perm;
258 unsigned int e_id;
259};
260
261struct posix_acl {
262 unsigned int a_count;
263 struct posix_acl_entry a_entries[0];
264};
265
266#define EXT3_ACL_VERSION 0x0001
267
268typedef struct {
269 u_int16_t e_tag;
270 u_int16_t e_perm;
271 u_int32_t e_id;
272} ext3_acl_entry;
273
274typedef struct {
275 u_int16_t e_tag;
276 u_int16_t e_perm;
277} ext3_acl_entry_short;
278
279typedef struct {
280 u_int32_t a_version;
281} ext3_acl_header;
282
283static inline int ext3_acl_count(size_t size)
284{
285 ssize_t s;
286 size -= sizeof(ext3_acl_header);
287 s = size - 4 * sizeof(ext3_acl_entry_short);
288 if (s < 0) {
289 if (size % sizeof(ext3_acl_entry_short))
290 return -1;
291 return size / sizeof(ext3_acl_entry_short);
292 } else {
293 if (s % sizeof(ext3_acl_entry))
294 return -1;
295 return s / sizeof(ext3_acl_entry) + 4;
296 }
297}
298
299int
300posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size) {
301 posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer;
302 posix_acl_xattr_entry *ext_entry = ext_acl->a_entries;
303 int real_size, n;
304
305 real_size = posix_acl_xattr_size(acl->a_count);
306 if (!buffer)
307 return real_size;
308 if (real_size > size) {
309 fprintf(stderr, "ACL: not enough space to convert (%d %d)\n", real_size, size);
310 return -1;
311 }
312
313 ext_acl->a_version = POSIX_ACL_XATTR_VERSION;
cb6d3f79
SP
314#if BYTE_ORDER == BIG_ENDIAN
315 swabst("1i", (u_char *)ext_acl);
316#endif
b630baf0
SP
317
318 for (n=0; n < acl->a_count; n++, ext_entry++) {
319 ext_entry->e_tag = acl->a_entries[n].e_tag;
320 ext_entry->e_perm = acl->a_entries[n].e_perm;
321 ext_entry->e_id = acl->a_entries[n].e_id;
cb6d3f79
SP
322#if BYTE_ORDER == BIG_ENDIAN
323 swabst("2s1i", (u_char *)ext_entry);
324#endif
b630baf0
SP
325 }
326 return real_size;
327}
328
329static struct posix_acl *
330ext3_acl_from_disk(const void *value, size_t size)
331{
332 const char *end = (char *)value + size;
333 int n, count;
334 struct posix_acl *acl;
335
336 if (!value)
337 return NULL;
338 if (size < sizeof(ext3_acl_header)) {
339 fprintf(stderr, "ACL size too little\n");
340 return NULL;
341 }
cb6d3f79
SP
342#if BYTE_ORDER == BIG_ENDIAN
343 swabst("1i", (u_char *)value);
344#endif
b630baf0
SP
345 if (((ext3_acl_header *)value)->a_version != EXT3_ACL_VERSION) {
346 fprintf(stderr, "ACL version unknown\n");
347 return NULL;
348 }
349 value = (char *)value + sizeof(ext3_acl_header);
350 count = ext3_acl_count(size);
351 if (count < 0) {
352 fprintf(stderr, "ACL bad count\n");
353 return NULL;
354 }
355 if (count == 0)
356 return NULL;
357 acl = malloc(sizeof(struct posix_acl) + count * sizeof(struct posix_acl_entry));
358 if (!acl) {
359 fprintf(stderr, "ACL malloc failed\n");
360 return NULL;
361 }
362 acl->a_count = count;
363
364 for (n=0; n < count; n++) {
b630baf0 365 ext3_acl_entry *entry = (ext3_acl_entry *)value;
cb6d3f79
SP
366#if BYTE_ORDER == BIG_ENDIAN
367 swabst("2s", (u_char *)entry);
368#endif
b630baf0
SP
369 if ((char *)value + sizeof(ext3_acl_entry_short) > end)
370 goto fail;
371 acl->a_entries[n].e_tag = entry->e_tag;
372 acl->a_entries[n].e_perm = entry->e_perm;
373 switch(acl->a_entries[n].e_tag) {
374 case ACL_USER_OBJ:
375 case ACL_GROUP_OBJ:
376 case ACL_MASK:
377 case ACL_OTHER:
378 value = (char *)value + sizeof(ext3_acl_entry_short);
379 acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
380 break;
381
382 case ACL_USER:
383 case ACL_GROUP:
cb6d3f79
SP
384#if BYTE_ORDER == BIG_ENDIAN
385 swabst("4b1i", (u_char *)entry);
386#endif
b630baf0
SP
387 value = (char *)value + sizeof(ext3_acl_entry);
388 if ((char *)value > end)
389 goto fail;
390 acl->a_entries[n].e_id = entry->e_id;
391 break;
392
393 default:
394 goto fail;
395 }
396 }
397 if (value != end)
398 goto fail;
399 return acl;
400
401fail:
402 fprintf(stderr, "ACL bad entry\n");
403 free(acl);
404 return NULL;
405}
406
cca7148b
SP
407/*
408 * Dump code starts here :)
409 */
410
411static int
7f7d329e 412xattr_cb_list(char *name, char *value, int valuelen, int isSELinux, void *private)
cca7148b 413{
7f7d329e 414 isSELinux;
cca7148b
SP
415 value[valuelen] = '\0';
416 printf("EA: %s:%s\n", name, value);
417
418 return GOOD;
419}
420
421static int
7f7d329e 422xattr_cb_set(char *name, char *value, int valuelen, int isSELinux, void *private)
cca7148b
SP
423{
424 char *path = (char *)private;
7f7d329e
SP
425 int err;
426
427 isSELinux;
428#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */
429 if (isSELinux)
430 err = lsetfilecon(path, value);
431 else
432#endif
433 err = lsetxattr(path, name, value, valuelen, 0);
434
435 if (err) {
436 warn("%s: EA set %s:%s failed", path, name, value);
cca7148b
SP
437 return FAIL;
438 }
7f7d329e 439
cca7148b
SP
440 return GOOD;
441}
442
443static int
7f7d329e 444xattr_cb_compare(char *name, char *value, int valuelen, int isSELinux, void *private)
cca7148b
SP
445{
446 char *path = (char *)private;
447 char valuef[XATTR_MAXSIZE];
448 int valuesz;
7f7d329e
SP
449
450 isSELinux;
451#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */
452 if (isSELinux)
453 {
454 security_context_t con = NULL;
455
456 if (lgetfilecon(path, &con) < 0) {
457 warn("%s: EA compare lgetfilecon failed\n", path);
458 return FAIL;
459 }
460
461 valuesz = strlen(con) + 1;
462 valuef[0] = 0;
463 strncat(valuef, con, sizeof valuef);
464 freecon(con);
cca7148b 465 }
7f7d329e
SP
466 else {
467#endif
468 valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE);
469 if (valuesz < 0) {
470 warn("%s: EA compare lgetxattr failed\n", path);
471 return FAIL;
472 }
473#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */
cca7148b 474 }
7f7d329e
SP
475#endif
476
477 if (valuesz != valuelen || memcmp(value, valuef, valuelen)) {
478 /* GAN24May06: show name and new value for user to compare */
479 fprintf(stderr, "%s: EA %s:%s value changed to %s\n", path, name, value, valuef);
cca7148b
SP
480 return FAIL;
481 }
482
483 return GOOD;
484}
485
486static int
487xattr_verify(char *buffer)
488{
489 struct ext2_xattr_entry *entry;
490 char *end;
491
492 end = buffer + XATTR_MAXSIZE;
493
cb6d3f79
SP
494#if BYTE_ORDER == BIG_ENDIAN
495 swabst("4i", (u_char *)buffer);
496#endif
cca7148b
SP
497
498 if (HDR(buffer)->h_magic != EXT2_XATTR_MAGIC &&
499 HDR(buffer)->h_magic != EXT2_XATTR_MAGIC2) {
500 fprintf(stderr, "error in EA block 1\n");
501 fprintf(stderr, "magic = %x\n", HDR(buffer)->h_magic);
502
503 return FAIL;
504 }
505
506 /* check the on-disk data structure */
507 entry = FIRST_ENTRY(buffer);
cb6d3f79
SP
508#if BYTE_ORDER == BIG_ENDIAN
509 swabst("2b1s3i", (u_char *)entry);
510#endif
cca7148b
SP
511 while (!IS_LAST_ENTRY(entry)) {
512 struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);
513
514 if ((char *)next >= end) {
515 fprintf(stderr, "error in EA block\n");
516 return FAIL;
517 }
518 entry = next;
cb6d3f79
SP
519#if BYTE_ORDER == BIG_ENDIAN
520 swabst("2b1s3i", (u_char *)entry);
521#endif
cca7148b
SP
522 }
523 return GOOD;
524}
525
526static int
527xattr_count(char *buffer, int *count)
528{
529 struct ext2_xattr_entry *entry;
530 int result = 0;
531
532 /* list the attribute names */
533 for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
534 entry = EXT2_XATTR_NEXT(entry))
535 result++;
536
537 *count = result;
538 return GOOD;
539}
540
541static int
7f7d329e 542xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, int, void *), void *private)
cca7148b
SP
543{
544 struct ext2_xattr_entry *entry;
545
546 /* list the attribute names */
547 for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
548 entry = EXT2_XATTR_NEXT(entry)) {
549 char name[XATTR_MAXSIZE], value[XATTR_MAXSIZE];
550 int off;
b630baf0 551 int convertacl = 0;
7f7d329e 552 int convertcon = 0;
cca7148b
SP
553
554 switch (entry->e_name_index) {
555 case EXT2_XATTR_INDEX_USER:
556 strcpy(name, "user.");
557 break;
558 case EXT2_XATTR_INDEX_POSIX_ACL_ACCESS:
b630baf0
SP
559 strcpy(name, "system.posix_acl_access");
560 convertacl = 1;
561 break;
cca7148b 562 case EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT:
b630baf0
SP
563 strcpy(name, "system.posix_acl_default");
564 convertacl = 1;
cca7148b
SP
565 break;
566 case EXT2_XATTR_INDEX_TRUSTED:
567 strcpy(name, "trusted.");
568 break;
569 case EXT2_XATTR_INDEX_LUSTRE:
570 strcpy(name, "lustre.");
571 break;
572 case EXT2_XATTR_INDEX_SECURITY:
573 strcpy(name, "security.");
7f7d329e
SP
574#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */
575 convertcon = transselinuxflag;
576#endif
cca7148b
SP
577 break;
578 default:
579 fprintf(stderr, "Unknown EA index\n");
580 return FAIL;
581 }
582
583 off = strlen(name);
584 memcpy(name + off, entry->e_name, entry->e_name_len);
585 name[off + entry->e_name_len] = '\0';
586
cca7148b
SP
587 memcpy(value, buffer + VALUE_OFFSET(buffer, entry), entry->e_value_size);
588
b630baf0
SP
589 if (convertacl) {
590 struct posix_acl *acl;
591 int size;
592
593 acl = ext3_acl_from_disk(value, entry->e_value_size);
594 if (!acl)
595 return FAIL;
596 size = posix_acl_to_xattr(acl, value, XATTR_MAXSIZE);
597 if (size < 0)
598 return FAIL;
599 entry->e_value_size = size;
600 free(acl);
601 }
7f7d329e
SP
602
603#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */
604 if (convertcon && strcmp(name, "security.selinux"))
605 convertcon = 0; /*GAN24May06 only for selinux */
606
607 if (convertcon)
608 {
609 security_context_t con = NULL;
610 int err;
611
612 if (!transselinuxarg)
613 err = security_canonicalize_context(value, &con);
614 else {
615 strncat(value, transselinuxarg, sizeof value);
616 err = security_canonicalize_context_raw(value, &con);
617 }
618
619 if (err < 0) {
620 warn("%s: EA canonicalize failed\n", value);
621 return FAIL;
622 }
623
624 entry->e_value_size = strlen(con) + 1;
625 value[0] = 0;
626 strncat(value, con, sizeof value);
627 freecon(con);
628 }
629#endif
b630baf0 630
7f7d329e 631 if (xattr_cb(name, value, entry->e_value_size, convertcon, private) != GOOD)
cca7148b
SP
632 return FAIL;
633 }
634
635 return GOOD;
636}
637
638int
639xattr_compare(char *path, char *buffer)
640{
641 int countf, countt;
642 char *names = NULL, *end_names, *name;
643
644 countf = llistxattr(path, NULL, 0);
645 if (countf < 0) {
646 warn("%s: llistxattr failed", path);
647 return FAIL;
648 }
649
650 names = malloc(countf + 1);
651 if (!names) {
652 warn("%s: llistxattr failed", path);
653 return FAIL;
654 }
655
656 countf = llistxattr(path, names, countf);
657 if (countf < 0) {
658 warn("%s: llistxattr failed", path);
659 free(names);
660 return FAIL;
661 }
662
663 names[countf] = '\0';
664 end_names = names + countf;
665
666 countf = 0;
667 for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
668 if (!*name)
669 continue;
670 countf++;
671 }
672
673 free(names);
674
675 if (buffer) {
676 if (xattr_verify(buffer) == FAIL)
677 return FAIL;
678
679 if (xattr_count(buffer, &countt) == FAIL)
680 return FAIL;
681 }
682 else
683 countt = 0;
684
685 if (countf != countt) {
686 fprintf(stderr, "%s: EA count changed from %d to %d\n", path, countt, countf);
687 return FAIL;
688 }
689
690 if (!buffer)
691 return GOOD;
692
693 return xattr_walk(buffer, xattr_cb_compare, path);
694}
695
696int
697xattr_extract(char *path, char *buffer)
698{
699 if (dflag) {
700 fprintf(stderr, "xattr_extract(%s)\n", path);
701 xattr_walk(buffer, xattr_cb_list, NULL);
702 }
703
704 if (xattr_verify(buffer) == FAIL)
705 return FAIL;
706
707 return xattr_walk(buffer, xattr_cb_set, path);
708}
709