/* Create a tar archive. Copyright (C) 2006 Free Software Foundation, Inc. Written by James Antill, on 2006-07-27. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "common.h" #ifndef HAVE_SELINUX_SELINUX_H # undef HAVE_LIBSELINUX #endif #ifndef HAVE_ATTR_XATTR_H # undef HAVE_XATTRS #endif #ifndef HAVE_SYS_ACL_H # undef HAVE_LIBACL #endif #ifdef HAVE_SELINUX_SELINUX_H # include #endif #ifdef HAVE_ATTR_XATTR_H # include #endif #ifdef HAVE_SYS_ACL_H # include #endif #if 0 /* unused by xattr's atm. */ static void xattrs__fd_get(struct tar_stat_info *st, char const *file_name, int fd, const char *attr, char **ret_ptr, size_t *ret_len) { #ifdef HAVE_XATTRS static ssize_t asz = 1024; ssize_t ret = 0; static char *val = NULL; if (!val) val = xmalloc (asz); while (((ret = fgetxattr (fd, attr, val, asz)) == -1) && (errno == ERANGE)) { asz <<= 1; val = xrealloc (val, asz); } if (ret != -1) { *ret_ptr = xmemdup (val, ret); *ret_len = ret; } else if (errno != ENOATTR) call_arg_warn ("fgetxattr", file_name); #endif } #endif static void xattrs__acls_get_a(struct tar_stat_info *st, char const *file_name, int fd, char **ret_ptr, size_t *ret_len) { /* "system.posix_acl_access" */ #ifdef HAVE_LIBACL char *val = NULL; size_t len; acl_t acl; if (fd != -1) { if ((acl = acl_get_fd (fd)) == (acl_t)NULL) { if (errno != ENOTSUP) call_arg_warn ("acl_get_fd", file_name); return; } } else if ((acl = acl_get_file (file_name, ACL_TYPE_ACCESS)) == (acl_t)NULL) { if (errno != ENOTSUP) call_arg_warn ("acl_get_file", file_name); return; } val = acl_to_text(acl, &len); acl_free (acl); if (val == NULL) { call_arg_warn ("acl_to_text", file_name); return; } *ret_ptr = xstrndup (val, len); *ret_len = len; acl_free (val); #endif } static void xattrs__acls_get_d(struct tar_stat_info *st, char const *file_name, char **ret_ptr, size_t *ret_len) { /* "system.posix_acl_default" */ #ifdef HAVE_LIBACL char *val = NULL; size_t len; acl_t acl; if ((acl = acl_get_file (file_name, ACL_TYPE_DEFAULT)) == (acl_t)NULL) { if (errno != ENOTSUP) call_arg_warn ("acl_get_file", file_name); return; } val = acl_to_text(acl, &len); acl_free (acl); if (val == NULL) { call_arg_warn ("acl_to_text", file_name); return; } *ret_ptr = xstrndup (val, len); *ret_len = len; acl_free (val); #endif } void xattrs_acls_get(struct tar_stat_info *st, char const *file_name, int fd, int xisfile) { if (acls_option > 0) { #ifndef HAVE_LIBACL static int done = 0; if (!done) WARN ((0, 0, _("ACL support requested, but not available"))); done = 1; #endif xattrs__acls_get_a (st, file_name, fd, &st->acls_a_ptr, &st->acls_a_len); if (!xisfile) xattrs__acls_get_d (st, file_name, &st->acls_d_ptr, &st->acls_d_len); } } void xattrs_selinux_get(struct tar_stat_info *st, char const *file_name, int fd) { if (selinux_context_option > 0) { #ifndef HAVE_LIBSELINUX static int done = 0; if (!done) WARN ((0, 0, _("SELinux support requested, but not available"))); done = 1; #else if (fd == -1) { if ((lgetfilecon (file_name, &st->cntx_name) == -1) && (errno != ENOTSUP) && (errno != ENODATA)) call_arg_warn ("lgetfilecon", file_name); } else if ((fgetfilecon (fd, &st->cntx_name) == -1) && (errno != ENOTSUP) && (errno != ENODATA)) call_arg_warn ("fgetfilecon", file_name); #endif } } void xattrs_xattrs_get(struct tar_stat_info *st, char const *file_name, int fd) { if (xattrs_option > 0) { /* get all xattrs ... this include security.* and system.* if available. We filter them here, but we have to filter them in xattrs_xattrs_set() anyway. */ static ssize_t xsz = 1024; static char *xatrs = NULL; ssize_t xret = -1; #ifndef HAVE_XATTRS static int done = 0; if ((xattrs_option > 0) && !done) WARN ((0, 0, _("Xattr support requested, but not available"))); done = 1; #else if (!xatrs) xatrs = xmalloc (xsz); while (((fd == -1) ? ((xret = listxattr (file_name, xatrs, xsz)) == -1) : ((xret = flistxattr (fd, xatrs, xsz)) == -1)) && (errno == ERANGE)) { xsz <<= 1; xatrs = xrealloc (xatrs, xsz); } if (xret == -1) call_arg_warn ((fd == -1) ? "listxattrs" : "flistxattrs", file_name); else { const char *attr = xatrs; static ssize_t asz = 1024; static char *val = NULL; if (!val) val = xmalloc (asz); while (xret > 0) { size_t len = strlen (attr); ssize_t aret = 0; // if (strncmp (attr, "user.", strlen("user.")) && // strncmp (attr, "trusted.", strlen("trusted."))) // goto next_attr; /* only store normal xattrs */ while (((fd == -1) ? ((aret = getxattr (file_name, attr, val, asz)) == -1) : ((aret = fgetxattr (fd, attr, val, asz)) == -1)) && (errno == ERANGE)) { asz <<= 1; val = xrealloc (val, asz); } if (aret != -1) xheader_xattr_add (st, attr, val, aret); else if (errno != ENOATTR) call_arg_warn ("fgetxattr", file_name); next_attr: attr += len + 1; xret -= len + 1; } } #endif } } 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) { #ifdef HAVE_XATTRS if (ptr) { const char *sysname = "setxattr"; int ret = -1; if (typeflag != SYMTYPE) ret = setxattr (file_name, attr, ptr, len, 0); else { sysname = "lsetxattr"; ret = lsetxattr (file_name, attr, ptr, len, 0); } if ((ret == -1) && (errno == EPERM)) call_arg_warn(sysname, file_name); else if ((ret == -1) && (errno != EOPNOTSUPP)) call_arg_error(sysname, file_name); } #endif } /* convert unix permissions into an ACL ... needed due to "default" ACLs */ #ifdef HAVE_LIBACL static acl_t perms2acl(int perms) { char val[] = "user::---,group::---,other::---"; /* 0123456789 123456789 123456789 123456789 */ /* user */ if (perms & 0400) val[ 6] = 'r'; if (perms & 0200) val[ 7] = 'w'; if (perms & 0100) val[ 8] = 'x'; /* group */ if (perms & 0040) val[17] = 'r'; if (perms & 0020) val[18] = 'w'; if (perms & 0010) val[19] = 'x'; /* other */ if (perms & 0004) val[28] = 'r'; if (perms & 0002) val[29] = 'w'; if (perms & 0001) val[30] = 'x'; return (acl_from_text (val)); } #endif static char *skip_to_ext_fields(char *ptr) { ptr += strcspn(ptr, ":,\n"); /* skip tag name. Ie. user/group/default/mask */ if (*ptr != ':') return (ptr); /* error? no user/group field */ ++ptr; ptr += strcspn(ptr, ":,\n"); /* skip user/group name */ if (*ptr != ':') return (ptr); /* error? no perms field */ ++ptr; ptr += strcspn(ptr, ":,\n"); /* skip perms */ if (*ptr != ':') return (ptr); /* no extra fields */ return (ptr); } /* The POSIX draft allows extra fields after the three main ones. Star uses this to add a fourth field for user/group which is the numeric ID. We just skip all extra fields atm. */ static const char *fixup_extra_acl_fields(const char *ptr) { char *src = (char *)ptr; char *dst = (char *)ptr; while (*src) { const char *old = src; size_t len = 0; src = skip_to_ext_fields(src); len = src - old; if (old != dst) memmove(dst, old, len); dst += len; if (*src == ':') /* We have extra fields, skip them all */ src += strcspn(src, "\n,"); if ((*src == '\n') || (*src == ',')) *dst++ = *src++; /* also done when dst == src, but that's ok */ } if (src != dst) *dst = 0; return ptr; } static void xattrs__acls_set(struct tar_stat_info const *st, char const *file_name, int type, const char *ptr, size_t len) { /* "system.posix_acl_access" */ #ifdef HAVE_LIBACL acl_t acl; if (ptr) { /* assert (strlen (ptr) == len); */ ptr = fixup_extra_acl_fields(ptr); acl = acl_from_text (ptr); acls_option = 1; } else if (acls_option > 0) acl = perms2acl (st->stat.st_mode); else return; /* don't call acl functions unless we first hit an ACL, or --acls was passed explicitly */ if (acl == (acl_t)NULL) { call_arg_warn ("acl_from_text", file_name); return; } if (acl_set_file (file_name, type, acl) == -1) { if (errno != ENOTSUP) call_arg_warn ("acl_set_file", file_name); } acl_free (acl); #endif } void xattrs_acls_set(struct tar_stat_info const *st, char const *file_name, char typeflag) { if ((acls_option >= 0) && (typeflag != SYMTYPE)) { #ifndef HAVE_LIBACL static int done = 0; if (!done) WARN ((0, 0, _("ACL support requested, but not available"))); done = 1; #else xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS, st->acls_a_ptr, st->acls_a_len); if (S_ISDIR (st->stat.st_mode)) xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT, st->acls_d_ptr, st->acls_d_len); #endif } } void xattrs_selinux_set(struct tar_stat_info const *st, char const *file_name, char typeflag) { if ((selinux_context_option >= 0) && st->cntx_name) { const char *sysname = "setfilecon"; int ret = -1; #ifndef HAVE_LIBSELINUX static int done = 0; if (!done) WARN ((0, 0, _("SELinux support requested, but not available"))); done = 1; #else if (typeflag != SYMTYPE) ret = setfilecon (file_name, st->cntx_name); else { sysname = "lsetfilecon"; ret = lsetfilecon (file_name, st->cntx_name); } /* do not print warnings when SELinux is disabled */ if ((ret == -1) && (errno != EPERM) && (errno != ENOTSUP)) call_arg_error(sysname, file_name); #endif } } void xattrs_xattrs_set(struct tar_stat_info const *st, char const *file_name, char typeflag) { if ((xattrs_option >= 0) && st->xattr_map_size) { size_t scan = 0; #ifndef HAVE_XATTRS static int done = 0; if (!done) WARN ((0, 0, _("Xattr support requested, but not available"))); done = 1; #else while (scan < st->xattr_map_size) { char *keyword = st->xattr_map[scan].xkey; /* assert (!memcpy (keyword, "SCHILY.xattr.", strlen("SCHILY.xattr."))); */ keyword += strlen("SCHILY.xattr."); // if (strncmp (keyword, "user.", strlen("user.")) && // strncmp (keyword, "trusted.", strlen("trusted."))) // continue; /* don't try and set anything but normal xattrs */ /* should we ignore trusted.* EPERM errors when not root ? */ xattrs__fd_set (st, file_name, typeflag, keyword, st->xattr_map[scan].xval_ptr, st->xattr_map[scan].xval_len); ++scan; } #endif } }