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