]> Dogcows Code - chaz/tar/blob - src/xattrs.c
Add SELinux context store/restore/list support.
[chaz/tar] / src / xattrs.c
1 /* Support for extended attributes.
2
3 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software
4 Foundation, Inc.
5
6 Written by James Antill, on 2006-07-27.
7
8 This program is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
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 int size;
37 int 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 acl_t acl_get_file_at (int dirfd, const char *file, acl_type_t type);
62 int acl_set_file_at (int dirfd, const char *file, acl_type_t type, acl_t acl);
63 int file_has_acl_at (int dirfd, char const *, struct stat const *);
64
65 /* acl_get_file_at */
66 #define AT_FUNC_NAME acl_get_file_at
67 #define AT_FUNC_RESULT acl_t
68 #define AT_FUNC_FAIL (acl_t)NULL
69 #define AT_FUNC_F1 acl_get_file
70 #define AT_FUNC_POST_FILE_PARAM_DECLS , acl_type_t type
71 #define AT_FUNC_POST_FILE_ARGS , type
72 #include "at-func.c"
73 #undef AT_FUNC_NAME
74 #undef AT_FUNC_F1
75 #undef AT_FUNC_RESULT
76 #undef AT_FUNC_FAIL
77 #undef AT_FUNC_POST_FILE_PARAM_DECLS
78 #undef AT_FUNC_POST_FILE_ARGS
79
80 /* acl_set_file_at */
81 #define AT_FUNC_NAME acl_set_file_at
82 #define AT_FUNC_F1 acl_set_file
83 #define AT_FUNC_POST_FILE_PARAM_DECLS , acl_type_t type, acl_t acl
84 #define AT_FUNC_POST_FILE_ARGS , type, acl
85 #include "at-func.c"
86 #undef AT_FUNC_NAME
87 #undef AT_FUNC_F1
88 #undef AT_FUNC_POST_FILE_PARAM_DECLS
89 #undef AT_FUNC_POST_FILE_ARGS
90
91 /* gnulib file_has_acl_at */
92 #define AT_FUNC_NAME file_has_acl_at
93 #define AT_FUNC_F1 file_has_acl
94 #define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat const *st
95 #define AT_FUNC_POST_FILE_ARGS , st
96 #include "at-func.c"
97 #undef AT_FUNC_NAME
98 #undef AT_FUNC_F1
99 #undef AT_FUNC_POST_FILE_PARAM_DECLS
100 #undef AT_FUNC_POST_FILE_ARGS
101
102 /* convert unix permissions into an ACL ... needed due to "default" ACLs */
103 static acl_t perms2acl (int perms)
104 {
105 char val[] = "user::---,group::---,other::---";
106 /* 0123456789 123456789 123456789 123456789 */
107
108 /* user */
109 if (perms & 0400) val[ 6] = 'r';
110 if (perms & 0200) val[ 7] = 'w';
111 if (perms & 0100) val[ 8] = 'x';
112
113 /* group */
114 if (perms & 0040) val[17] = 'r';
115 if (perms & 0020) val[18] = 'w';
116 if (perms & 0010) val[19] = 'x';
117
118 /* other */
119 if (perms & 0004) val[28] = 'r';
120 if (perms & 0002) val[29] = 'w';
121 if (perms & 0001) val[30] = 'x';
122
123 return (acl_from_text (val));
124 }
125
126 static char *skip_to_ext_fields (char *ptr)
127 {
128 ptr += strcspn (ptr, ":,\n"); /* skip tag name. Ie. user/group/default/mask */
129
130 if (*ptr != ':')
131 return (ptr); /* error? no user/group field */
132 ++ptr;
133
134 ptr += strcspn (ptr, ":,\n"); /* skip user/group name */
135
136 if (*ptr != ':')
137 return (ptr); /* error? no perms field */
138 ++ptr;
139
140 ptr += strcspn (ptr, ":,\n"); /* skip perms */
141
142 if (*ptr != ':')
143 return (ptr); /* no extra fields */
144
145 return (ptr);
146 }
147
148 /* The POSIX draft allows extra fields after the three main ones. Star
149 uses this to add a fourth field for user/group which is the numeric ID.
150 We just skip all extra fields atm. */
151 static const char *fixup_extra_acl_fields (const char *ptr)
152 {
153 char *src = (char *)ptr;
154 char *dst = (char *)ptr;
155
156 while (*src)
157 {
158 const char *old = src;
159 size_t len = 0;
160
161 src = skip_to_ext_fields (src);
162 len = src - old;
163 if (old != dst) memmove (dst, old, len);
164 dst += len;
165
166 if (*src == ':') /* We have extra fields, skip them all */
167 src += strcspn (src, "\n,");
168
169 if ((*src == '\n') || (*src == ','))
170 *dst++ = *src++; /* also done when dst == src, but that's ok */
171 }
172 if (src != dst)
173 *dst = 0;
174
175 return ptr;
176 }
177
178 static void xattrs__acls_set (struct tar_stat_info const *st,
179 char const *file_name, int type,
180 const char *ptr, size_t len, bool def)
181 { /* "system.posix_acl_access" */
182 acl_t acl;
183
184 if (ptr)
185 {
186 /* assert (strlen (ptr) == len); */
187 ptr = fixup_extra_acl_fields (ptr);
188
189 acl = acl_from_text (ptr);
190 acls_option = 1;
191 }
192 else if (acls_option > 0)
193 acl = perms2acl (st->stat.st_mode);
194 else
195 return; /* don't call acl functions unless we first hit an ACL, or
196 --acls was passed explicitly */
197
198 if (acl == (acl_t)NULL)
199 {
200 call_arg_warn ("acl_from_text", file_name);
201 return;
202 }
203
204 if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
205 /* warn even if filesystem does not support acls */
206 WARNOPT (WARN_XATTR_WRITE, (0, errno,
207 _("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"), file_name));
208
209 acl_free (acl);
210 }
211
212 static void xattrs__acls_get_a (int parentfd, const char *file_name,
213 struct tar_stat_info *st,
214 char **ret_ptr, size_t *ret_len)
215 { /* "system.posix_acl_access" */
216 char *val = NULL;
217 ssize_t len;
218 acl_t acl;
219
220 if ((acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_ACCESS))
221 == (acl_t)NULL)
222 {
223 if (errno != ENOTSUP)
224 call_arg_warn ("acl_get_file_at", file_name);
225 return;
226 }
227
228 val = acl_to_text (acl, &len);
229 acl_free (acl);
230
231 if (val == NULL)
232 {
233 call_arg_warn ("acl_to_text", file_name);
234 return;
235 }
236
237 *ret_ptr = xstrdup (val);
238 *ret_len = len;
239
240 acl_free (val);
241 }
242
243 static void xattrs__acls_get_d (int parentfd, char const *file_name,
244 struct tar_stat_info *st,
245 char **ret_ptr, size_t *ret_len)
246 { /* "system.posix_acl_default" */
247 char *val = NULL;
248 ssize_t len;
249 acl_t acl;
250
251 if ((acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_DEFAULT))
252 == (acl_t)NULL)
253 {
254 if (errno != ENOTSUP)
255 call_arg_warn ("acl_get_file_at", file_name);
256 return;
257 }
258
259 val = acl_to_text (acl, &len);
260 acl_free (acl);
261
262 if (val == NULL)
263 {
264 call_arg_warn ("acl_to_text", file_name);
265 return;
266 }
267
268 *ret_ptr = xstrdup (val);
269 *ret_len = len;
270
271 acl_free (val);
272 }
273 #endif /* HAVE_POSIX_ACLS */
274
275 static void acls_one_line (const char *prefix, char delim,
276 const char *aclstring, size_t len)
277 {
278 /* support both long and short text representation of posix acls */
279 struct obstack stk;
280 obstack_init (&stk);
281 int pref_len = strlen (prefix);
282 const char *oldstring = aclstring;
283
284 if (!aclstring || !len)
285 return;
286
287 int pos = 0;
288 while (pos <= len)
289 {
290 int move = strcspn (aclstring, ",\n");
291 if (!move)
292 break;
293
294 if (oldstring != aclstring)
295 obstack_1grow (&stk, delim);
296
297 obstack_grow (&stk, prefix, pref_len);
298 obstack_grow (&stk, aclstring, move);
299
300 aclstring += move + 1;
301 }
302
303 obstack_1grow (&stk, '\0');
304 const char *toprint = obstack_finish (&stk);
305
306 fprintf (stdlis, "%s", toprint);
307
308 obstack_free (&stk, NULL);
309 }
310
311 void xattrs_acls_get (int parentfd, char const *file_name,
312 struct tar_stat_info *st, int fd, int xisfile)
313 {
314 if (acls_option > 0)
315 {
316 #ifndef HAVE_POSIX_ACLS
317 static int done = 0;
318 if (!done)
319 WARN ((0, 0, _("POSIX ACL support is not available")));
320 done = 1;
321 #else
322 int err = file_has_acl_at (parentfd, file_name, &st->stat);
323 if (err == 0)
324 return;
325 if (err == -1)
326 {
327 call_arg_warn ("file_has_acl_at", file_name);
328 return;
329 }
330
331 xattrs__acls_get_a (parentfd, file_name, st,
332 &st->acls_a_ptr, &st->acls_a_len);
333 if (!xisfile)
334 xattrs__acls_get_d (parentfd, file_name, st,
335 &st->acls_d_ptr, &st->acls_d_len);
336 #endif
337 }
338 }
339
340 void xattrs_acls_set (struct tar_stat_info const *st,
341 char const *file_name, char typeflag)
342 {
343 if ((acls_option > 0) && (typeflag != SYMTYPE))
344 {
345 #ifndef HAVE_POSIX_ACLS
346 static int done = 0;
347 if (!done)
348 WARN ((0, 0, _("POSIX ACL support is not available")));
349 done = 1;
350 #else
351 xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
352 st->acls_a_ptr, st->acls_a_len, false);
353 if ((typeflag == DIRTYPE) || (typeflag == GNUTYPE_DUMPDIR))
354 xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT,
355 st->acls_d_ptr, st->acls_d_len, true);
356 #endif
357 }
358 }
359
360 static void mask_map_realloc (struct xattrs_mask_map *map)
361 {
362 if (map->size == 0)
363 {
364 map->size = 4;
365 map->masks = xmalloc (16 * sizeof (char *));
366 return;
367 }
368
369 if (map->size <= map->used)
370 {
371 map->size *= 2;
372 map->masks = xrealloc (map->masks, map->size * sizeof (char *));
373 return;
374 }
375 }
376
377 void xattrs_mask_add (const char *mask, bool incl)
378 {
379 struct xattrs_mask_map *mask_map = incl ? &xattrs_setup.incl
380 : &xattrs_setup.excl;
381 /* ensure there is enough space */
382 mask_map_realloc (mask_map);
383 /* just assign pointers -- we silently expect that pointer "mask" is valid
384 through the whole program (pointer to argv array) */
385 mask_map->masks[mask_map->used++] = mask;
386 }
387
388 static void clear_mask_map (struct xattrs_mask_map *mask_map)
389 {
390 if (mask_map->size)
391 free (mask_map->masks);
392 }
393
394 void xattrs_clear_setup ()
395 {
396 clear_mask_map (&xattrs_setup.incl);
397 clear_mask_map (&xattrs_setup.excl);
398 }
399
400 /* get all xattrs from file given by FILE_NAME or FD (when non-zero). This
401 includes all the user.*, security.*, system.*, etc. available domains */
402 void xattrs_xattrs_get (int parentfd, char const *file_name,
403 struct tar_stat_info *st, int fd)
404 {
405 if (xattrs_option > 0)
406 {
407 #ifndef HAVE_XATTRS
408 static int done = 0;
409 if (!done)
410 WARN ((0, 0, _("XATTR support is not available")));
411 done = 1;
412 #else
413 static ssize_t xsz = 1024;
414 static char *xatrs = NULL;
415 ssize_t xret = -1;
416
417 if (!xatrs) xatrs = xmalloc (xsz);
418
419 while (((fd == 0) ?
420 ((xret = llistxattrat (parentfd, file_name, xatrs, xsz)) == -1) :
421 ((xret = flistxattr (fd, xatrs, xsz)) == -1)) &&
422 (errno == ERANGE))
423 {
424 xsz <<= 1;
425 xatrs = xrealloc (xatrs, xsz);
426 }
427
428 if (xret == -1)
429 call_arg_warn ((fd == 0) ? "llistxattrat" : "flistxattr", file_name);
430 else
431 {
432 const char *attr = xatrs;
433 static ssize_t asz = 1024;
434 static char *val = NULL;
435
436 if (!val) val = xmalloc (asz);
437
438 while (xret > 0)
439 {
440 size_t len = strlen (attr);
441 ssize_t aret = 0;
442
443 /* Archive all xattrs during creation, decide at extraction time
444 * which ones are of interest/use for the target filesystem. */
445 while (((fd == 0)
446 ? ((aret = lgetxattrat (parentfd, file_name, attr,
447 val, asz)) == -1)
448 : ((aret = fgetxattr (fd, attr, val, asz)) == -1))
449 && (errno == ERANGE))
450 {
451 asz <<= 1;
452 val = xrealloc (val, asz);
453 }
454
455 if (aret != -1)
456 xheader_xattr_add (st, attr, val, aret);
457 else if (errno != ENOATTR)
458 call_arg_warn ((fd == 0) ? "lgetxattrat"
459 : "fgetxattr", file_name);
460
461 attr += len + 1;
462 xret -= len + 1;
463 }
464 }
465 #endif
466 }
467 }
468
469 static void xattrs__fd_set (struct tar_stat_info const *st,
470 char const *file_name, char typeflag,
471 const char *attr,
472 const char *ptr, size_t len)
473 {
474 if (ptr)
475 {
476 const char *sysname = "setxattrat";
477 int ret = -1;
478
479 if (typeflag != SYMTYPE)
480 ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0);
481 else
482 {
483 sysname = "lsetxattr";
484 ret = lsetxattrat (chdir_fd, file_name, attr, ptr, len, 0);
485 }
486
487 if (ret == -1)
488 WARNOPT (WARN_XATTR_WRITE, (0, errno,
489 _("%s: Cannot set '%s' extended attribute for file '%s'"),
490 sysname, attr, file_name));
491 }
492 }
493
494 /* lgetfileconat is called against FILE_NAME iff the FD parameter is set to
495 zero, otherwise the fgetfileconat is used against correct file descriptor */
496 void xattrs_selinux_get (int parentfd, char const *file_name,
497 struct tar_stat_info *st, int fd)
498 {
499 if (selinux_context_option > 0)
500 {
501 #if HAVE_SELINUX_SELINUX_H != 1
502 static int done = 0;
503 if (!done)
504 WARN ((0, 0, _("SELinux support is not available")));
505 done = 1;
506 #else
507 int result = (fd ? fgetfilecon (fd, &st->cntx_name)
508 : lgetfileconat (parentfd, file_name, &st->cntx_name));
509
510 if (result == -1 && errno != ENODATA && errno != ENOTSUP)
511 call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
512 #endif
513 }
514 }
515
516 void xattrs_selinux_set (struct tar_stat_info const *st,
517 char const *file_name, char typeflag)
518 {
519 if (selinux_context_option > 0)
520 {
521 #if HAVE_SELINUX_SELINUX_H != 1
522 static int done = 0;
523 if (!done)
524 WARN ((0, 0, _("SELinux support is not available")));
525 done = 1;
526 #else
527 const char *sysname = "setfilecon";
528 int ret;
529
530 if (!st->cntx_name)
531 return;
532
533 if (typeflag != SYMTYPE)
534 {
535 ret = setfileconat (chdir_fd, file_name, st->cntx_name);
536 sysname = "setfileconat";
537 }
538 else
539 {
540 ret = lsetfileconat (chdir_fd, file_name, st->cntx_name);
541 sysname = "lsetfileconat";
542 }
543
544 if (ret == -1)
545 WARNOPT (WARN_XATTR_WRITE, (0, errno,
546 _("%s: Cannot set SELinux context for file '%s'"), sysname,
547 file_name));
548 #endif
549 }
550 }
551
552 static bool xattrs_matches_mask (const char *kw, struct xattrs_mask_map *mm)
553 {
554 int i;
555
556 if (!mm->size)
557 return false;
558
559 for (i = 0; i < mm->used; i++)
560 if (fnmatch (mm->masks[i], kw, 0) == 0)
561 return true;
562
563 return false;
564 }
565
566 static bool xattrs_kw_included (const char *kw, bool archiving)
567 {
568 if (xattrs_setup.incl.size)
569 return xattrs_matches_mask (kw, &xattrs_setup.incl);
570 else
571 {
572 if (archiving)
573 return true;
574 else
575 return strncmp (kw, "user.", strlen ("user.")) == 0;
576 }
577 }
578
579 static bool xattrs_kw_excluded (const char *kw, bool archiving)
580 {
581 if (!xattrs_setup.excl.size)
582 return false;
583
584 return xattrs_matches_mask (kw, &xattrs_setup.excl);
585 }
586
587 /* Check whether the xattr with keyword KW should be discarded from list of
588 attributes that are going to be archived/excluded (set ARCHIVING=true for
589 archiving, false for excluding) */
590 static bool xattrs_masked_out (const char *kw, bool archiving)
591 {
592 if (!xattrs_kw_included (kw, archiving))
593 return true;
594
595 return xattrs_kw_excluded (kw, archiving);
596 }
597
598 void xattrs_xattrs_set (struct tar_stat_info const *st,
599 char const *file_name, char typeflag,
600 int later_run)
601 {
602 if (xattrs_option > 0)
603 {
604 #ifndef HAVE_XATTRS
605 static int done = 0;
606 if (!done)
607 WARN ((0, 0, _("XATTR support is not available")));
608 done = 1;
609 #else
610 size_t scan = 0;
611
612 if (!st->xattr_map_size)
613 return;
614
615 for (; scan < st->xattr_map_size; ++scan)
616 {
617 char *keyword = st->xattr_map[scan].xkey;
618 keyword += strlen ("SCHILY.xattr.");
619
620 /* TODO: this 'later_run' workaround is temporary solution -> once
621 capabilities should become fully supported by it's API and there
622 should exist something like xattrs_capabilities_set() call.
623 For a regular files: all extended attributes are restored during
624 the first run except 'security.capability' which is restored in
625 'later_run == 1'. */
626 if (typeflag == REGTYPE
627 && later_run == !!strcmp (keyword, "security.capability"))
628 continue;
629
630 if (xattrs_masked_out (keyword, false /* extracting */ ))
631 /* we don't want to restore this keyword */
632 continue;
633
634 xattrs__fd_set (st, file_name, typeflag, keyword,
635 st->xattr_map[scan].xval_ptr,
636 st->xattr_map[scan].xval_len);
637 }
638 #endif
639 }
640 }
641
642 void xattrs_print_char (struct tar_stat_info const *st, char *output)
643 {
644 int i;
645 if (verbose_option < 2)
646 {
647 *output = 0;
648 return;
649 }
650
651 if (xattrs_option > 0 || selinux_context_option > 0 || acls_option > 0)
652 {
653 /* placeholders */
654 *output = ' ';
655 *(output + 1) = 0;
656 }
657
658 if (xattrs_option > 0 && st->xattr_map_size)
659 for (i = 0; i < st->xattr_map_size; ++i)
660 {
661 char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
662 if (xattrs_masked_out (keyword, false /* like extracting */ ))
663 continue;
664 *output = '*';
665 break;
666 }
667
668 if (selinux_context_option > 0 && st->cntx_name)
669 *output = '.';
670
671 if (acls_option && (st->acls_a_len || st->acls_d_len))
672 *output = '+';
673 }
674
675 void xattrs_print (struct tar_stat_info const *st)
676 {
677 if (verbose_option < 3)
678 return;
679
680 /* selinux */
681 if (selinux_context_option && st->cntx_name)
682 fprintf (stdlis, " s: %s\n", st->cntx_name);
683
684 /* acls */
685 if (acls_option && (st->acls_a_len || st->acls_d_len))
686 {
687 fprintf (stdlis, " a: ");
688 acls_one_line ("", ',', st->acls_a_ptr, st->acls_a_len);
689 acls_one_line ("default:", ',', st->acls_d_ptr, st->acls_d_len);
690 fprintf (stdlis, "\n");
691 }
692
693 /* xattrs */
694 if (xattrs_option && st->xattr_map_size)
695 {
696 int i;
697 for (i = 0; i < st->xattr_map_size; ++i)
698 {
699 char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
700 if (xattrs_masked_out (keyword, false /* like extracting */ ))
701 continue;
702 fprintf (stdlis, " x: %lu %s\n",
703 (unsigned long) st->xattr_map[i].xval_len, keyword);
704 }
705 }
706 }
This page took 0.068583 seconds and 5 git commands to generate.