1 /* Support for extended attributes.
2
3 Copyright (C) 2006-2023 Free Software Foundation, Inc.
4
5 This file is part of GNU tar.
6
7 GNU tar is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU tar is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 Written by James Antill, on 2006-07-27. */
21
22 #include <config.h>
23 #include <system.h>
24
25 #include <fnmatch.h>
26 #include <quotearg.h>
27
28 #include "common.h"
29
30 #include "xattr-at.h"
31 #include "selinux-at.h"
32
33 #define XATTRS_PREFIX "SCHILY.xattr."
34 #define XATTRS_PREFIX_LEN (sizeof XATTRS_PREFIX - 1)
35
36 void
37 xheader_xattr_init (struct tar_stat_info *st)
38 {
39 xattr_map_init (&st->xattr_map);
40
41 st->acls_a_ptr = NULL;
42 st->acls_a_len = 0;
43 st->acls_d_ptr = NULL;
44 st->acls_d_len = 0;
45 st->cntx_name = NULL;
46 }
47
48 void
49 xattr_map_init (struct xattr_map *map)
50 {
51 memset (map, 0, sizeof *map);
52 }
53
54 void
55 xattr_map_free (struct xattr_map *xattr_map)
56 {
57 size_t i;
58
59 for (i = 0; i < xattr_map->xm_size; i++)
60 {
61 free (xattr_map->xm_map[i].xkey);
62 free (xattr_map->xm_map[i].xval_ptr);
63 }
64 free (xattr_map->xm_map);
65 }
66
67 void
68 xattr_map_add (struct xattr_map *map,
69 const char *key, const char *val, size_t len)
70 {
71 struct xattr_array *p;
72
73 if (map->xm_size == map->xm_max)
74 map->xm_map = x2nrealloc (map->xm_map, &map->xm_max,
75 sizeof (map->xm_map[0]));
76 p = &map->xm_map[map->xm_size];
77 p->xkey = xstrdup (key);
78 p->xval_ptr = xmemdup (val, len + 1);
79 p->xval_len = len;
80 map->xm_size++;
81 }
82
83 void
84 xheader_xattr_add (struct tar_stat_info *st,
85 const char *key, const char *val, size_t len)
86 {
87 size_t klen = strlen (key);
88 char *xkey = xmalloc (XATTRS_PREFIX_LEN + klen + 1);
89 char *tmp = xkey;
90
91 tmp = stpcpy (tmp, XATTRS_PREFIX);
92 stpcpy (tmp, key);
93
94 xattr_map_add (&st->xattr_map, xkey, val, len);
95
96 free (xkey);
97 }
98
99 void
100 xattr_map_copy (struct xattr_map *dst, const struct xattr_map *src)
101 {
102 size_t i;
103
104 for (i = 0; i < src->xm_size; i++)
105 xattr_map_add (dst, src->xm_map[i].xkey,
106 src->xm_map[i].xval_ptr,
107 src->xm_map[i].xval_len);
108 }
109
110 struct xattrs_mask_map
111 {
112 const char **masks;
113 size_t size;
114 size_t used;
115 };
116
117 /* list of fnmatch patterns */
118 static struct
119 {
120 /* lists of fnmatch patterns */
121 struct xattrs_mask_map incl;
122 struct xattrs_mask_map excl;
123 } xattrs_setup;
124
125 /* disable posix acls when problem found in gnulib script m4/acl.m4 */
126 #if ! USE_ACL
127 # undef HAVE_POSIX_ACLS
128 #endif
129
130 #ifdef HAVE_POSIX_ACLS
131 # include "acl.h"
132 # include <sys/acl.h>
133 # ifdef HAVE_ACL_LIBACL_H
134 # /* needed for numeric-owner support */
135 # include <acl/libacl.h>
136 # endif
137 #endif
138
139 #ifdef HAVE_POSIX_ACLS
140
141 /* acl-at wrappers, TODO: move to gnulib in future? */
142 static acl_t acl_get_file_at (int, const char *, acl_type_t);
143 static int acl_set_file_at (int, const char *, acl_type_t, acl_t);
144 static int file_has_acl_at (int, char const *, struct stat const *);
145 static int acl_delete_def_file_at (int, char const *);
146
147 /* acl_get_file_at */
148 #define AT_FUNC_NAME acl_get_file_at
149 #define AT_FUNC_RESULT acl_t
150 #define AT_FUNC_FAIL (acl_t)NULL
151 #define AT_FUNC_F1 acl_get_file
152 #define AT_FUNC_POST_FILE_PARAM_DECLS , acl_type_t type
153 #define AT_FUNC_POST_FILE_ARGS , type
154 #include "at-func.c"
155 #undef AT_FUNC_NAME
156 #undef AT_FUNC_F1
157 #undef AT_FUNC_RESULT
158 #undef AT_FUNC_FAIL
159 #undef AT_FUNC_POST_FILE_PARAM_DECLS
160 #undef AT_FUNC_POST_FILE_ARGS
161
162 /* acl_set_file_at */
163 #define AT_FUNC_NAME acl_set_file_at
164 #define AT_FUNC_F1 acl_set_file
165 #define AT_FUNC_POST_FILE_PARAM_DECLS , acl_type_t type, acl_t acl
166 #define AT_FUNC_POST_FILE_ARGS , type, acl
167 #include "at-func.c"
168 #undef AT_FUNC_NAME
169 #undef AT_FUNC_F1
170 #undef AT_FUNC_POST_FILE_PARAM_DECLS
171 #undef AT_FUNC_POST_FILE_ARGS
172
173 /* acl_delete_def_file_at */
174 #define AT_FUNC_NAME acl_delete_def_file_at
175 #define AT_FUNC_F1 acl_delete_def_file
176 #define AT_FUNC_POST_FILE_PARAM_DECLS
177 #define AT_FUNC_POST_FILE_ARGS
178 #include "at-func.c"
179 #undef AT_FUNC_NAME
180 #undef AT_FUNC_F1
181 #undef AT_FUNC_POST_FILE_PARAM_DECLS
182 #undef AT_FUNC_POST_FILE_ARGS
183
184 /* gnulib file_has_acl_at */
185 #define AT_FUNC_NAME file_has_acl_at
186 #define AT_FUNC_F1 file_has_acl
187 #define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat const *st
188 #define AT_FUNC_POST_FILE_ARGS , st
189 #include "at-func.c"
190 #undef AT_FUNC_NAME
191 #undef AT_FUNC_F1
192 #undef AT_FUNC_POST_FILE_PARAM_DECLS
193 #undef AT_FUNC_POST_FILE_ARGS
194
195 /* convert unix permissions into an ACL ... needed due to "default" ACLs */
196 static acl_t
197 perms2acl (int perms)
198 {
199 char val[] = "user::---,group::---,other::---";
200 /* 0123456789 123456789 123456789 123456789 */
201
202 /* user */
203 if (perms & 0400)
204 val[6] = 'r';
205 if (perms & 0200)
206 val[7] = 'w';
207 if (perms & 0100)
208 val[8] = 'x';
209
210 /* group */
211 if (perms & 0040)
212 val[17] = 'r';
213 if (perms & 0020)
214 val[18] = 'w';
215 if (perms & 0010)
216 val[19] = 'x';
217
218 /* other */
219 if (perms & 0004)
220 val[28] = 'r';
221 if (perms & 0002)
222 val[29] = 'w';
223 if (perms & 0001)
224 val[30] = 'x';
225
226 return acl_from_text (val);
227 }
228
229 static char *
230 skip_to_ext_fields (char *ptr)
231 {
232 /* skip tag name (user/group/default/mask) */
233 ptr += strcspn (ptr, ":,\n");
234
235 if (*ptr != ':')
236 return ptr;
237 ++ptr;
238
239 ptr += strcspn (ptr, ":,\n"); /* skip user/group name */
240
241 if (*ptr != ':')
242 return ptr;
243 ++ptr;
244
245 ptr += strcspn (ptr, ":,\n"); /* skip perms */
246
247 return ptr;
248 }
249
250 /* The POSIX draft allows extra fields after the three main ones. Star
251 uses this to add a fourth field for user/group which is the numeric ID.
252 This function removes such extra fields by overwriting them with the
253 characters that follow. */
254 static char *
255 fixup_extra_acl_fields (char *ptr)
256 {
257 char *src = ptr;
258 char *dst = ptr;
259
260 while (*src)
261 {
262 const char *old = src;
263 size_t len = 0;
264
265 src = skip_to_ext_fields (src);
266 len = src - old;
267 if (old != dst)
268 memmove (dst, old, len);
269 dst += len;
270
271 if (*src == ':') /* We have extra fields, skip them all */
272 src += strcspn (src, "\n,");
273
274 if ((*src == '\n') || (*src == ','))
275 *dst++ = *src++; /* also done when dst == src, but that's ok */
276 }
277 if (src != dst)
278 *dst = 0;
279
280 return ptr;
281 }
282
283 /* Set the "system.posix_acl_access/system.posix_acl_default" extended
284 attribute. Called only when acls_option > 0. */
285 static void
286 xattrs__acls_set (struct tar_stat_info const *st,
287 char const *file_name, int type,
288 char *ptr, bool def)
289 {
290 acl_t acl;
291
292 if (ptr)
293 {
294 ptr = fixup_extra_acl_fields (ptr);
295 acl = acl_from_text (ptr);
296 }
297 else if (def)
298 {
299 /* No "default" IEEE 1003.1e ACL set for directory. At this moment,
300 FILE_NAME may already have inherited default acls from parent
301 directory; clean them up. */
302 if (acl_delete_def_file_at (chdir_fd, file_name))
303 WARNOPT (WARN_XATTR_WRITE,
304 (0, errno,
305 _("acl_delete_def_file_at: Cannot drop default POSIX ACLs "
306 "for file '%s'"),
307 file_name));
308 return;
309 }
310 else
311 acl = perms2acl (st->stat.st_mode);
312
313 if (!acl)
314 {
315 call_arg_warn ("acl_from_text", file_name);
316 return;
317 }
318
319 if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
320 /* warn even if filesystem does not support acls */
321 WARNOPT (WARN_XATTR_WRITE,
322 (0, errno,
323 _ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
324 file_name));
325
326 acl_free (acl);
327 }
328
329 /* Cleanup textual representation of the ACL in VAL by eliminating tab
330 characters and comments */
331 static void
332 xattrs_acls_cleanup (char *val, size_t *plen)
333 {
334 char *p, *q;
335
336 p = q = val + strcspn (val, "#\t");
337 while (*q)
338 {
339 if (*q == '\t')
340 q++;
341 else if (*q == '#')
342 {
343 while (*q != '\n')
344 q++;
345 }
346 else
347 *p++ = *q++;
348 }
349 *plen = p - val;
350 *p++ = 0;
351 }
352
353 static void
354 acls_get_text (int parentfd, const char *file_name, acl_type_t type,
355 char **ret_ptr, size_t * ret_len)
356 {
357 char *val = NULL;
358 acl_t acl;
359
360 if (!(acl = acl_get_file_at (parentfd, file_name, type)))
361 {
362 if (errno != ENOTSUP)
363 call_arg_warn ("acl_get_file_at", file_name);
364 return;
365 }
366
367 if (numeric_owner_option)
368 {
369 #ifdef HAVE_ACL_LIBACL_H
370 val = acl_to_any_text (acl, NULL, '\n',
371 TEXT_SOME_EFFECTIVE | TEXT_NUMERIC_IDS);
372 #else
373 static int warned;
374 if (!warned)
375 {
376 WARN ((0, 0, _("--numeric-owner is ignored for ACLs: libacl is not available")));
377 warned = 1;
378 }
379 #endif
380 }
381 else
382 val = acl_to_text (acl, NULL);
383 acl_free (acl);
384
385 if (!val)
386 {
387 call_arg_warn ("acl_to_text", file_name);
388 return;
389 }
390
391 *ret_ptr = xstrdup (val);
392 xattrs_acls_cleanup (*ret_ptr, ret_len);
393 acl_free (val);
394 }
395
396 static void
397 xattrs__acls_get_a (int parentfd, const char *file_name,
398 char **ret_ptr, size_t *ret_len)
399 {
400 acls_get_text (parentfd, file_name, ACL_TYPE_ACCESS, ret_ptr, ret_len);
401 }
402
403 /* "system.posix_acl_default" */
404 static void
405 xattrs__acls_get_d (int parentfd, char const *file_name,
406 char **ret_ptr, size_t * ret_len)
407 {
408 acls_get_text (parentfd, file_name, ACL_TYPE_DEFAULT, ret_ptr, ret_len);
409 }
410 #endif /* HAVE_POSIX_ACLS */
411
412 static void
413 acls_one_line (const char *prefix, char delim,
414 const char *aclstring, size_t len)
415 {
416 /* support both long and short text representation of posix acls */
417 struct obstack stk;
418 int pref_len = strlen (prefix);
419 const char *oldstring = aclstring;
420 int pos = 0;
421
422 if (!aclstring || !len)
423 return;
424
425 obstack_init (&stk);
426 while (pos <= len)
427 {
428 int move = strcspn (aclstring, ",\n");
429 if (!move)
430 break;
431
432 if (oldstring != aclstring)
433 obstack_1grow (&stk, delim);
434
435 obstack_grow (&stk, prefix, pref_len);
436 obstack_grow (&stk, aclstring, move);
437
438 pos += move + 1;
439 aclstring += move + 1;
440 }
441
442 obstack_1grow (&stk, '\0');
443
444 fprintf (stdlis, "%s", (char *) obstack_finish (&stk));
445
446 obstack_free (&stk, NULL);
447 }
448
449 void
450 xattrs_acls_get (int parentfd, char const *file_name,
451 struct tar_stat_info *st, int xisfile)
452 {
453 if (acls_option > 0)
454 {
455 #ifndef HAVE_POSIX_ACLS
456 static int done = 0;
457 if (!done)
458 WARN ((0, 0, _("POSIX ACL support is not available")));
459 done = 1;
460 #else
461 int err = file_has_acl_at (parentfd, file_name, &st->stat);
462 if (err == 0)
463 return;
464 if (err == -1)
465 {
466 call_arg_warn ("file_has_acl_at", file_name);
467 return;
468 }
469
470 xattrs__acls_get_a (parentfd, file_name,
471 &st->acls_a_ptr, &st->acls_a_len);
472 if (!xisfile)
473 xattrs__acls_get_d (parentfd, file_name,
474 &st->acls_d_ptr, &st->acls_d_len);
475 #endif
476 }
477 }
478
479 void
480 xattrs_acls_set (struct tar_stat_info const *st,
481 char const *file_name, char typeflag)
482 {
483 if (acls_option > 0 && typeflag != SYMTYPE)
484 {
485 #ifndef HAVE_POSIX_ACLS
486 static int done = 0;
487 if (!done)
488 WARN ((0, 0, _("POSIX ACL support is not available")));
489 done = 1;
490 #else
491 xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
492 st->acls_a_ptr, false);
493 if (typeflag == DIRTYPE || typeflag == GNUTYPE_DUMPDIR)
494 xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT,
495 st->acls_d_ptr, true);
496 #endif
497 }
498 }
499
500 static void
501 mask_map_realloc (struct xattrs_mask_map *map)
502 {
503 if (map->used == map->size)
504 {
505 if (map->size == 0)
506 map->size = 4;
507 map->masks = x2nrealloc (map->masks, &map->size, sizeof (map->masks[0]));
508 }
509 }
510
511 void
512 xattrs_mask_add (const char *mask, bool incl)
513 {
514 struct xattrs_mask_map *mask_map =
515 incl ? &xattrs_setup.incl : &xattrs_setup.excl;
516 /* ensure there is enough space */
517 mask_map_realloc (mask_map);
518 /* just assign pointers -- we silently expect that pointer "mask" is valid
519 through the whole program (pointer to argv array) */
520 mask_map->masks[mask_map->used++] = mask;
521 }
522
523 static void
524 clear_mask_map (struct xattrs_mask_map *mask_map)
525 {
526 if (mask_map->size)
527 free (mask_map->masks);
528 }
529
530 void
531 xattrs_clear_setup (void)
532 {
533 clear_mask_map (&xattrs_setup.incl);
534 clear_mask_map (&xattrs_setup.excl);
535 }
536
537 static bool xattrs_masked_out (const char *kw, bool archiving);
538
539 /* get xattrs from file given by FILE_NAME or FD (when non-zero)
540 xattrs are checked against the user supplied include/exclude mask
541 if no mask is given this includes all the user.*, security.*, system.*,
542 etc. available domains */
543 void
544 xattrs_xattrs_get (int parentfd, char const *file_name,
545 struct tar_stat_info *st, int fd)
546 {
547 if (xattrs_option > 0)
548 {
549 #ifndef HAVE_XATTRS
550 static int done = 0;
551 if (!done)
552 WARN ((0, 0, _("XATTR support is not available")));
553 done = 1;
554 #else
555 static size_t xsz = 1024;
556 static char *xatrs = NULL;
557 ssize_t xret = -1;
558
559 if (!xatrs)
560 xatrs = x2nrealloc (xatrs, &xsz, 1);
561
562 while (((fd == 0) ?
563 ((xret =
564 llistxattrat (parentfd, file_name, xatrs, xsz)) == -1) :
565 ((xret = flistxattr (fd, xatrs, xsz)) == -1))
566 && (errno == ERANGE))
567 {
568 xatrs = x2nrealloc (xatrs, &xsz, 1);
569 }
570
571 if (xret == -1)
572 call_arg_warn ((fd == 0) ? "llistxattrat" : "flistxattr", file_name);
573 else
574 {
575 const char *attr = xatrs;
576 static size_t asz = 1024;
577 static char *val = NULL;
578
579 if (!val)
580 val = x2nrealloc (val, &asz, 1);
581
582 while (xret > 0)
583 {
584 size_t len = strlen (attr);
585 ssize_t aret = 0;
586
587 while (((fd == 0)
588 ? ((aret = lgetxattrat (parentfd, file_name, attr,
589 val, asz)) == -1)
590 : ((aret = fgetxattr (fd, attr, val, asz)) == -1))
591 && (errno == ERANGE))
592 {
593 val = x2nrealloc (val, &asz, 1);
594 }
595
596 if (aret != -1)
597 {
598 if (!xattrs_masked_out (attr, true))
599 xheader_xattr_add (st, attr, val, aret);
600 }
601 else if (errno != ENOATTR)
602 call_arg_warn ((fd == 0) ? "lgetxattrat"
603 : "fgetxattr", file_name);
604
605 attr += len + 1;
606 xret -= len + 1;
607 }
608 }
609 #endif
610 }
611 }
612
613 #ifdef HAVE_XATTRS
614 static void
615 xattrs__fd_set (char const *file_name, char typeflag,
616 const char *attr, const char *ptr, size_t len)
617 {
618 if (ptr)
619 {
620 const char *sysname = "setxattrat";
621 int ret = -1;
622
623 if (typeflag != SYMTYPE)
624 ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0);
625 else
626 {
627 sysname = "lsetxattr";
628 ret = lsetxattrat (chdir_fd, file_name, attr, ptr, len, 0);
629 }
630
631 if (ret == -1)
632 WARNOPT (WARN_XATTR_WRITE,
633 (0, errno,
634 _("%s: Cannot set '%s' extended attribute for file '%s'"),
635 sysname, attr, file_name));
636 }
637 }
638 #endif
639
640 /* lgetfileconat is called against FILE_NAME iff the FD parameter is set to
641 zero, otherwise the fgetfileconat is used against correct file descriptor */
642 void
643 xattrs_selinux_get (int parentfd, char const *file_name,
644 struct tar_stat_info *st, int fd)
645 {
646 if (selinux_context_option > 0)
647 {
648 #if HAVE_SELINUX_SELINUX_H != 1
649 static int done = 0;
650 if (!done)
651 WARN ((0, 0, _("SELinux support is not available")));
652 done = 1;
653 #else
654 int result = fd ?
655 fgetfilecon (fd, &st->cntx_name)
656 : lgetfileconat (parentfd, file_name, &st->cntx_name);
657
658 if (result == -1 && errno != ENODATA && errno != ENOTSUP)
659 call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
660 #endif
661 }
662 }
663
664 void
665 xattrs_selinux_set (struct tar_stat_info const *st,
666 char const *file_name, char typeflag)
667 {
668 if (selinux_context_option > 0)
669 {
670 #if HAVE_SELINUX_SELINUX_H != 1
671 static int done = 0;
672 if (!done)
673 WARN ((0, 0, _("SELinux support is not available")));
674 done = 1;
675 #else
676 const char *sysname = "setfilecon";
677 int ret;
678
679 if (!st->cntx_name)
680 return;
681
682 if (typeflag != SYMTYPE)
683 {
684 ret = setfileconat (chdir_fd, file_name, st->cntx_name);
685 sysname = "setfileconat";
686 }
687 else
688 {
689 ret = lsetfileconat (chdir_fd, file_name, st->cntx_name);
690 sysname = "lsetfileconat";
691 }
692
693 if (ret == -1)
694 WARNOPT (WARN_XATTR_WRITE,
695 (0, errno,
696 _("%s: Cannot set SELinux context for file '%s'"),
697 sysname, file_name));
698 #endif
699 }
700 }
701
702 static bool
703 xattrs_matches_mask (const char *kw, struct xattrs_mask_map *mm)
704 {
705 int i;
706
707 if (!mm->size)
708 return false;
709
710 for (i = 0; i < mm->used; i++)
711 if (fnmatch (mm->masks[i], kw, 0) == 0)
712 return true;
713
714 return false;
715 }
716
717 #define USER_DOT_PFX "user."
718
719 static bool
720 xattrs_kw_included (const char *kw, bool archiving)
721 {
722 if (xattrs_setup.incl.size)
723 return xattrs_matches_mask (kw, &xattrs_setup.incl);
724 else if (archiving)
725 return true;
726 else
727 return strncmp (kw, USER_DOT_PFX, sizeof (USER_DOT_PFX) - 1) == 0;
728 }
729
730 static bool
731 xattrs_kw_excluded (const char *kw)
732 {
733 return xattrs_setup.excl.size ?
734 xattrs_matches_mask (kw, &xattrs_setup.excl) : false;
735 }
736
737 /* Check whether the xattr with keyword KW should be discarded from list of
738 attributes that are going to be archived/excluded (set ARCHIVING=true for
739 archiving, false for excluding) */
740 static bool
741 xattrs_masked_out (const char *kw, bool archiving)
742 {
743 return xattrs_kw_included (kw, archiving) ? xattrs_kw_excluded (kw) : true;
744 }
745
746 void
747 xattrs_xattrs_set (struct tar_stat_info const *st,
748 char const *file_name, char typeflag, int later_run)
749 {
750 if (xattrs_option > 0)
751 {
752 #ifndef HAVE_XATTRS
753 static int done = 0;
754 if (!done)
755 WARN ((0, 0, _("XATTR support is not available")));
756 done = 1;
757 #else
758 size_t i;
759
760 if (!st->xattr_map.xm_size)
761 return;
762
763 for (i = 0; i < st->xattr_map.xm_size; i++)
764 {
765 char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
766
767 /* TODO: this 'later_run' workaround is temporary solution -> once
768 capabilities should become fully supported by it's API and there
769 should exist something like xattrs_capabilities_set() call.
770 For a regular files: all extended attributes are restored during
771 the first run except 'security.capability' which is restored in
772 'later_run == 1'. */
773 if (typeflag == REGTYPE
774 && later_run == !!strcmp (keyword, "security.capability"))
775 continue;
776
777 if (xattrs_masked_out (keyword, false /* extracting */ ))
778 /* we don't want to restore this keyword */
779 continue;
780
781 xattrs__fd_set (file_name, typeflag, keyword,
782 st->xattr_map.xm_map[i].xval_ptr,
783 st->xattr_map.xm_map[i].xval_len);
784 }
785 #endif
786 }
787 }
788
789 void
790 xattrs_print_char (struct tar_stat_info const *st, char *output)
791 {
792 int i;
793
794 if (verbose_option < 2)
795 {
796 *output = 0;
797 return;
798 }
799
800 if (xattrs_option > 0 || selinux_context_option > 0 || acls_option > 0)
801 {
802 /* placeholders */
803 *output = ' ';
804 output[1] = 0;
805 }
806
807 if (xattrs_option > 0 && st->xattr_map.xm_size)
808 for (i = 0; i < st->xattr_map.xm_size; ++i)
809 {
810 char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
811 if (!xattrs_masked_out (keyword, false /* like extracting */ ))
812 {
813 *output = '*';
814 break;
815 }
816 }
817
818 if (selinux_context_option > 0 && st->cntx_name)
819 *output = '.';
820
821 if (acls_option > 0 && (st->acls_a_len || st->acls_d_len))
822 *output = '+';
823 }
824
825 void
826 xattrs_print (struct tar_stat_info const *st)
827 {
828 if (verbose_option < 3)
829 return;
830
831 /* selinux */
832 if (selinux_context_option > 0 && st->cntx_name)
833 fprintf (stdlis, " s: %s\n", st->cntx_name);
834
835 /* acls */
836 if (acls_option > 0 && (st->acls_a_len || st->acls_d_len))
837 {
838 fprintf (stdlis, " a: ");
839 acls_one_line ("", ',', st->acls_a_ptr, st->acls_a_len);
840 if (st->acls_a_len && st->acls_d_len)
841 fprintf (stdlis, ",");
842 acls_one_line ("default:", ',', st->acls_d_ptr, st->acls_d_len);
843 fprintf (stdlis, "\n");
844 }
845
846 /* xattrs */
847 if (xattrs_option > 0 && st->xattr_map.xm_size)
848 {
849 int i;
850
851 for (i = 0; i < st->xattr_map.xm_size; ++i)
852 {
853 char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
854 if (!xattrs_masked_out (keyword, false /* like extracting */ ))
855 fprintf (stdlis, " x: %lu %s\n",
856 (unsigned long) st->xattr_map.xm_map[i].xval_len, keyword);
857 }
858 }
859 }