]> git.wh0rd.org Git - dump.git/blob - restore/xattr.c
Don't attempt to extract EA in 'restore -N' mode.
[dump.git] / 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.6 2010/03/08 10:40:52 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 #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
198 static int lsetxattr __P((const char *, const char *, void *, size_t, int));
199 static ssize_t lgetxattr __P((const char *, const char *, void *, size_t));
200 static ssize_t llistxattr __P((const char *, char *, size_t));
201 static int xattr_cb_list __P((char *, char *, int, int, void *));
202 static int xattr_cb_set __P((char *, char *, int, int, void *));
203 static int xattr_cb_compare __P((char *, char *, int, int, void *));
204 static int xattr_verify __P((char *));
205 static int xattr_count __P((char *, int *));
206 static int xattr_walk __P((char *, int (*)(char *, char *, int, int, void *), void *));
207
208 static int
209 lsetxattr(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
214 static ssize_t
215 lgetxattr(const char *path, const char *name, void *value, size_t size)
216 {
217         return SYSCALL(__NR_lgetxattr, path, name, value, size);
218 }
219
220 static ssize_t
221 llistxattr(const char *path, char *list, size_t size)
222 {
223         return SYSCALL(__NR_llistxattr, path, list, size);
224 }
225
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
237 typedef 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
243 typedef struct {
244         u_int32_t               a_version;
245         posix_acl_xattr_entry   a_entries[0];
246 } posix_acl_xattr_header;
247
248 static inline size_t
249 posix_acl_xattr_size(int count)
250 {
251         return (sizeof(posix_acl_xattr_header) +
252                 (count * sizeof(posix_acl_xattr_entry)));
253 }
254
255 struct posix_acl_entry {
256         short           e_tag;
257         unsigned short  e_perm;
258         unsigned int    e_id;
259 };
260
261 struct posix_acl {
262         unsigned int            a_count;
263         struct posix_acl_entry  a_entries[0];
264 };
265
266 #define EXT3_ACL_VERSION        0x0001
267
268 typedef struct {
269         u_int16_t       e_tag;
270         u_int16_t       e_perm;
271         u_int32_t       e_id;
272 } ext3_acl_entry;
273
274 typedef struct {
275         u_int16_t       e_tag;
276         u_int16_t       e_perm;
277 } ext3_acl_entry_short;
278
279 typedef struct {
280         u_int32_t       a_version;
281 } ext3_acl_header;
282
283 static 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
299 int
300 posix_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;
314 #if BYTE_ORDER == BIG_ENDIAN
315         swabst("1i", (u_char *)ext_acl);
316 #endif
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;
322 #if BYTE_ORDER == BIG_ENDIAN
323                 swabst("2s1i", (u_char *)ext_entry);
324 #endif
325         }
326         return real_size;
327 }
328
329 static struct posix_acl *
330 ext3_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         }
342 #if BYTE_ORDER == BIG_ENDIAN
343         swabst("1i", (u_char *)value);
344 #endif
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++) {
365                 ext3_acl_entry *entry = (ext3_acl_entry *)value;
366 #if BYTE_ORDER == BIG_ENDIAN
367                 swabst("2s", (u_char *)entry);
368 #endif
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:
384 #if BYTE_ORDER == BIG_ENDIAN
385                         swabst("4b1i", (u_char *)entry);
386 #endif
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
401 fail:
402         fprintf(stderr, "ACL bad entry\n");
403         free(acl);
404         return NULL;
405 }
406
407 /*
408  * Dump code starts here :)
409  */
410
411 static int
412 xattr_cb_list(char *name, char *value, int valuelen, int isSELinux, void *private)
413 {
414         isSELinux;
415         value[valuelen] = '\0';
416         printf("EA: %s:%s\n", name, value);
417
418         return GOOD;
419 }
420
421 static int
422 xattr_cb_set(char *name, char *value, int valuelen, int isSELinux, void *private)
423 {
424         char *path = (char *)private;
425         int err;
426
427         if (Nflag)
428                 return GOOD;
429
430         isSELinux;
431 #ifdef TRANSSELINUX                     /*GAN6May06 SELinux MLS */
432         if (isSELinux)
433                 err = lsetfilecon(path, value);
434         else
435 #endif
436                 err = lsetxattr(path, name, value, valuelen, 0);
437         
438         if (err) {
439                 warn("%s: EA set %s:%s failed", path, name, value);
440                 return FAIL;
441         }
442         
443         return GOOD;
444 }
445
446 static int
447 xattr_cb_compare(char *name, char *value, int valuelen, int isSELinux, void *private)
448 {
449         char *path = (char *)private;
450         char valuef[XATTR_MAXSIZE];
451         int valuesz;
452         
453         isSELinux;
454 #ifdef TRANSSELINUX                     /*GAN6May06 SELinux MLS */
455         if (isSELinux)
456         {
457                 security_context_t con = NULL;
458                 
459                 if (lgetfilecon(path, &con) < 0) {
460                         warn("%s: EA compare lgetfilecon failed\n", path);
461                         return FAIL;
462                 }
463                 
464                 valuesz = strlen(con) + 1;
465                 valuef[0] = 0;
466                 strncat(valuef, con, sizeof valuef);
467                 freecon(con);
468         }
469         else {
470 #endif
471                 valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE);
472                 if (valuesz < 0) {
473                         warn("%s: EA compare lgetxattr failed\n", path);
474                         return FAIL;
475                 }
476 #ifdef TRANSSELINUX                     /*GAN6May06 SELinux MLS */
477         }
478 #endif
479         
480         if (valuesz != valuelen || memcmp(value, valuef, valuelen)) {
481                 /* GAN24May06: show name and new value for user to compare */
482                 fprintf(stderr, "%s: EA %s:%s value changed to %s\n", path, name, value, valuef);
483                 return FAIL;
484         }
485
486         return GOOD;
487 }
488
489 static int
490 xattr_verify(char *buffer)
491 {
492         struct ext2_xattr_entry *entry;
493         char *end;
494
495         end = buffer + XATTR_MAXSIZE;
496
497 #if BYTE_ORDER == BIG_ENDIAN
498         swabst("4i", (u_char *)buffer);
499 #endif
500
501         if (HDR(buffer)->h_magic != EXT2_XATTR_MAGIC &&
502             HDR(buffer)->h_magic != EXT2_XATTR_MAGIC2) {
503                 fprintf(stderr, "error in EA block 1\n");
504                 fprintf(stderr, "magic = %x\n", HDR(buffer)->h_magic);
505                 
506                 return FAIL;
507         }
508
509         /* check the on-disk data structure */
510         entry = FIRST_ENTRY(buffer);
511 #if BYTE_ORDER == BIG_ENDIAN
512         swabst("2b1s3i", (u_char *)entry);
513 #endif
514         while (!IS_LAST_ENTRY(entry)) {
515                 struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);
516
517                 if ((char *)next >= end) {
518                         fprintf(stderr, "error in EA block\n");
519                         return FAIL;
520                 }
521                 entry = next;
522 #if BYTE_ORDER == BIG_ENDIAN
523                 swabst("2b1s3i", (u_char *)entry);
524 #endif
525         }
526         return GOOD;
527 }
528
529 static int
530 xattr_count(char *buffer, int *count)
531 {
532         struct ext2_xattr_entry *entry;
533         int result = 0;
534
535         /* list the attribute names */
536         for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
537              entry = EXT2_XATTR_NEXT(entry))
538                 result++;
539
540         *count = result;
541         return GOOD;
542 }
543
544 static int
545 xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, int, void *), void *private)
546 {
547         struct ext2_xattr_entry *entry;
548
549         /* list the attribute names */
550         for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry);
551              entry = EXT2_XATTR_NEXT(entry)) {
552                 char name[XATTR_MAXSIZE], value[XATTR_MAXSIZE];
553                 int size;
554                 int off;
555                 int convertacl = 0;
556                 int convertcon = 0;
557
558                 switch (entry->e_name_index) {
559                 case EXT2_XATTR_INDEX_USER:
560                         strcpy(name, "user.");
561                         break;
562                 case EXT2_XATTR_INDEX_POSIX_ACL_ACCESS:
563                         strcpy(name, "system.posix_acl_access");
564                         convertacl = 1;
565                         break;
566                 case EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT:
567                         strcpy(name, "system.posix_acl_default");
568                         convertacl = 1;
569                         break;
570                 case EXT2_XATTR_INDEX_TRUSTED:
571                         strcpy(name, "trusted.");
572                         break;
573                 case EXT2_XATTR_INDEX_LUSTRE:
574                         strcpy(name, "lustre.");
575                         break;
576                 case EXT2_XATTR_INDEX_SECURITY:
577                         strcpy(name, "security.");
578 #ifdef TRANSSELINUX                     /*GAN6May06 SELinux MLS */
579                         convertcon = transselinuxflag;
580 #endif
581                         break;
582                 default:
583                         fprintf(stderr, "Unknown EA index\n");
584                         return FAIL;
585                 }
586
587                 off = strlen(name);
588                 memcpy(name + off, entry->e_name, entry->e_name_len);
589                 name[off + entry->e_name_len] = '\0';
590                 size = entry->e_value_size;
591
592                 memcpy(value, buffer + VALUE_OFFSET(buffer, entry), size);
593
594                 if (convertacl) {
595                         struct posix_acl *acl;
596
597                         acl = ext3_acl_from_disk(value, size);
598                         if (!acl)
599                                 return FAIL;
600                         size = posix_acl_to_xattr(acl, value, XATTR_MAXSIZE);
601                         if (size < 0)
602                                 return FAIL;
603                         free(acl);
604                 }
605                 
606 #ifdef TRANSSELINUX                     /*GAN6May06 SELinux MLS */
607                 if (convertcon  &&  strcmp(name, "security.selinux"))
608                         convertcon = 0; /*GAN24May06 only for selinux */
609                 
610                 if (convertcon)
611                 {
612                         security_context_t con = NULL;
613                         int err;
614                         
615                         if (!transselinuxarg)
616                                 err = security_canonicalize_context(value, &con);
617                         else {
618                                 strncat(value, transselinuxarg, sizeof value);
619                                 err = security_canonicalize_context_raw(value, &con);
620                         }
621                         
622                         if (err < 0) {
623                                 warn("%s: EA canonicalize failed\n", value);
624                                 return FAIL;
625                         }
626
627                         size = strlen(con) + 1;
628                         value[0] = 0;
629                         strncat(value, con, sizeof value);
630                         freecon(con);
631                 }
632 #endif
633
634                 if (xattr_cb(name, value, size, convertcon, private) != GOOD)
635                         return FAIL;
636         }
637
638         return GOOD;
639 }
640
641 int
642 xattr_compare(char *path, char *buffer)
643 {
644         int countf, countt;
645         char *names = NULL, *end_names, *name;
646
647         countf = llistxattr(path, NULL, 0);
648         if (countf < 0) {
649                 warn("%s: llistxattr failed", path);
650                 return FAIL;
651         }
652
653         names = malloc(countf + 1);
654         if (!names) {
655                 warn("%s: llistxattr failed", path);
656                 return FAIL;
657         }
658
659         countf = llistxattr(path, names, countf);
660         if (countf < 0) {
661                 warn("%s: llistxattr failed", path);
662                 free(names);
663                 return FAIL;
664         }
665
666         names[countf] = '\0';
667         end_names = names + countf;
668
669         countf = 0;
670         for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
671                 if (!*name)
672                         continue;
673                 countf++;
674         }
675
676         free(names);
677
678         if (buffer) {
679                 if (xattr_verify(buffer) == FAIL)
680                         return FAIL;
681
682                 if (xattr_count(buffer, &countt) == FAIL)
683                         return FAIL;
684         }
685         else
686                 countt = 0;
687
688         if (countf != countt) {
689                 fprintf(stderr, "%s: EA count changed from %d to %d\n", path, countt, countf);
690                 return FAIL;
691         }
692
693         if (!buffer)
694                 return GOOD;
695
696         return xattr_walk(buffer, xattr_cb_compare, path);
697 }
698
699 int
700 xattr_extract(char *path, char *buffer)
701 {
702         if (dflag) {
703                 fprintf(stderr, "xattr_extract(%s)\n", path);
704                 xattr_walk(buffer, xattr_cb_list, NULL);
705         }
706
707         if (xattr_verify(buffer) == FAIL)
708                 return FAIL;
709
710         return xattr_walk(buffer, xattr_cb_set, path);
711 }
712