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