From: Radoslaw Korzeniewski Date: Sat, 19 Nov 2016 15:56:53 +0000 (+0100) Subject: XACL - refactoring an ACL and XATTR codes. X-Git-Tag: Release-7.4.5~13 X-Git-Url: https://git.sur5r.net/?p=bacula%2Fbacula;a=commitdiff_plain;h=aa7041b2ca4730efc8c09f4058633463a1c29511 XACL - refactoring an ACL and XATTR codes. A specialized class to handle ACL and XATTR in Bacula Enterprise. The runtime consist of two parts: 1. OS independent class: XACL 2. OS dependent subclass: XACL_* OS dependent subclasses are available for the following OS: - Darwin (OSX) - FreeBSD (POSIX and NFSv4/ZFS acls) - Linux - Solaris (POSIX and NFSv4/ZFS acls) OS dependend subclasses in progress: - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface) - HPUX - IRIX - Tru64 --- diff --git a/bacula/.gitignore b/bacula/.gitignore index 2e7992fad0..637dbef7cd 100644 --- a/bacula/.gitignore +++ b/bacula/.gitignore @@ -450,6 +450,7 @@ src/qt-console/Makefile.mingw32 src/qt-console/Makefile.mingw32.Debug src/qt-console/Makefile.mingw32.Release src/qt-console/bat.pro.mingw32 +src/qt-console/bat.pro.mingw64 # src/qt-console/clients/ src/qt-console/clients/.*.swp diff --git a/bacula/AUTHORS b/bacula/AUTHORS index 462cc81a32..23c66fb5d1 100644 --- a/bacula/AUTHORS +++ b/bacula/AUTHORS @@ -104,6 +104,7 @@ Peter Much Philippe Chauvat Phil Stracchino Preben Guldberg +Radoslaw Korzeniewski Riccardo Ghetta Richard Mortimer Robert Nelson diff --git a/bacula/src/filed/Makefile.in b/bacula/src/filed/Makefile.in index 7fca1a8e49..fdbffb4385 100644 --- a/bacula/src/filed/Makefile.in +++ b/bacula/src/filed/Makefile.in @@ -1,7 +1,7 @@ # # Bacula Makefile for the File daemon # -# Copyright (C) 2000-2015 Kern Sibbald +# Copyright (C) 2000-2016 Kern Sibbald # License: BSD 2-Clause; see file LICENSE-FOSS # @MCOMMON@ @@ -30,11 +30,12 @@ first_rule: all dummy: # -SVRSRCS = filed.c authenticate.c acl.c backup.c crypto.c \ +SVRSRCS = filed.c authenticate.c backup.c crypto.c \ estimate.c \ fd_plugins.c accurate.c \ filed_conf.c heartbeat.c hello.c job.c fd_snapshot.c \ - restore.c status.c verify.c verify_vol.c xattr.c + restore.c status.c verify.c verify_vol.c \ + xacl.c xacl_linux.c xacl_osx.c xacl_solaris.c xacl_freebsd.c SVROBJS = $(SVRSRCS:.c=.o) # these are the objects that are changed by the .configure process @@ -59,7 +60,7 @@ all: Makefile bacula-fd @STATIC_FD@ @echo "==== Make of filed is good ====" @echo " " -acl.o: acl.c +xacl.o: xacl.c @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(LZO_INC) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) $(AFS_CFLAGS) $< diff --git a/bacula/src/filed/acl.c b/bacula/src/filed/acl.c deleted file mode 100644 index 5285a4c690..0000000000 --- a/bacula/src/filed/acl.c +++ /dev/null @@ -1,2209 +0,0 @@ -/* - Bacula(R) - The Network Backup Solution - - Copyright (C) 2000-2015 Kern Sibbald - Copyright (C) 2004-2015 Free Software Foundation Europe e.V. - - The original author of Bacula is Kern Sibbald, with contributions - from many others, a complete list can be found in the file AUTHORS. - - You may use this file and others of this release according to the - license defined in the LICENSE file, which includes the Affero General - Public License, v3.0 ("AGPLv3") and some additional permissions and - terms pursuant to its AGPLv3 Section 7. - - This notice must be preserved when any source code is - conveyed and/or propagated. - - Bacula(R) is a registered trademark of Kern Sibbald. -*/ -/* - * Functions to handle ACLs for bacula. - * - * Currently we support the following OSes: - * - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface) - * - Darwin - * - FreeBSD (POSIX and NFSv4/ZFS acls) - * - GNU Hurd - * - HPUX - * - IRIX - * - Linux - * - Solaris (POSIX and NFSv4/ZFS acls) - * - Tru64 - * - * Next to OS specific acls we support AFS acls using the pioctl interface. - * - * We handle two different types of ACLs: access and default ACLS. - * On most systems that support default ACLs they only apply to directories. - * - * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs - * independently, while others (eg. Solaris) provide both in one call. - * - * The Filed saves ACLs in their native format and uses different streams - * for all different platforms. Currently we only allow ACLs to be restored - * which were saved in the native format of the platform they are extracted - * on. Later on we might add conversion functions for mapping from one - * platform to an other or allow restores of systems that use the same - * native format. - * - * Its also interesting to see what the exact format of acl text is on - * certain platforms and if they use they same encoding we might allow - * different platform streams to be decoded on an other similar platform. - * - * Original written by Preben 'Peppe' Guldberg, December 2004 - * - */ - -#include "bacula.h" -#include "filed.h" - -#if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL) -/* - * Entry points when compiled without support for ACLs or on an unsupported platform. - */ -bool backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - Jmsg(jcr, M_FATAL, 0, "ACL backup requested but not configured in Bacula.\n"); - return false; -} - -bacl_rtn_code restore_acl_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - Jmsg(jcr, M_FATAL, 0, "ACL backup requested but not configured in Bacula.\n"); - return bacl_rtn_fatal; -} -#else -/* - * Send an ACL stream to the SD. - */ -static bacl_rtn_code send_acl_stream(JCR *jcr, int stream) -{ - BSOCK *sd = jcr->store_bsock; - POOLMEM *msgsave; -#ifdef FD_NO_SEND_TEST - return bacl_rtn_ok; -#endif - - /* - * Sanity check - */ - if (jcr->acl_ctx->content_length <= 0) { - return bacl_rtn_ok; - } - - /* - * Send header - */ - if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); - return bacl_rtn_fatal; - } - - /* - * Send the buffer to the storage deamon - */ - Dmsg1(400, "Backing up ACL <%s>\n", jcr->acl_ctx->content); - msgsave = sd->msg; - sd->msg = jcr->acl_ctx->content; - sd->msglen = jcr->acl_ctx->content_length + 1; - if (!sd->send()) { - sd->msg = msgsave; - sd->msglen = 0; - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); - return bacl_rtn_fatal; - } - - jcr->JobBytes += sd->msglen; - sd->msg = msgsave; - if (!sd->signal(BNET_EOD)) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); - return bacl_rtn_fatal; - } - - Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname); - return bacl_rtn_ok; -} - -/* - * First the native ACLs. - */ -#if defined(HAVE_ACL) -#if defined(HAVE_AIX_OS) - -#if defined(HAVE_EXTENDED_ACL) - -#include -#include - -static bool acl_is_trivial(struct acl *acl) -{ - return (acl_last(acl) != acl->acl_ext ? false : true); -} - -static bool acl_nfs4_is_trivial(nfs4_acl_int_t *acl) -{ - int i; - int count = acl->aclEntryN; - nfs4_ace_int_t *ace; - - for (i = 0; i < count; i++) { - ace = &acl->aclEntry[i]; - if (!((ace->flags & ACE4_ID_SPECIAL) != 0 && - (ace->aceWho.special_whoid == ACE4_WHO_OWNER || - ace->aceWho.special_whoid == ACE4_WHO_GROUP || - ace->aceWho.special_whoid == ACE4_WHO_EVERYONE) && - ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE && - ace->aceFlags == 0 && - (ace->aceMask & ~(ACE4_READ_DATA | - ACE4_LIST_DIRECTORY | - ACE4_WRITE_DATA | - ACE4_ADD_FILE | - ACE4_EXECUTE)) == 0)) { - return false; - } - } - return true; -} - -/* - * Define the supported ACL streams for this OS - */ -static int os_access_acl_streams[3] = { - STREAM_ACL_AIX_TEXT, - STREAM_ACL_AIX_AIXC, - STREAM_ACL_AIX_NFS4 -}; -static int os_default_acl_streams[1] = { - -1 -}; - -static bacl_rtn_code aix_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - mode_t mode; - acl_type_t type; - size_t aclsize, acltxtsize; - bacl_rtn_code retval = bacl_rtn_error; - POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE); - - /* - * First see how big the buffers should be. - */ - memset(&type, 0, sizeof(acl_type_t)); - type.u64 = ACL_ANY; - if (aclx_get(jcr->last_fname, GET_ACLINFO_ONLY, &type, NULL, &aclsize, &mode) < 0) { - berrno be; - if (errno == ENOENT) { - retval = bacl_rtn_ok; - } else if (errno == ENOSYS) { - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files - * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE; - retval = bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - - /* - * Make sure the buffers are big enough. - */ - aclbuf = check_pool_memory_size(aclbuf, aclsize + 1); - - /* - * Retrieve the ACL info. - */ - if (aclx_get(jcr->last_fname, 0, &type, aclbuf, &aclsize, &mode) < 0) { - berrno be; - if (errno == ENOENT) { - retval = bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - - /* - * See if the acl is non trivial. - */ - switch (type.u64) { - case ACL_AIXC: - if (acl_is_trivial((struct acl *)aclbuf)) { - retval = bacl_rtn_ok; - goto get_out; - } - break; - case ACL_NFS4: - if (acl_nfs4_is_trivial((nfs4_acl_int_t *)aclbuf)) { - retval = bacl_rtn_ok; - goto get_out; - } - break; - default: - Mmsg2(jcr->errmsg, _("Unknown acl type encountered on file \"%s\": %ld\n"), - jcr->last_fname, type.u64); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * We have a non-trivial acl lets convert it into some ASCII form. - */ - acltxtsize = sizeof_pool_memory(jcr->acl_ctx->content); - if (aclx_printStr(jcr->acl_ctx->content, &acltxtsize, aclbuf, - aclsize, type, jcr->last_fname, 0) < 0) { - if (errno == ENOSPC) { - /* - * Our buffer is not big enough, acltxtsize should be updated with the value - * the aclx_printStr really need. So we increase the buffer and try again. - */ - jcr->acl_ctx->content = check_pool_memory_size(jcr->acl_ctx->content, acltxtsize + 1); - if (aclx_printStr(jcr->acl_ctx->content, &acltxtsize, aclbuf, - aclsize, type, jcr->last_fname, 0) < 0) { - Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } else { - Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - jcr->acl_ctx->content_length = strlen(jcr->acl_ctx->content) + 1; - switch (type.u64) { - case ACL_AIXC: - retval = send_acl_stream(jcr, STREAM_ACL_AIX_AIXC); - break; - case ACL_NFS4: - retval = send_acl_stream(jcr, STREAM_ACL_AIX_NFS4); - break; - } - -get_out: - free_pool_memory(aclbuf); - - return retval; -} - -/* - * See if a specific type of ACLs are supported on the filesystem - * the file is located on. - */ -static inline bool aix_query_acl_support(JCR *jcr, - uint64_t aclType, - acl_type_t *pacl_type_info) -{ - unsigned int i; - acl_types_list_t acl_type_list; - size_t acl_type_list_len = sizeof(acl_types_list_t); - - memset(&acl_type_list, 0, sizeof(acl_type_list)); - if (aclx_gettypes(jcr->last_fname, &acl_type_list, &acl_type_list_len)) { - return false; - } - - for (i = 0; i < acl_type_list.num_entries; i++) { - if (acl_type_list.entries[i].u64 == aclType) { - memcpy(pacl_type_info, acl_type_list.entries + i, sizeof(acl_type_t)); - return true; - } - } - return false; -} - -static bacl_rtn_code aix_restore_acl_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - int cnt; - acl_type_t type; - size_t aclsize; - bacl_rtn_code retval = bacl_rtn_error; - POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE); - - switch (stream) { - case STREAM_ACL_AIX_TEXT: - /* - * Handle the old stream using the old system call for now. - */ - if (acl_put(jcr->last_fname, content, 0) != 0) { - retval = bacl_rtn_error; - goto get_out; - } - retval = bacl_rtn_ok; - goto get_out; - case STREAM_ACL_AIX_AIXC: - if (!aix_query_acl_support(jcr, ACL_AIXC, &type)) { - Mmsg1(jcr->errmsg, - _("Trying to restore POSIX acl on file \"%s\" on filesystem without AIXC acl support\n"), - jcr->last_fname); - goto get_out; - } - break; - case STREAM_ACL_AIX_NFS4: - if (!aix_query_acl_support(jcr, ACL_NFS4, &type)) { - Mmsg1(jcr->errmsg, - _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without NFS4 acl support\n"), - jcr->last_fname); - goto get_out; - } - break; - default: - goto get_out; - } - - /* - * Set the acl buffer to an initial size. For now we set it - * to the same size as the ASCII representation. - */ - aclbuf = check_pool_memory_size(aclbuf, content_length); - aclsize = content_length; - if (aclx_scanStr(content, aclbuf, &aclsize, type) < 0) { - berrno be; - if (errno == ENOSPC) { - /* - * The buffer isn't big enough. The man page doesn't say that aclsize - * is updated to the needed size as what is done with aclx_printStr. - * So for now we try to increase the buffer a maximum of 3 times - * and retry the conversion. - */ - for (cnt = 0; cnt < 3; cnt++) { - aclsize = 2 * aclsize; - aclbuf = check_pool_memory_size(aclbuf, aclsize); - - if (aclx_scanStr(content, aclbuf, &aclsize, type) == 0) { - break; - } - - /* - * See why we failed this time, ENOSPC retry if max retries not met, - * otherwise abort. - */ - if (errno == ENOSPC && cnt < 3) { - continue; - } - Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror(errno)); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } else { - Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out - } - } - - if (aclx_put(jcr->last_fname, SET_ACL, type, aclbuf, aclsize, 0) < 0) { - berrno be; - if (errno == ENOENT) { - retval = bacl_rtn_ok; - } else if (ENOSYS) { - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files - * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_RESTORE_NATIVE; - } else { - Mmsg2(jcr->errmsg, _("aclx_put error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - retval = bacl_rtn_ok; - -get_out: - free_pool_memory(aclbuf); - return retval; -} - -#else /* HAVE_EXTENDED_ACL */ - -#include - -/* - * Define the supported ACL streams for this OS - */ -static int os_access_acl_streams[1] = { - STREAM_ACL_AIX_TEXT -}; -static int os_default_acl_streams[1] = { - -1 -}; - -static bacl_rtn_code aix_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - char *acl_text; - - if ((acl_text = acl_get(jcr->last_fname)) != NULL) { - jcr->acl_ctx->content_length = - pm_strcpy(jcr->acl_ctx->content, acl_text); - actuallyfree(acl_text); - return send_acl_stream(jcr, STREAM_ACL_AIX_TEXT); - } - return bacl_rtn_error; -} - -static bacl_rtn_code aix_restore_acl_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - if (acl_put(jcr->last_fname, content, 0) != 0) { - return bacl_rtn_error; - } - return bacl_rtn_ok; -} -#endif /* HAVE_EXTENDED_ACL */ - -/* - * For this OS setup the build and parse function pointer to the OS specific functions. - */ -static bacl_rtn_code (*os_backup_acl_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - aix_backup_acl_streams; -static bacl_rtn_code (*os_restore_acl_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - aix_restore_acl_streams; - -#elif defined(HAVE_DARWIN_OS) || \ - defined(HAVE_FREEBSD_OS) || \ - defined(HAVE_IRIX_OS) || \ - defined(HAVE_OSF1_OS) || \ - defined(HAVE_LINUX_OS) || \ - defined(HAVE_HURD_OS) - -#include - -#ifdef HAVE_SYS_ACL_H -#include -#else -#error "configure failed to detect availability of sys/acl.h" -#endif - -/* - * On IRIX we can get shortened ACLs - */ -#if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS) -#define acl_to_text(acl,len) acl_to_short_text((acl), (len)) -#endif - -/* - * On Linux we can get numeric and/or shorted ACLs - */ -#if defined(HAVE_LINUX_OS) -#if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS) -#define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS) -#elif defined(BACL_WANT_SHORT_ACLS) -#define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE -#elif defined(BACL_WANT_NUMERIC_IDS) -#define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS -#endif -#ifdef BACL_ALTERNATE_TEXT -#include -#define acl_to_text(acl,len) (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT)) -#endif -#endif - -/* - * On FreeBSD we can get numeric ACLs - */ -#if defined(HAVE_FREEBSD_OS) -#if defined(BACL_WANT_NUMERIC_IDS) -#define BACL_ALTERNATE_TEXT ACL_TEXT_NUMERIC_IDS -#endif -#ifdef BACL_ALTERNATE_TEXT -#define acl_to_text(acl,len) (acl_to_text_np((acl), (len), BACL_ALTERNATE_TEXT)) -#endif -#endif - -/* - * Some generic functions used by multiple OSes. - */ -static acl_type_t bac_to_os_acltype(bacl_type acltype) -{ - acl_type_t ostype; - - switch (acltype) { - case BACL_TYPE_ACCESS: - ostype = ACL_TYPE_ACCESS; - break; - case BACL_TYPE_DEFAULT: - ostype = ACL_TYPE_DEFAULT; - break; -#ifdef HAVE_ACL_TYPE_NFS4 - /* - * FreeBSD has an additional acl type named ACL_TYPE_NFS4. - */ - case BACL_TYPE_NFS4: - ostype = ACL_TYPE_NFS4; - break; -#endif -#ifdef HAVE_ACL_TYPE_DEFAULT_DIR - case BACL_TYPE_DEFAULT_DIR: - /* - * TRU64 has an additional acl type named ACL_TYPE_DEFAULT_DIR. - */ - ostype = ACL_TYPE_DEFAULT_DIR; - break; -#endif -#ifdef HAVE_ACL_TYPE_EXTENDED - case BACL_TYPE_EXTENDED: - /* - * MacOSX has an additional acl type named ACL_TYPE_EXTENDED. - */ - ostype = ACL_TYPE_EXTENDED; - break; -#endif - default: - /* - * This should never happen, as the per OS version function only tries acl - * types supported on a certain platform. - */ - ostype = (acl_type_t)ACL_TYPE_NONE; - break; - } - return ostype; -} - -static int acl_count_entries(acl_t acl) -{ - int count = 0; -#if defined(HAVE_FREEBSD_OS) || \ - defined(HAVE_LINUX_OS) || \ - defined(HAVE_HURD_OS) - acl_entry_t ace; - int entry_available; - - entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace); - while (entry_available == 1) { - count++; - entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace); - } -#elif defined(HAVE_IRIX_OS) - count = acl->acl_cnt; -#elif defined(HAVE_OSF1_OS) - count = acl->acl_num; -#elif defined(HAVE_DARWIN_OS) - acl_entry_t ace; - int entry_available; - - entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace); - while (entry_available == 0) { - count++; - entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace); - } -#endif - return count; -} - -#if !defined(HAVE_DARWIN_OS) -/* - * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.) - * There is no need to store those acls as we already store the stat bits too. - */ -static bool acl_is_trivial(acl_t acl) -{ - /* - * acl is trivial if it has only the following entries: - * "user::", - * "group::", - * "other::" - */ - acl_entry_t ace; - acl_tag_t tag; -#if defined(HAVE_FREEBSD_OS) || \ - defined(HAVE_LINUX_OS) || \ - defined(HAVE_HURD_OS) - int entry_available; - - entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace); - while (entry_available == 1) { - /* - * Get the tag type of this acl entry. - * If we fail to get the tagtype we call the acl non-trivial. - */ - if (acl_get_tag_type(ace, &tag) < 0) - return true; - /* - * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell. - */ - if (tag != ACL_USER_OBJ && - tag != ACL_GROUP_OBJ && - tag != ACL_OTHER) - return false; - entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace); - } - return true; -#elif defined(HAVE_IRIX_OS) - int n; - - for (n = 0; n < acl->acl_cnt; n++) { - ace = &acl->acl_entry[n]; - tag = ace->ae_tag; - - /* - * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell. - */ - if (tag != ACL_USER_OBJ && - tag != ACL_GROUP_OBJ && - tag != ACL_OTHER_OBJ) - return false; - } - return true; -#elif defined(HAVE_OSF1_OS) - int count; - - ace = acl->acl_first; - count = acl->acl_num; - - while (count > 0) { - tag = ace->entry->acl_type; - /* - * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell. - */ - if (tag != ACL_USER_OBJ && - tag != ACL_GROUP_OBJ && - tag != ACL_OTHER) - return false; - /* - * On Tru64, perm can also contain non-standard bits such as - * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ... - */ - if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE))) - return false; - ace = ace->next; - count--; - } - return true; -#endif -} -#endif - -/* - * Generic wrapper around acl_get_file call. - */ -static bacl_rtn_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype) -{ - acl_t acl; - acl_type_t ostype; - char *acl_text; - bacl_rtn_code retval = bacl_rtn_ok; - - ostype = bac_to_os_acltype(acltype); - acl = acl_get_file(jcr->last_fname, ostype); - if (acl) { - /* - * From observation, IRIX's acl_get_file() seems to return a - * non-NULL acl with a count field of -1 when a file has no ACL - * defined, while IRIX's acl_to_text() returns NULL when presented - * with such an ACL. - * - * For all other implmentations we check if there are more then - * zero entries in the acl returned. - */ - if (acl_count_entries(acl) <= 0) { - goto get_out; - } - - /* - * Make sure this is not just a trivial ACL. - */ -#ifndef HAVE_DARWIN_OS - if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) { - /* - * The ACLs simply reflect the (already known) standard permissions - * So we don't send an ACL stream to the SD. - */ - goto get_out; - } -#endif - -#if defined(HAVE_FREEBSD_OS) && defined(_PC_ACL_NFS4) - if (acltype == BACL_TYPE_NFS4) { - int trivial; - if (acl_is_trivial_np(acl, &trivial) == 0) { - /* Trivial ACLS are standard permissions. Do not send to SD. */ - if (trivial == 1) { - goto get_out; - } - } - } -#endif - - /* - * Convert the internal acl representation into a text representation. - */ - if ((acl_text = acl_to_text(acl, NULL)) != NULL) { - jcr->acl_ctx->content_length = - pm_strcpy(jcr->acl_ctx->content, acl_text); - acl_free(acl); - acl_free(acl_text); - return bacl_rtn_ok; - } - - berrno be; - Mmsg2(jcr->errmsg, - _("acl_to_text error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - retval = bacl_rtn_error; - } else { - berrno be; - - /* - * Handle errors gracefully. - */ - switch (errno) { - case BACL_ENOTSUP: - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files - * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE; - break; - case ENOENT: - break; - default: - /* Some real error */ - Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - retval = bacl_rtn_error; - break; - } - } - -get_out: - if (acl) { - acl_free(acl); - } - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return retval; -} - -/* - * Generic wrapper around acl_set_file call. - */ -static bacl_rtn_code generic_set_acl_on_os(JCR *jcr, - bacl_type acltype, - char *content, - uint32_t content_length) -{ - acl_t acl; - acl_type_t ostype; - - /* - * If we get empty default ACLs, clear ACLs now - */ - ostype = bac_to_os_acltype(acltype); - if (ostype == ACL_TYPE_DEFAULT && strlen(content) == 0) { - if (acl_delete_def_file(jcr->last_fname) == 0) { - return bacl_rtn_ok; - } - berrno be; - - switch (errno) { - case ENOENT: - return bacl_rtn_ok; - case BACL_ENOTSUP: - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files - * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_RESTORE_NATIVE; - Mmsg1(jcr->errmsg, - _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), - jcr->last_fname); - return bacl_rtn_error; - default: - Mmsg2(jcr->errmsg, - _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - return bacl_rtn_error; - } - } - - acl = acl_from_text(content); - if (acl == NULL) { - berrno be; - - Mmsg2(jcr->errmsg, - _("acl_from_text error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - -#ifndef HAVE_FREEBSD_OS - /* - * FreeBSD always fails acl_valid() - at least on valid input... - * As it does the right thing, given valid input, just ignore acl_valid(). - */ - if (acl_valid(acl) != 0) { - berrno be; - - Mmsg2(jcr->errmsg, - _("acl_valid error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - acl_free(acl); - return bacl_rtn_error; - } -#endif - - /* - * Restore the ACLs, but don't complain about links which really should - * not have attributes, and the file it is linked to may not yet be restored. - * This is only true for the old acl streams as in the new implementation we - * don't save acls of symlinks (which cannot have acls anyhow) - */ - if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) { - berrno be; - switch (errno) { - case ENOENT: - acl_free(acl); - return bacl_rtn_ok; - case BACL_ENOTSUP: - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files - * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_RESTORE_NATIVE; - Mmsg1(jcr->errmsg, - _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - acl_free(acl); - return bacl_rtn_error; - default: - Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - acl_free(acl); - return bacl_rtn_error; - } - } - acl_free(acl); - return bacl_rtn_ok; -} - -/* - * OS specific functions for handling different types of acl streams. - */ -#if defined(HAVE_DARWIN_OS) -/* - * Define the supported ACL streams for this OS - */ -static int os_access_acl_streams[1] = { - STREAM_ACL_DARWIN_ACCESS -}; -static int os_default_acl_streams[1] = { - -1 -}; - -static bacl_rtn_code darwin_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ -#if defined(HAVE_ACL_TYPE_EXTENDED) - /* - * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS) - * and acl_get_file (name, ACL_TYPE_DEFAULT) - * always return NULL / EINVAL. There is no point in making - * these two useless calls. The real ACL is retrieved through - * acl_get_file (name, ACL_TYPE_EXTENDED). - * - * Read access ACLs for files, dirs and links - */ - if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bacl_rtn_fatal) - return bacl_rtn_fatal; -#else - /* - * Read access ACLs for files, dirs and links - */ - if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_rtn_fatal) - return bacl_rtn_fatal; -#endif - - if (jcr->acl_ctx->content_length > 0) { - return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS); - } - return bacl_rtn_ok; -} - -static bacl_rtn_code darwin_restore_acl_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ -#if defined(HAVE_ACL_TYPE_EXTENDED) - return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED, content, content_length); -#else - return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length); -#endif -} - -/* - * For this OS setup the build and parse function pointer to the OS specific functions. - */ -static bacl_rtn_code (*os_backup_acl_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - darwin_backup_acl_streams; -static bacl_rtn_code (*os_restore_acl_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - darwin_restore_acl_streams; - -#elif defined(HAVE_FREEBSD_OS) -/* - * Define the supported ACL streams for these OSes - */ -static int os_access_acl_streams[2] = { - STREAM_ACL_FREEBSD_ACCESS, - STREAM_ACL_FREEBSD_NFS4 -}; -static int os_default_acl_streams[1] = { - STREAM_ACL_FREEBSD_DEFAULT -}; - -static bacl_rtn_code freebsd_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - int acl_enabled = 0; - bacl_type acltype = BACL_TYPE_NONE; - -#if defined(_PC_ACL_NFS4) - /* - * See if filesystem supports NFS4 acls. - */ - acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4); - if (acl_enabled < 0) { - berrno be; - if (errno == ENOENT) { - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - } else if (acl_enabled != 0) { - acltype = BACL_TYPE_NFS4; - } -#endif - - if (acl_enabled == 0) { - /* - * See if filesystem supports POSIX acls. - */ - acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED); - if (acl_enabled < 0) { - berrno be; - if (errno == ENOENT) { - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - } else if (acl_enabled != 0) { - acltype = BACL_TYPE_ACCESS; - } - } - - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files - * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - if (acl_enabled == 0) { - jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE; - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_ok; - } - - /* - * Based on the supported ACLs retrieve and store them. - */ - switch (acltype) { - case BACL_TYPE_NFS4: - /* - * Read NFS4 ACLs for files, dirs and links - */ - if (generic_get_acl_from_os(jcr, BACL_TYPE_NFS4) == bacl_rtn_fatal) - return bacl_rtn_fatal; - - if (jcr->acl_ctx->content_length > 0) { - if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_NFS4) == bacl_rtn_fatal) - return bacl_rtn_fatal; - } - break; - case BACL_TYPE_ACCESS: - /* - * Read access ACLs for files, dirs and links - */ - if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_rtn_fatal) - return bacl_rtn_fatal; - - if (jcr->acl_ctx->content_length > 0) { - if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_ACCESS) == bacl_rtn_fatal) - return bacl_rtn_fatal; - } - - /* - * Directories can have default ACLs too - */ - if (ff_pkt->type == FT_DIREND) { - if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_rtn_fatal) - return bacl_rtn_fatal; - if (jcr->acl_ctx->content_length > 0) { - if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_DEFAULT) == bacl_rtn_fatal) - return bacl_rtn_fatal; - } - } - break; - default: - break; - } - - return bacl_rtn_ok; -} - -static bacl_rtn_code freebsd_restore_acl_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - int acl_enabled = 0; - const char *acl_type_name; - - /* - * First make sure the filesystem supports acls. - */ - switch (stream) { - case STREAM_UNIX_ACCESS_ACL: - case STREAM_ACL_FREEBSD_ACCESS: - case STREAM_UNIX_DEFAULT_ACL: - case STREAM_ACL_FREEBSD_DEFAULT: - acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED); - acl_type_name = "POSIX"; - break; - case STREAM_ACL_FREEBSD_NFS4: -#if defined(_PC_ACL_NFS4) - acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4); -#endif - acl_type_name = "NFS4"; - break; - default: - acl_type_name = "unknown"; - break; - } - - if (acl_enabled < 0) { - berrno be; - if (errno == ENOENT) { - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - } else if (acl_enabled == 0) { - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files - * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE; - Mmsg2(jcr->errmsg, - _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"), - jcr->last_fname, acl_type_name); - return bacl_rtn_error; - } - - /* - * Restore the ACLs. - */ - switch (stream) { - case STREAM_UNIX_ACCESS_ACL: - case STREAM_ACL_FREEBSD_ACCESS: - return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length); - case STREAM_UNIX_DEFAULT_ACL: - case STREAM_ACL_FREEBSD_DEFAULT: - return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT, content, content_length); - case STREAM_ACL_FREEBSD_NFS4: - return generic_set_acl_on_os(jcr, BACL_TYPE_NFS4, content, content_length); - default: - break; - } - return bacl_rtn_error; -} - -/* - * For this OSes setup the build and parse function pointer to the OS specific functions. - */ -static bacl_rtn_code (*os_backup_acl_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - freebsd_backup_acl_streams; -static bacl_rtn_code (*os_restore_acl_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - freebsd_restore_acl_streams; - -#elif defined(HAVE_IRIX_OS) || \ - defined(HAVE_LINUX_OS) || \ - defined(HAVE_HURD_OS) -/* - * Define the supported ACL streams for these OSes - */ -#if defined(HAVE_IRIX_OS) -static int os_access_acl_streams[1] = {STREAM_ACL_IRIX_ACCESS_ACL}; -static int os_default_acl_streams[1] = {STREAM_ACL_IRIX_DEFAULT_ACL}; -#elif defined(HAVE_LINUX_OS) -static int os_access_acl_streams[1] = {STREAM_ACL_LINUX_ACCESS}; -static int os_default_acl_streams[1] = {STREAM_ACL_LINUX_DEFAULT}; -#elif defined(HAVE_HURD_OS) -static int os_access_acl_streams[1] = {STREAM_ACL_HURD_ACCESS}; -static int os_default_acl_streams[1] = {STREAM_ACL_HURD_DEFAULT}; -#endif - -static bacl_rtn_code generic_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - /* - * Read access ACLs for files, dirs and links - */ - if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_rtn_fatal) - return bacl_rtn_fatal; - - if (jcr->acl_ctx->content_length > 0) { - if (send_acl_stream(jcr, os_access_acl_streams[0]) == bacl_rtn_fatal) - return bacl_rtn_fatal; - } - - /* - * Directories can have default ACLs too - */ - if (ff_pkt->type == FT_DIREND) { - if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_rtn_fatal) - return bacl_rtn_fatal; - if (jcr->acl_ctx->content_length > 0) { - if (send_acl_stream(jcr, os_default_acl_streams[0]) == bacl_rtn_fatal) - return bacl_rtn_fatal; - } - } - return bacl_rtn_ok; -} - -static bacl_rtn_code generic_restore_acl_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - unsigned int cnt; - - switch (stream) { - case STREAM_UNIX_ACCESS_ACL: - return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length); - case STREAM_UNIX_DEFAULT_ACL: - return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT, content, content_length); - default: - /* - * See what type of acl it is. - */ - for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) { - if (os_access_acl_streams[cnt] == stream) { - return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length); - } - } - for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) { - if (os_default_acl_streams[cnt] == stream) { - return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT, content, content_length); - } - } - break; - } - return bacl_rtn_error; -} - -/* - * For this OSes setup the build and parse function pointer to the OS specific functions. - */ -static bacl_rtn_code (*os_backup_acl_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - generic_backup_acl_streams; -static bacl_rtn_code (*os_restore_acl_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - generic_restore_acl_streams; - -#elif defined(HAVE_OSF1_OS) - -/* - * Define the supported ACL streams for this OS - */ -static int os_access_acl_streams[1] = { - STREAM_ACL_TRU64_ACCESS -}; -static int os_default_acl_streams[2] = { - STREAM_ACL_TRU64_DEFAULT, - STREAM_ACL_TRU64_DEFAULT_DIR -}; - -static bacl_rtn_code tru64_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - /* - * Read access ACLs for files, dirs and links - */ - if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_rtn_fatal) { - return bacl_rtn_error; - if (jcr->acl_ctx->content_length > 0) { - if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL)) - return bacl_rtn_error; - } - /* - * Directories can have default ACLs too - */ - if (ff_pkt->type == FT_DIREND) { - if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_rtn_fatal) { - return bacl_rtn_error; - if (jcr->acl_ctx->content_length > 0) { - if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL)) - return bacl_rtn_error; - } - /* - * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls. - * This is an inherited acl for all subdirs. - * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM - * Section 21.5 Default ACLs - */ - if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR) == bacl_rtn_fatal) { - return bacl_rtn_error; - if (jcr->acl_ctx->content_length > 0) { - if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL)) - return bacl_rtn_error; - } - } - return bacl_rtn_ok; -} - -static bacl_rtn_code tru64_restore_acl_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - switch (stream) { - case STREAM_UNIX_ACCESS_ACL: - case STREAM_ACL_TRU64_ACCESS_ACL: - return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length); - case STREAM_UNIX_DEFAULT_ACL: - case STREAM_ACL_TRU64_DEFAULT_ACL: - return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT, content, content_length); - case STREAM_ACL_TRU64_DEFAULT_DIR_ACL: - return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR, content, content_length); -} - -/* - * For this OS setup the build and parse function pointer to the OS specific functions. - */ -static bacl_rtn_code (*os_backup_acl_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - tru64_backup_acl_streams; -static bacl_rtn_code (*os_restore_acl_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - tru64_restore_acl_streams; - -#endif - -#elif defined(HAVE_HPUX_OS) -#ifdef HAVE_SYS_ACL_H -#include -#else -#error "configure failed to detect availability of sys/acl.h" -#endif - -#include - -/* - * Define the supported ACL streams for this OS - */ -static int os_access_acl_streams[1] = { - STREAM_ACL_HPUX_ACL_ENTRY -}; -static int os_default_acl_streams[1] = { - -1 -}; - -/* - * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.) - * There is no need to store those acls as we already store the stat bits too. - */ -static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb) -{ - int n; - struct acl_entry ace - - for (n = 0; n < count; n++) { - ace = entries[n]; - /* - * See if this acl just is the stat mode in acl form. - */ - if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) || - (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) || - (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP))) - return false; - } - return true; -} - -/* - * OS specific functions for handling different types of acl streams. - */ -static bacl_rtn_code hpux_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - int n; - struct acl_entry acls[NACLENTRIES]; - char *acl_text; - - if ((n = getacl(jcr->last_fname, 0, acls)) < 0) { - berrno be; - switch (errno) { - case BACL_ENOTSUP: - /* - * Not supported, just pretend there is nothing to see - * - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files - * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE; - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_ok; - case ENOENT: - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_ok; - default: - Mmsg2(jcr->errmsg, _("getacl error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_error; - } - } - if (n == 0) { - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_ok; - } - if ((n = getacl(jcr->last_fname, n, acls)) > 0) { - if (acl_is_trivial(n, acls, ff_pkt->statp)) { - /* - * The ACLs simply reflect the (already known) standard permissions - * So we don't send an ACL stream to the SD. - */ - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_ok; - } - if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) { - jcr->acl_ctx->content_length = - pm_strcpy(jcr->acl_ctx->content, acl_text); - actuallyfree(acl_text); - - return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY); - } - - berrno be; - Mmsg2(jcr->errmsg, _("acltostr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - return bacl_rtn_error; -} - -static bacl_rtn_code hpux_restore_acl_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - int n, stat; - struct acl_entry acls[NACLENTRIES]; - - n = strtoacl(content, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP); - if (n <= 0) { - berrno be; - Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - if (strtoacl(content, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) { - berrno be; - Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - /* - * Restore the ACLs, but don't complain about links which really should - * not have attributes, and the file it is linked to may not yet be restored. - * This is only true for the old acl streams as in the new implementation we - * don't save acls of symlinks (which cannot have acls anyhow) - */ - if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) { - berrno be; - - switch (errno) { - case ENOENT: - return bacl_rtn_ok; - case BACL_ENOTSUP: - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files - * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE; - Mmsg1(jcr->errmsg, - _("setacl error on file \"%s\": filesystem doesn't support ACLs\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - default: - Mmsg2(jcr->errmsg, _("setacl error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - } - return bacl_rtn_ok; -} - -/* - * For this OS setup the build and parse function pointer to the OS specific functions. - */ -static bacl_rtn_code (*os_backup_acl_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - hpux_backup_acl_streams; - -static bacl_rtn_code (*os_restore_acl_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - hpux_restore_acl_streams; - -#elif defined(HAVE_SUN_OS) -#ifdef HAVE_SYS_ACL_H -#include -#else -#error "configure failed to detect availability of sys/acl.h" -#endif - -#if defined(HAVE_EXTENDED_ACL) -/* - * We define some internals of the Solaris acl libs here as those - * are not exposed yet. Probably because they want us to see the - * acls as opague data. But as we need to support different platforms - * and versions of Solaris we need to expose some data to be able - * to determine the type of acl used to stuff it into the correct - * data stream. I know this is far from portable, but maybe the - * proper interface is exposed later on and we can get ride of - * this kludge. Newer versions of Solaris include sys/acl_impl.h - * which has implementation details of acls, if thats included we - * don't have to define it ourself. - */ -#if !defined(_SYS_ACL_IMPL_H) -typedef enum acl_type { - ACLENT_T = 0, - ACE_T = 1 -} acl_type_t; -#endif - -/* - * Two external references to functions in the libsec library function not in current include files. - */ -extern "C" { -int acl_type(acl_t *); -char *acl_strerror(int); -} - -/* - * Define the supported ACL streams for this OS - */ -static int os_access_acl_streams[2] = { - STREAM_ACL_SOLARIS_POSIX, - STREAM_ACL_SOLARIS_NFS4 -}; -static int os_default_acl_streams[1] = { - -1 -}; - -/* - * As the new libsec interface with acl_totext and acl_fromtext also handles - * the old format from acltotext we can use the new functions even - * for acls retrieved and stored in the database with older fd versions. If the - * new interface is not defined (Solaris 9 and older we fall back to the old code) - */ -static bacl_rtn_code solaris_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - int acl_enabled, flags; - acl_t *aclp; - char *acl_text; - bacl_rtn_code stream_status = bacl_rtn_error; - - /* - * See if filesystem supports acls. - */ - acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED); - if (acl_enabled == 0) { - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files - * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE; - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_ok; - } - if (acl_enabled < 0) { - berrno be; - if (errno == ENOENT) { - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - } - - /* - * Get ACL info: don't bother allocating space if there is only a trivial ACL. - */ - if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) { - berrno be; - if (errno == ENOENT) { - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"), - jcr->last_fname, acl_strerror(errno)); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - } - - if (!aclp) { - /* - * The ACLs simply reflect the (already known) standard permissions - * So we don't send an ACL stream to the SD. - */ - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_ok; - } - -#if defined(ACL_SID_FMT) - /* - * New format flag added in newer Solaris versions. - */ - flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT; -#else - flags = ACL_APPEND_ID | ACL_COMPACT_FMT; -#endif /* ACL_SID_FMT */ - - if ((acl_text = acl_totext(aclp, flags)) != NULL) { - jcr->acl_ctx->content_length = - pm_strcpy(jcr->acl_ctx->content, acl_text); - actuallyfree(acl_text); - - switch (acl_type(aclp)) { - case ACLENT_T: - stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_POSIX); - break; - case ACE_T: - stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_NFS4); - break; - default: - break; - } - - acl_free(aclp); - } - return stream_status; -} - -static bacl_rtn_code solaris_restore_acl_streams(JCR *jcr, int stream, char *content, - uint32_t content_length) -{ - acl_t *aclp; - int acl_enabled, error; - - if (stream != STREAM_UNIX_ACCESS_ACL || stream != STREAM_ACL_SOLARIS_POSIX || - stream != STREAM_ACL_SOLARIS_NFS4) { - return bacl_rtn_error; - } - - /* - * First make sure the filesystem supports acls. - */ - acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED); - if (acl_enabled == 0) { - /* - * If the filesystem reports it doesn't support ACLs we clear the - * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files - * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again - * when we change from one filesystem to an other. - */ - jcr->acl_ctx->flags &= ~BACL_FLAG_RESTORE_NATIVE; - Mmsg1(jcr->errmsg, - _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"), - jcr->last_fname); - return bacl_rtn_error; - } else if (acl_enabled < 0) { - berrno be; - if (errno == ENOENT) { - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - } - /* - * On a filesystem with ACL support make sure this particular ACL type can be restored. - */ - switch (stream) { - case STREAM_ACL_SOLARIS_POSIX: - /* - * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support. - */ - if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) { - Mmsg1(jcr->errmsg, - _("Trying to restore POSIX acl on file \"%s\" on filesystem without aclent acl support\n"), - jcr->last_fname); - return bacl_rtn_error; - } - break; - case STREAM_ACL_SOLARIS_NFS4: - /* - * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support. - */ - if ((acl_enabled & _ACL_ACE_ENABLED) == 0) { - Mmsg1(jcr->errmsg, - _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without ace acl support\n"), - jcr->last_fname); - return bacl_rtn_error; - } - break; - default: - /* - * Stream id which doesn't describe the type of acl which is encoded. - */ - break; - } - - if ((error = acl_fromtext(content, &aclp)) != 0) { - Mmsg2(jcr->errmsg, - _("acl_fromtext error on file \"%s\": ERR=%s\n"), - jcr->last_fname, acl_strerror(error)); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - - /* - * Validate that the conversion gave us the correct acl type. - */ - switch (stream) { - case STREAM_ACL_SOLARIS_POSIX: - if (acl_type(aclp) != ACLENT_T) { - Mmsg1(jcr->errmsg, - _("wrong encoding of acl type in acl stream on file \"%s\"\n"), - jcr->last_fname); - return bacl_rtn_error; - } - break; - case STREAM_ACL_SOLARIS_NFS4: - if (acl_type(aclp) != ACE_T) { - Mmsg1(jcr->errmsg, - _("wrong encoding of acl type in acl stream on file \"%s\"\n"), - jcr->last_fname); - return bacl_rtn_error; - } - break; - default: - /* - * Stream id which doesn't describe the type of acl which is encoded. - */ - break; - } - - /* - * Restore the ACLs, but don't complain about links which really should - * not have attributes, and the file it is linked to may not yet be restored. - * This is only true for the old acl streams as in the new implementation we - * don't save acls of symlinks (which cannot have acls anyhow) - */ - if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) { - if (errno == ENOENT) { - acl_free(aclp); - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"), - jcr->last_fname, acl_strerror(error)); - Dmsg1(100, "%s", jcr->errmsg); - acl_free(aclp); - return bacl_rtn_error; - } - } - - acl_free(aclp); - return bacl_rtn_ok; -} - -#else /* HAVE_EXTENDED_ACL */ - -/* - * Define the supported ACL streams for this OS - */ -static int os_access_acl_streams[1] = { - STREAM_ACL_SOLARIS_POSIX -}; -static int os_default_acl_streams[1] = { - -1 -}; - -/* - * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.) - * There is no need to store those acls as we already store the stat bits too. - */ -static bool acl_is_trivial(int count, aclent_t *entries) -{ - int n; - aclent_t *ace; - - for (n = 0; n < count; n++) { - ace = &entries[n]; - - if (!(ace->a_type == USER_OBJ || - ace->a_type == GROUP_OBJ || - ace->a_type == OTHER_OBJ || - ace->a_type == CLASS_OBJ)) - return false; - } - return true; -} - -/* - * OS specific functions for handling different types of acl streams. - */ -static bacl_rtn_code solaris_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - int n; - aclent_t *acls; - char *acl_text; - - n = acl(jcr->last_fname, GETACLCNT, 0, NULL); - if (n < MIN_ACL_ENTRIES) { - return bacl_rtn_error; - } - - acls = (aclent_t *)malloc(n * sizeof(aclent_t)); - if (acl(jcr->last_fname, GETACL, n, acls) == n) { - if (acl_is_trivial(n, acls)) { - /* - * The ACLs simply reflect the (already known) standard permissions - * So we don't send an ACL stream to the SD. - */ - free(acls); - pm_strcpy(jcr->acl_ctx->content, ""); - jcr->acl_ctx->content_length = 0; - return bacl_rtn_ok; - } - - if ((acl_text = acltotext(acls, n)) != NULL) { - jcr->acl_ctx->content_length = - pm_strcpy(jcr->acl_ctx->content, acl_text); - actuallyfree(acl_text); - free(acls); - return send_acl_stream(jcr, STREAM_ACL_SOLARIS_POSIX); - } - - berrno be; - Mmsg2(jcr->errmsg, _("acltotext error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - - free(acls); - return bacl_rtn_error; -} - -static bacl_rtn_code solaris_restore_acl_streams(JCR *jcr, int stream, char *content, - uint32_t content_length) -{ - int n; - aclent_t *acls; - - acls = aclfromtext(content, &n); - if (!acls) { - berrno be; - - Mmsg2(jcr->errmsg, _("aclfromtext error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - - /* - * Restore the ACLs, but don't complain about links which really should - * not have attributes, and the file it is linked to may not yet be restored. - */ - if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) { - berrno be; - if (errno == ENOENT) { - actuallyfree(acls); - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("acl(SETACL) error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - actuallyfree(acls); - return bacl_rtn_error; - } - } - actuallyfree(acls); - return bacl_rtn_ok; -} -#endif /* HAVE_EXTENDED_ACL */ - -/* - * For this OS setup the build and parse function pointer to the OS specific functions. - */ -static bacl_rtn_code (*os_backup_acl_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - solaris_backup_acl_streams; -static bacl_rtn_code (*os_restore_acl_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - solaris_restore_acl_streams; - -#endif /* HAVE_SUN_OS */ -#endif /* HAVE_ACL */ - -#if defined(HAVE_AFS_ACL) - -#if defined(HAVE_AFS_AFSINT_H) && defined(HAVE_AFS_VENUS_H) -#include -#include -#else -#error "configure failed to detect availability of afs/afsint.h and/or afs/venus.h" -#endif - -/* - * External references to functions in the libsys library function not in current include files. - */ -extern "C" { -long pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow); -} - -static bacl_rtn_code afs_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - int error; - struct ViceIoctl vip; - char acl_text[BUFSIZ]; - - /* - * AFS ACLs can only be set on a directory, so no need to try to - * request them for anything other then that. - */ - if (ff_pkt->type != FT_DIREND) { - return bacl_rtn_ok; - } - - vip.in = NULL; - vip.in_size = 0; - vip.out = acl_text; - vip.out_size = sizeof(acl_text); - memset((caddr_t)acl_text, 0, sizeof(acl_text)); - - if ((error = pioctl(jcr->last_fname, VIOCGETAL, &vip, 0)) < 0) { - berrno be; - Mmsg2(jcr->errmsg, _("pioctl VIOCGETAL error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - jcr->acl_ctx->content_length = pm_strcpy(jcr->acl_ctx->content, acl_text); - return send_acl_stream(jcr, STREAM_ACL_AFS_TEXT); -} - -static bacl_rtn_code afs_restore_acl_stream(JCR *jcr, int stream, char *content, - uint32_t content_length) -{ - int error; - struct ViceIoctl vip; - - vip.in = content; - vip.in_size = content_length; - vip.out = NULL; - vip.out_size = 0; - - if ((error = pioctl(jcr->last_fname, VIOCSETAL, &vip, 0)) < 0) { - berrno be; - Mmsg2(jcr->errmsg, _("pioctl VIOCSETAL error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - return bacl_rtn_ok; -} -#endif /* HAVE_AFS_ACL */ - -/* - * Entry points when compiled with support for ACLs on a supported platform. - */ - -/* - * Read and send an ACL for the last encountered file. - */ -bool backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - bacl_rtn_code rtn = bacl_rtn_error; - - if (!(ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK && !ff_pkt->cmd_plugin)) { - return true; /* No acl request */ - } - jcr->errmsg[0] = 0; - - /* - * See if we are changing from one device to an other. - * We save the current device we are scanning and compare - * it with the current st_dev in the last stat performed on - * the file we are currently storing. - */ - if (jcr->acl_ctx->current_dev != ff_pkt->statp.st_dev) { - /* - * Reset the acl save flags. - */ - jcr->acl_ctx->flags = 0; - -#if defined(HAVE_AFS_ACL) - /* - * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem - * Set the BACL_FLAG_SAVE_AFS flag if it is. If not set the BACL_FLAG_SAVE_NATIVE flag. - */ - if (fstype_cmp(ff_pkt, "afs")) { - jcr->acl_ctx->flags |= BACL_FLAG_SAVE_AFS; - } else { - jcr->acl_ctx->flags |= BACL_FLAG_SAVE_NATIVE; - } -#else - jcr->acl_ctx->flags |= BACL_FLAG_SAVE_NATIVE; -#endif - - /* - * Save that we started scanning a new filesystem. - */ - jcr->acl_ctx->current_dev = ff_pkt->statp.st_dev; - } - -#ifdef HAVE_AFS_ACL - /* - * See if the BACL_FLAG_SAVE_AFS flag is set which lets us know if we should - * save AFS ACLs. - */ - if (jcr->acl_ctx->flags & BACL_FLAG_SAVE_AFS) { - rtn = afs_backup_acl_streams(jcr, ff_pkt); - goto get_out; - } -#endif - -#ifdef HAVE_ACL - /* - * See if the BACL_FLAG_SAVE_NATIVE flag is set which lets us know if we should - * save native ACLs. - */ - if (jcr->acl_ctx->flags & BACL_FLAG_SAVE_NATIVE) { - /* - * Call the appropriate function. - */ - if (os_backup_acl_streams) { - rtn = os_backup_acl_streams(jcr, ff_pkt); - goto get_out; - } - } else { - return true; - } -#endif - -get_out: - switch (rtn) { - case bacl_rtn_fatal: - return false; - case bacl_rtn_ok: - return true; - case bacl_rtn_error: - if (jcr->acl_ctx->nr_errors < ACL_MAX_ERROR_PRINT_PER_JOB) { - if (jcr->errmsg[0]) { - Jmsg(jcr, M_WARNING, 0, "Operating system ACLs not configured.\n"); - } else { - Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); - } - jcr->acl_ctx->nr_errors++; - } - return true; - } - /* Theoretically we cannot get here */ - return false; -} - -bacl_rtn_code restore_acl_streams(JCR *jcr, int stream, - char *content, uint32_t content_length) -{ - int ret; - struct stat st; - unsigned int cnt; - - /* - * See if we are changing from one device to an other. - * We save the current device we are restoring to and compare - * it with the current st_dev in the last stat performed on - * the file we are currently restoring. - */ - ret = lstat(jcr->last_fname, &st); - if (ret < 0) { - berrno be; - if (errno == ENOENT) { - return bacl_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("Unable to stat file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bacl_rtn_error; - } - } - if (jcr->acl_ctx->current_dev != st.st_dev) { - /* - * Reset the acl save flags. - */ - jcr->acl_ctx->flags = 0; - -#if defined(HAVE_AFS_ACL) - /* - * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem - * Set the BACL_FLAG_RESTORE_AFS flag if it is. If not set the BACL_FLAG_RETORE_NATIVE flag. - */ - if (fstype_cmp(ff_pkt, "afs")) { - jcr->acl_ctx->flags |= BACL_FLAG_RESTORE_AFS; - } else { - jcr->acl_ctx->flags |= BACL_FLAG_RESTORE_NATIVE; - } -#else - jcr->acl_ctx->flags |= BACL_FLAG_RESTORE_NATIVE; -#endif - - /* Save that we started restoring to a new filesystem. */ - jcr->acl_ctx->current_dev = st.st_dev; - } - - switch (stream) { -#ifdef HAVE_AFS_ACL - case STREAM_ACL_AFS_TEXT: - if (jcr->acl_ctx->flags & BACL_FLAG_RESTORE_AFS) { - return afs_restore_acl_stream(jcr, stream, content, content_length); - } else { - /* - * Increment error count but don't log an error again for the same filesystem. - */ - jcr->acl_ctx->nr_errors++; - return bacl_rtn_ok; - } -#endif - -#ifdef HAVE_ACL - case STREAM_UNIX_ACCESS_ACL: - case STREAM_UNIX_DEFAULT_ACL: - /* - * Handle legacy ACL streams. - */ - if ((jcr->acl_ctx->flags & BACL_FLAG_RESTORE_NATIVE) && os_restore_acl_streams) { - return os_restore_acl_streams(jcr, stream, content, content_length); - } else { - /* - * Increment error count but don't log an error again for the same filesystem. - */ - jcr->acl_ctx->nr_errors++; - return bacl_rtn_ok; - } - break; - default: - if ((jcr->acl_ctx->flags & BACL_FLAG_RESTORE_NATIVE) && os_restore_acl_streams) { - /* - * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS. - */ - for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) { - if (os_access_acl_streams[cnt] == stream) { - return os_restore_acl_streams(jcr, stream, content, content_length); - } - } - /* - * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS. - */ - for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) { - if (os_default_acl_streams[cnt] == stream) { - return os_restore_acl_streams(jcr, stream, content, content_length); - } - } - } else { - /* - * Increment error count but don't log an error again for the same filesystem. - */ - jcr->acl_ctx->nr_errors++; - return bacl_rtn_ok; - } - break; -#else - default: - break; -#endif - } - Qmsg2(jcr, M_WARNING, 0, _("Cannot restore ACLs of %s - incompatible acl stream encountered - %d\n"), - jcr->last_fname, stream); - return bacl_rtn_error; -} -#endif diff --git a/bacula/src/filed/acl.h b/bacula/src/filed/acl.h deleted file mode 100644 index 716a7d5653..0000000000 --- a/bacula/src/filed/acl.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - Bacula(R) - The Network Backup Solution - - Copyright (C) 2000-2015 Kern Sibbald - Copyright (C) 2004-2014 Free Software Foundation Europe e.V. - - The original author of Bacula is Kern Sibbald, with contributions - from many others, a complete list can be found in the file AUTHORS. - - You may use this file and others of this release according to the - license defined in the LICENSE file, which includes the Affero General - Public License, v3.0 ("AGPLv3") and some additional permissions and - terms pursuant to its AGPLv3 Section 7. - - This notice must be preserved when any source code is - conveyed and/or propagated. - - Bacula(R) is a registered trademark of Kern Sibbald. -*/ -/* - * Properties we use for getting and setting ACLs. - */ - -#ifndef __BACL_H_ -#define __BACL_H_ - -/* Global JCR data */ -struct acl_ctx_t { - uint32_t nr_errors; - uint32_t flags; /* See BACL_FLAG_* */ - uint32_t current_dev; - uint32_t content_length; - POOLMEM *content; -}; - -/* - * We support the following types of ACLs - */ -typedef enum { - BACL_TYPE_NONE = 0, - BACL_TYPE_ACCESS = 1, - BACL_TYPE_DEFAULT = 2, - BACL_TYPE_DEFAULT_DIR = 3, - BACL_TYPE_EXTENDED = 4, - BACL_TYPE_NFS4 = 5 -} bacl_type; - -#define BACL_FLAG_SAVE_NATIVE 0x01 -#define BACL_FLAG_SAVE_AFS 0x02 -#define BACL_FLAG_RESTORE_NATIVE 0x04 -#define BACL_FLAG_RESTORE_AFS 0x08 - -/* - * Ensure we have none - */ -#ifndef ACL_TYPE_NONE -#define ACL_TYPE_NONE 0x0 -#endif - -#ifdef HAVE_IRIX_OS -#define BACL_ENOTSUP ENOSYS -#else -#define BACL_ENOTSUP EOPNOTSUPP -#endif /* HAVE_IRIX_OS */ - -#endif /* __BACL_H_ */ diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index 9d2b58f106..a036cb6a07 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -1,7 +1,7 @@ /* Bacula(R) - The Network Backup Solution - Copyright (C) 2000-2015 Kern Sibbald + Copyright (C) 2000-2016 Kern Sibbald The original author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -11,13 +11,13 @@ Public License, v3.0 ("AGPLv3") and some additional permissions and terms pursuant to its AGPLv3 Section 7. - This notice must be preserved when any source code is + This notice must be preserved when any source code is conveyed and/or propagated. Bacula(R) is a registered trademark of Kern Sibbald. */ /* - * Bacula File Daemon backup.c send file attributes and data + * Bacula File Daemon backup.c send file attributes and data * to the Storage daemon. * * Kern Sibbald, March MM @@ -27,19 +27,19 @@ #include "bacula.h" #include "filed.h" #include "backup.h" - + #ifdef HAVE_LZO const bool have_lzo = true; -#else +#else const bool have_lzo = false; -#endif - +#endif + #ifdef HAVE_LIBZ const bool have_libz = true; -#else +#else const bool have_libz = false; -#endif - +#endif + /* Forward referenced functions */ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level); static int send_data(bctx_t &bctx, int stream); @@ -82,7 +82,7 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) if (client) { buf_size = client->max_network_buffer_size; } else { - buf_size = 0; /* use default */ + buf_size = 0; /* use default */ } if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) { jcr->setJobStatus(JS_ErrorTerminated); @@ -99,7 +99,7 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) * same output buffer can be used without growing it. * * For LZO1X compression the recommended value is : - * output_block_size = input_block_size + (input_block_size / 16) + 64 + 3 + sizeof(comp_stream_header) + * output_block_size = input_block_size + (input_block_size / 16) + 64 + 3 + sizeof(comp_stream_header) * * The zlib compression workset is initialized here to minimize * the "per file" load. The jcr member is only set, if the init @@ -124,9 +124,9 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) pZlibStream->state = Z_NULL; if (deflateInit(pZlibStream, Z_DEFAULT_COMPRESSION) == Z_OK) { - jcr->pZLIB_compress_workset = pZlibStream; + jcr->pZLIB_compress_workset = pZlibStream; } else { - free (pZlibStream); + free (pZlibStream); } } #endif @@ -135,9 +135,9 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) lzo_voidp pLzoMem = (lzo_voidp) malloc(LZO1X_1_MEM_COMPRESS); if (pLzoMem) { if (lzo_init() == LZO_E_OK) { - jcr->LZO_compress_workset = pLzoMem; + jcr->LZO_compress_workset = pLzoMem; } else { - free (pLzoMem); + free (pLzoMem); } } #endif @@ -155,58 +155,36 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) } start_heartbeat_monitor(jcr); -#ifdef HAVE_ACL - jcr->acl_ctx = (acl_ctx_t *)malloc(sizeof(acl_ctx_t)); - memset(jcr->acl_ctx, 0, sizeof(acl_ctx_t)); - jcr->acl_ctx->content = get_pool_memory(PM_MESSAGE); -#endif -#ifdef HAVE_XATTR - jcr->xattr_ctx = (xattr_ctx_t *)malloc(sizeof(xattr_ctx_t)); - memset(jcr->xattr_ctx, 0, sizeof(xattr_ctx_t)); - jcr->xattr_ctx->content = get_pool_memory(PM_MESSAGE); -#endif - + jcr->xacl = (XACL*)new_xacl(); + /* Subroutine save_file() is called for each file */ if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, plugin_save)) { - ok = false; /* error */ + ok = false; /* error */ jcr->setJobStatus(JS_ErrorTerminated); } -#ifdef HAVE_ACL - if (jcr->acl_ctx->nr_errors > 0) { - Jmsg(jcr, M_WARNING, 0, _("Had %ld acl errors while doing backup\n"), - jcr->acl_ctx->nr_errors); - } -#endif -#ifdef HAVE_XATTR - if (jcr->xattr_ctx->nr_errors > 0) { - Jmsg(jcr, M_WARNING, 0, _("Had %ld xattr errors while doing backup\n"), - jcr->xattr_ctx->nr_errors); - } -#endif + if (jcr->xacl->get_acl_nr_errors() > 0) { + Jmsg(jcr, M_WARNING, 0, _("Had %ld acl errors while doing backup\n"), jcr->xacl->get_acl_nr_errors()); + } + + if (jcr->xacl->get_xattr_nr_errors() > 0) { + Jmsg(jcr, M_WARNING, 0, _("Had %ld xattr errors while doing backup\n"), jcr->xacl->get_xattr_nr_errors()); + } + /* Delete or keep snapshots */ close_snapshot_backup_session(jcr); close_vss_backup_session(jcr); - accurate_finish(jcr); /* send deleted or base file list to SD */ + accurate_finish(jcr); /* send deleted or base file list to SD */ stop_heartbeat_monitor(jcr); - sd->signal(BNET_EOD); /* end of sending data */ + sd->signal(BNET_EOD); /* end of sending data */ - -#ifdef HAVE_ACL - if (jcr->acl_ctx) { - free_and_null_pool_memory(jcr->acl_ctx->content); - bfree_and_null(jcr->acl_ctx); - } -#endif -#ifdef HAVE_XATTR - if (jcr->xattr_ctx) { - free_and_null_pool_memory(jcr->xattr_ctx->content); - bfree_and_null(jcr->xattr_ctx); - } -#endif + if (jcr->xacl) { + delete(jcr->xacl); + jcr->xacl = NULL; + } if (jcr->big_buf) { bfree_and_null(jcr->big_buf); } @@ -239,8 +217,8 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) * Send the file and its data to the Storage daemon. * * Returns: 1 if OK - * 0 if error - * -1 to ignore file/directory (not used here) + * 0 if error + * -1 to ignore file/directory (not used here) */ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) { @@ -250,9 +228,9 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) int stat; int rtnstat = 0; bool has_file_data = false; - struct save_pkt sp; /* used by option plugin */ + struct save_pkt sp; /* used by option plugin */ BSOCK *sd = jcr->store_bsock; - bctx_t bctx; /* backup context */ + bctx_t bctx; /* backup context */ memset(&bctx, 0, sizeof(bctx)); bctx.sd = sd; @@ -263,10 +241,10 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) time_t now = time(NULL); if (jcr->last_stat_time == 0) { jcr->last_stat_time = now; - jcr->stat_interval = 30; /* Default 30 seconds */ + jcr->stat_interval = 30; /* Default 30 seconds */ } else if (now >= jcr->last_stat_time + jcr->stat_interval) { jcr->dir_bsock->fsend("Progress Job=x files=%ld bytes=%lld bps=%ld\n", - jcr->JobFiles, jcr->JobBytes, jcr->LastRate); + jcr->JobFiles, jcr->JobBytes, jcr->LastRate); jcr->last_stat_time = now; } @@ -275,10 +253,10 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) return 0; } - jcr->num_files_examined++; /* bump total file count */ + jcr->num_files_examined++; /* bump total file count */ switch (ff_pkt->type) { - case FT_LNKSAVED: /* Hard linked, file already saved */ + case FT_LNKSAVED: /* Hard linked, file already saved */ Dmsg2(130, "FT_LNKSAVED hard link: %s => %s\n", ff_pkt->fname, ff_pkt->link); break; case FT_REGE: @@ -300,28 +278,28 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) break; case FT_DIRBEGIN: jcr->num_files_examined--; /* correct file count */ - return 1; /* not used */ + return 1; /* not used */ case FT_NORECURSE: - Jmsg(jcr, M_INFO, 1, _(" Recursion turned off. Will not descend from %s into %s\n"), - ff_pkt->top_fname, ff_pkt->fname); + Jmsg(jcr, M_INFO, 1, _(" Recursion turned off. Will not descend from %s into %s\n"), + ff_pkt->top_fname, ff_pkt->fname); ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_NOFSCHG: /* Suppress message for /dev filesystems */ if (!is_in_fileset(ff_pkt)) { - Jmsg(jcr, M_INFO, 1, _(" %s is a different filesystem. Will not descend from %s into it.\n"), - ff_pkt->fname, ff_pkt->top_fname); + Jmsg(jcr, M_INFO, 1, _(" %s is a different filesystem. Will not descend from %s into it.\n"), + ff_pkt->fname, ff_pkt->top_fname); } ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_INVALIDFS: - Jmsg(jcr, M_INFO, 1, _(" Disallowed filesystem. Will not descend from %s into %s\n"), - ff_pkt->top_fname, ff_pkt->fname); + Jmsg(jcr, M_INFO, 1, _(" Disallowed filesystem. Will not descend from %s into %s\n"), + ff_pkt->top_fname, ff_pkt->fname); ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_INVALIDDT: - Jmsg(jcr, M_INFO, 1, _(" Disallowed drive type. Will not descend into %s\n"), - ff_pkt->fname); + Jmsg(jcr, M_INFO, 1, _(" Disallowed drive type. Will not descend into %s\n"), + ff_pkt->fname); break; case FT_REPARSE: case FT_JUNCTION: @@ -331,8 +309,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) case FT_SPEC: Dmsg1(130, "FT_SPEC saving: %s\n", ff_pkt->fname); if (S_ISSOCK(ff_pkt->statp.st_mode)) { - Jmsg(jcr, M_SKIPPED, 1, _(" Socket file skipped: %s\n"), ff_pkt->fname); - return 1; + Jmsg(jcr, M_SKIPPED, 1, _(" Socket file skipped: %s\n"), ff_pkt->fname); + return 1; } break; case FT_RAW: @@ -345,21 +323,21 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) case FT_NOACCESS: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not access \"%s\": ERR=%s\n"), ff_pkt->fname, - be.bstrerror(ff_pkt->ff_errno)); + be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } case FT_NOFOLLOW: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not follow link \"%s\": ERR=%s\n"), - ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); + ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } case FT_NOSTAT: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not stat \"%s\": ERR=%s\n"), ff_pkt->fname, - be.bstrerror(ff_pkt->ff_errno)); + be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } @@ -373,7 +351,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) case FT_NOOPEN: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not open directory \"%s\": ERR=%s\n"), - ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); + ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } @@ -381,8 +359,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) Dmsg1(130, "FT_DELETED: %s\n", ff_pkt->fname); break; default: - Jmsg(jcr, M_NOTSAVED, 0, _(" Unknown file type %d; not saved: %s\n"), - ff_pkt->type, ff_pkt->fname); + Jmsg(jcr, M_NOTSAVED, 0, _(" Unknown file type %d; not saved: %s\n"), + ff_pkt->type, ff_pkt->fname); jcr->JobErrors++; return 1; } @@ -409,27 +387,27 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) /* ask the option plugin what to do with this file */ switch (plugin_option_handle_file(jcr, ff_pkt, &sp)) { case bRC_OK: - Dmsg2(10, "Option plugin %s will be used to backup %s\n", - ff_pkt->plugin, ff_pkt->fname); - do_plugin_set = true; - break; + Dmsg2(10, "Option plugin %s will be used to backup %s\n", + ff_pkt->plugin, ff_pkt->fname); + do_plugin_set = true; + break; case bRC_Skip: - Dmsg2(10, "Option plugin %s decided to skip %s\n", - ff_pkt->plugin, ff_pkt->fname); - goto good_rtn; + Dmsg2(10, "Option plugin %s decided to skip %s\n", + ff_pkt->plugin, ff_pkt->fname); + goto good_rtn; default: - Dmsg2(10, "Option plugin %s decided to let bacula handle %s\n", - ff_pkt->plugin, ff_pkt->fname); - break; + Dmsg2(10, "Option plugin %s decided to let bacula handle %s\n", + ff_pkt->plugin, ff_pkt->fname); + break; } } if (do_plugin_set) { /* Tell bfile that it needs to call plugin */ if (!set_cmd_plugin(&ff_pkt->bfd, jcr)) { - goto bail_out; + goto bail_out; } - send_plugin_name(jcr, sd, true); /* signal start of plugin data */ + send_plugin_name(jcr, sd, true); /* signal start of plugin data */ plugin_started = true; } @@ -448,7 +426,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) /** Set up the encryption context and send the session data to the SD */ if (has_file_data && jcr->crypto.pki_encrypt) { if (!crypto_session_send(jcr, sd)) { - goto bail_out; + goto bail_out; } } @@ -465,8 +443,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) do_read = ff_pkt->statp.st_size > 0; #endif } else if (ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO || - ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_JUNCTION || - (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) { + ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_JUNCTION || + (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) { do_read = true; } @@ -479,41 +457,41 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) btimer_t *tid; if (ff_pkt->type == FT_FIFO) { - tid = start_thread_timer(jcr, pthread_self(), 60); + tid = start_thread_timer(jcr, pthread_self(), 60); } else { - tid = NULL; + tid = NULL; } int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0; ff_pkt->bfd.reparse_point = (ff_pkt->type == FT_REPARSE || - ff_pkt->type == FT_JUNCTION); + ff_pkt->type == FT_JUNCTION); set_fattrs(&ff_pkt->bfd, &ff_pkt->statp); if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0) < 0) { - ff_pkt->ff_errno = errno; - berrno be; - Jmsg(jcr, M_NOTSAVED, 0, _(" Cannot open \"%s\": ERR=%s.\n"), ff_pkt->fname, - be.bstrerror()); - jcr->JobErrors++; - if (tid) { - stop_thread_timer(tid); - tid = NULL; - } - goto good_rtn; + ff_pkt->ff_errno = errno; + berrno be; + Jmsg(jcr, M_NOTSAVED, 0, _(" Cannot open \"%s\": ERR=%s.\n"), ff_pkt->fname, + be.bstrerror()); + jcr->JobErrors++; + if (tid) { + stop_thread_timer(tid); + tid = NULL; + } + goto good_rtn; } if (tid) { - stop_thread_timer(tid); - tid = NULL; + stop_thread_timer(tid); + tid = NULL; } stat = send_data(bctx, bctx.data_stream); if (ff_pkt->flags & FO_CHKCHANGES) { - has_file_changed(jcr, ff_pkt); + has_file_changed(jcr, ff_pkt); } bclose(&ff_pkt->bfd); if (!stat) { - goto bail_out; + goto bail_out; } } @@ -524,24 +502,17 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) #endif /* - * Save ACLs when requested and available for anything not being a symlink - * and not being a plugin. - */ -#ifdef HAVE_ACL - if (!backup_acl_streams(jcr, ff_pkt)) { - goto bail_out; - } -#endif - - /* - * Save Extended Attributes when requested and available for all files not - * being a plugin. + * Save ACLs and Extended Attributes when requested and available + * for anything not being a symlink and not being a plugin (why not?). */ -#ifdef HAVE_XATTR - if (!backup_xattr_streams(jcr, ff_pkt)) { - goto bail_out; + if (jcr->xacl){ + if (jcr->xacl->backup_acl(jcr, ff_pkt) == bRC_XACL_error) { + goto bail_out; + } + if (jcr->xacl->backup_xattr(jcr, ff_pkt) == bRC_XACL_error) { + goto bail_out; + } } -#endif if (!crypto_terminate_digests(bctx)) { goto bail_out; @@ -559,7 +530,7 @@ bail_out: send_plugin_name(jcr, sd, false); /* signal end of plugin data */ } if (ff_pkt->opt_plugin) { - jcr->plugin_sp = NULL; /* sp is local to this function */ + jcr->plugin_sp = NULL; /* sp is local to this function */ jcr->plugin_ctx = NULL; jcr->plugin = NULL; jcr->opt_plugin = false; @@ -592,9 +563,9 @@ static int send_data(bctx_t &bctx, int stream) bctx.fileAddr = 0; bctx.cipher_ctx = NULL; bctx.msgsave = sd->msg; - bctx.rbuf = sd->msg; /* read buffer */ - bctx.wbuf = sd->msg; /* write buffer */ - bctx.cipher_input = (uint8_t *)bctx.rbuf; /* encrypt uncompressed data */ + bctx.rbuf = sd->msg; /* read buffer */ + bctx.wbuf = sd->msg; /* write buffer */ + bctx.cipher_input = (uint8_t *)bctx.rbuf; /* encrypt uncompressed data */ Dmsg1(300, "Saving data, type=%d\n", bctx.ff_pkt->type); @@ -608,13 +579,13 @@ static int send_data(bctx_t &bctx, int stream) /** * Send Data header to Storage daemon - * + * */ if (!sd->fsend("%ld %d %lld", jcr->JobFiles, stream, - (int64_t)bctx.ff_pkt->statp.st_size)) { + (int64_t)bctx.ff_pkt->statp.st_size)) { if (!jcr->is_job_canceled()) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), + sd->bstrerror()); } goto err; } @@ -622,7 +593,7 @@ static int send_data(bctx_t &bctx, int stream) /** * Make space at beginning of buffer for fileAddr because this - * same buffer will be used for writing if compression is off. + * same buffer will be used for writing if compression is off. */ if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) { bctx.rbuf += OFFSET_FADDR_SIZE; @@ -644,13 +615,13 @@ static int send_data(bctx_t &bctx, int stream) Dmsg1(200, "Fattrs=0X%x\n", bctx.ff_pkt->bfd.fattrs); if (bctx.ff_pkt->bfd.fattrs & FILE_ATTRIBUTE_ENCRYPTED) { if (!p_ReadEncryptedFileRaw) { - Jmsg0(bctx.jcr, M_FATAL, 0, _("Windows Encrypted data not supported on this OS.\n")); - goto err; + Jmsg0(bctx.jcr, M_FATAL, 0, _("Windows Encrypted data not supported on this OS.\n")); + goto err; } /* This single call reads all EFS data delivers it to a callback */ if (p_ReadEncryptedFileRaw((PFE_EXPORT_FUNC)read_efs_data_cb, &bctx, - bctx.ff_pkt->bfd.pvContext) != 0) { - goto err; + bctx.ff_pkt->bfd.pvContext) != 0) { + goto err; } /* All read, so skip to finish sending */ goto finish_sending; @@ -663,18 +634,18 @@ static int send_data(bctx_t &bctx, int stream) */ while ((sd->msglen=(uint32_t)bread(&bctx.ff_pkt->bfd, bctx.rbuf, bctx.rsize)) > 0) { if (!process_and_send_data(bctx)) { - goto err; + goto err; } } /* end while read file data */ goto finish_sending; finish_sending: - if (sd->msglen < 0) { /* error */ + if (sd->msglen < 0) { /* error */ berrno be; Jmsg(jcr, M_ERROR, 0, _("Read error on file %s. ERR=%s\n"), - bctx.ff_pkt->fname, be.bstrerror(bctx.ff_pkt->bfd.berrno)); - if (jcr->JobErrors++ > 1000) { /* insanity check */ - Jmsg(jcr, M_FATAL, 0, _("Too many errors. JobErrors=%d.\n"), jcr->JobErrors); + bctx.ff_pkt->fname, be.bstrerror(bctx.ff_pkt->bfd.berrno)); + if (jcr->JobErrors++ > 1000) { /* insanity check */ + Jmsg(jcr, M_FATAL, 0, _("Too many errors. JobErrors=%d.\n"), jcr->JobErrors); } } else if (bctx.ff_pkt->flags & FO_ENCRYPT) { /** @@ -682,34 +653,34 @@ finish_sending: * buffered data. */ if (!crypto_cipher_finalize(bctx.cipher_ctx, (uint8_t *)jcr->crypto.crypto_buf, - &bctx.encrypted_len)) { - /* Padding failed. Shouldn't happen. */ - Jmsg(jcr, M_FATAL, 0, _("Encryption padding error\n")); - goto err; + &bctx.encrypted_len)) { + /* Padding failed. Shouldn't happen. */ + Jmsg(jcr, M_FATAL, 0, _("Encryption padding error\n")); + goto err; } /** Note, on SSL pre-0.9.7, there is always some output */ if (bctx.encrypted_len > 0) { - sd->msglen = bctx.encrypted_len; /* set encrypted length */ - sd->msg = jcr->crypto.crypto_buf; /* set correct write buffer */ - if (!sd->send()) { - if (!jcr->is_job_canceled()) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); - } - goto err; - } - Dmsg1(130, "Send data to SD len=%d\n", sd->msglen); - jcr->JobBytes += sd->msglen; /* count bytes saved possibly compressed/encrypted */ - sd->msg = bctx.msgsave; /* restore bnet buffer */ + sd->msglen = bctx.encrypted_len; /* set encrypted length */ + sd->msg = jcr->crypto.crypto_buf; /* set correct write buffer */ + if (!sd->send()) { + if (!jcr->is_job_canceled()) { + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), + sd->bstrerror()); + } + goto err; + } + Dmsg1(130, "Send data to SD len=%d\n", sd->msglen); + jcr->JobBytes += sd->msglen; /* count bytes saved possibly compressed/encrypted */ + sd->msg = bctx.msgsave; /* restore bnet buffer */ } } if (!sd->signal(BNET_EOD)) { /* indicate end of file data */ if (!jcr->is_job_canceled()) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), + sd->bstrerror()); } goto err; } @@ -746,20 +717,20 @@ bool process_and_send_data(bctx_t &bctx) ser_declare; bool allZeros = false; if ((sd->msglen == bctx.rsize && - bctx.fileAddr+sd->msglen < (uint64_t)bctx.ff_pkt->statp.st_size) || - ((bctx.ff_pkt->type == FT_RAW || bctx.ff_pkt->type == FT_FIFO) && - (uint64_t)bctx.ff_pkt->statp.st_size == 0)) { - allZeros = is_buf_zero(bctx.rbuf, bctx.rsize); + bctx.fileAddr+sd->msglen < (uint64_t)bctx.ff_pkt->statp.st_size) || + ((bctx.ff_pkt->type == FT_RAW || bctx.ff_pkt->type == FT_FIFO) && + (uint64_t)bctx.ff_pkt->statp.st_size == 0)) { + allZeros = is_buf_zero(bctx.rbuf, bctx.rsize); } if (!allZeros) { - /** Put file address as first data in buffer */ - ser_begin(bctx.wbuf, OFFSET_FADDR_SIZE); - ser_uint64(bctx.fileAddr); /* store fileAddr in begin of buffer */ + /** Put file address as first data in buffer */ + ser_begin(bctx.wbuf, OFFSET_FADDR_SIZE); + ser_uint64(bctx.fileAddr); /* store fileAddr in begin of buffer */ } - bctx.fileAddr += sd->msglen; /* update file address */ + bctx.fileAddr += sd->msglen; /* update file address */ /** Skip block of all zeros */ if (allZeros) { - return true; /* skip block of zeros */ + return true; /* skip block of zeros */ } } else if (bctx.ff_pkt->flags & FO_OFFSETS) { ser_declare; @@ -767,7 +738,7 @@ bool process_and_send_data(bctx_t &bctx) ser_uint64(bctx.ff_pkt->bfd.offset); /* store offset in begin of buffer */ } - jcr->ReadBytes += sd->msglen; /* count bytes read */ + jcr->ReadBytes += sd->msglen; /* count bytes read */ /** Uncompressed cipher input length */ bctx.cipher_input_len = sd->msglen; @@ -808,7 +779,7 @@ bool process_and_send_data(bctx_t &bctx) ser_declare; if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) { - bctx.cipher_input_len += OFFSET_FADDR_SIZE; + bctx.cipher_input_len += OFFSET_FADDR_SIZE; } /** Encrypt the length of the input block */ @@ -819,26 +790,26 @@ bool process_and_send_data(bctx_t &bctx) Dmsg1(20, "Encrypt len=%d\n", bctx.cipher_input_len); if (!crypto_cipher_update(bctx.cipher_ctx, packet_len, sizeof(packet_len), - (uint8_t *)jcr->crypto.crypto_buf, &initial_len)) { - /** Encryption failed. Shouldn't happen. */ - Jmsg(jcr, M_FATAL, 0, _("Encryption error\n")); - goto err; + (uint8_t *)jcr->crypto.crypto_buf, &initial_len)) { + /** Encryption failed. Shouldn't happen. */ + Jmsg(jcr, M_FATAL, 0, _("Encryption error\n")); + goto err; } /** Encrypt the input block */ if (crypto_cipher_update(bctx.cipher_ctx, bctx.cipher_input, bctx.cipher_input_len, - (uint8_t *)&jcr->crypto.crypto_buf[initial_len], &bctx.encrypted_len)) { - if ((initial_len + bctx.encrypted_len) == 0) { - /** No full block of data available, read more data */ - return true; - } - Dmsg2(400, "encrypted len=%d unencrypted len=%d\n", bctx.encrypted_len, - sd->msglen); - sd->msglen = initial_len + bctx.encrypted_len; /* set encrypted length */ + (uint8_t *)&jcr->crypto.crypto_buf[initial_len], &bctx.encrypted_len)) { + if ((initial_len + bctx.encrypted_len) == 0) { + /** No full block of data available, read more data */ + return true; + } + Dmsg2(400, "encrypted len=%d unencrypted len=%d\n", bctx.encrypted_len, + sd->msglen); + sd->msglen = initial_len + bctx.encrypted_len; /* set encrypted length */ } else { - /** Encryption failed. Shouldn't happen. */ - Jmsg(jcr, M_FATAL, 0, _("Encryption error\n")); - goto err; + /** Encryption failed. Shouldn't happen. */ + Jmsg(jcr, M_FATAL, 0, _("Encryption error\n")); + goto err; } } @@ -846,18 +817,18 @@ bool process_and_send_data(bctx_t &bctx) if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) { sd->msglen += OFFSET_FADDR_SIZE; /* include fileAddr in size */ } - sd->msg = bctx.wbuf; /* set correct write buffer */ + sd->msg = bctx.wbuf; /* set correct write buffer */ if (!sd->send()) { if (!jcr->is_job_canceled()) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), + sd->bstrerror()); } goto err; } Dmsg1(130, "Send data to SD len=%d\n", sd->msglen); - /* #endif */ + /* #endif */ jcr->JobBytes += sd->msglen; /* count bytes saved possibly compressed/encrypted */ - sd->msg = bctx.msgsave; /* restore read buffer */ + sd->msg = bctx.msgsave; /* restore read buffer */ return true; err: @@ -901,7 +872,7 @@ bool encode_and_send_attributes(bctx_t &bctx) Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx); jcr->lock(); - jcr->JobFiles++; /* increment number of files sent */ + jcr->JobFiles++; /* increment number of files sent */ ff_pkt->FileIndex = jcr->JobFiles; /* return FileIndex */ pm_strcpy(jcr->last_fname, ff_pkt->fname); jcr->unlock(); @@ -921,12 +892,12 @@ bool encode_and_send_attributes(bctx_t &bctx) /** * Send Attributes header to Storage daemon - * + * */ if (!sd->fsend("%ld %d 0", jcr->JobFiles, attr_stream)) { if (!jcr->is_canceled() && !jcr->is_incomplete()) { - Jmsg2(jcr, M_FATAL, 0, _("Network send error to SD. Data=%s ERR=%s\n"), - sd->msg, sd->bstrerror()); + Jmsg2(jcr, M_FATAL, 0, _("Network send error to SD. Data=%s ERR=%s\n"), + sd->msg, sd->bstrerror()); } return false; } @@ -934,23 +905,23 @@ bool encode_and_send_attributes(bctx_t &bctx) /** * Send file attributes to Storage daemon - * File_index - * File type - * Filename (full path) - * Encoded attributes - * Link name (if type==FT_LNK or FT_LNKSAVED) - * Encoded extended-attributes (for Win32) + * File_index + * File type + * Filename (full path) + * Encoded attributes + * Link name (if type==FT_LNK or FT_LNKSAVED) + * Encoded extended-attributes (for Win32) * * or send Restore Object to Storage daemon - * File_index - * File_type - * Object_index - * Object_len (possibly compressed) - * Object_full_len (not compressed) - * Object_compression - * Plugin_name - * Object_name - * Binary Object data + * File_index + * File_type + * Object_index + * Object_len (possibly compressed) + * Object_full_len (not compressed) + * Object_compression + * Plugin_name + * Object_name + * Binary Object data * * For a directory, link is the same as fname, but with trailing * slash. For a linked file, link is the link. @@ -963,58 +934,58 @@ bool encode_and_send_attributes(bctx_t &bctx) case FT_LNKSAVED: Dmsg3(300, "Link %d %s to %s\n", jcr->JobFiles, ff_pkt->fname, ff_pkt->link); stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c%u%c", jcr->JobFiles, - ff_pkt->type, ff_pkt->fname, 0, attribs, 0, - ff_pkt->link, 0, attribsEx, 0, ff_pkt->delta_seq, 0); + ff_pkt->type, ff_pkt->fname, 0, attribs, 0, + ff_pkt->link, 0, attribsEx, 0, ff_pkt->delta_seq, 0); break; case FT_DIREND: case FT_REPARSE: case FT_JUNCTION: /* Here link is the canonical filename (i.e. with trailing slash) */ stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles, - ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, - attribsEx, 0, ff_pkt->delta_seq, 0); + ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, + attribsEx, 0, ff_pkt->delta_seq, 0); break; case FT_PLUGIN_CONFIG: case FT_RESTORE_FIRST: comp_len = ff_pkt->object_len; ff_pkt->object_compression = 0; if (ff_pkt->object_len > 1000) { - /* Big object, compress it */ - comp_len = ff_pkt->object_len + 1000; - POOLMEM *comp_obj = get_memory(comp_len); - /* *** FIXME *** check Zdeflate error */ - Zdeflate(ff_pkt->object, ff_pkt->object_len, comp_obj, comp_len); - if (comp_len < ff_pkt->object_len) { - ff_pkt->object = comp_obj; - ff_pkt->object_compression = 1; /* zlib level 9 compression */ - } else { - /* Uncompressed object smaller, use it */ - comp_len = ff_pkt->object_len; - } - Dmsg2(100, "Object compressed from %d to %d bytes\n", ff_pkt->object_len, comp_len); + /* Big object, compress it */ + comp_len = ff_pkt->object_len + 1000; + POOLMEM *comp_obj = get_memory(comp_len); + /* *** FIXME *** check Zdeflate error */ + Zdeflate(ff_pkt->object, ff_pkt->object_len, comp_obj, comp_len); + if (comp_len < ff_pkt->object_len) { + ff_pkt->object = comp_obj; + ff_pkt->object_compression = 1; /* zlib level 9 compression */ + } else { + /* Uncompressed object smaller, use it */ + comp_len = ff_pkt->object_len; + } + Dmsg2(100, "Object compressed from %d to %d bytes\n", ff_pkt->object_len, comp_len); } sd->msglen = Mmsg(sd->msg, "%d %d %d %d %d %d %s%c%s%c", - jcr->JobFiles, ff_pkt->type, ff_pkt->object_index, - comp_len, ff_pkt->object_len, ff_pkt->object_compression, - ff_pkt->fname, 0, ff_pkt->object_name, 0); + jcr->JobFiles, ff_pkt->type, ff_pkt->object_index, + comp_len, ff_pkt->object_len, ff_pkt->object_compression, + ff_pkt->fname, 0, ff_pkt->object_name, 0); sd->msg = check_pool_memory_size(sd->msg, sd->msglen + comp_len + 2); memcpy(sd->msg + sd->msglen, ff_pkt->object, comp_len); /* Note we send one extra byte so Dir can store zero after object */ sd->msglen += comp_len + 1; stat = sd->send(); if (ff_pkt->object_compression) { - free_and_null_pool_memory(ff_pkt->object); + free_and_null_pool_memory(ff_pkt->object); } break; case FT_REG: stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%d%c", jcr->JobFiles, - ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0, - ff_pkt->delta_seq, 0); + ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0, + ff_pkt->delta_seq, 0); break; default: stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles, - ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, - attribsEx, 0, ff_pkt->delta_seq, 0); + ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, + attribsEx, 0, ff_pkt->delta_seq, 0); break; } @@ -1025,9 +996,9 @@ bool encode_and_send_attributes(bctx_t &bctx) Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg); if (!stat && !jcr->is_job_canceled()) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); + sd->bstrerror()); } - sd->signal(BNET_EOD); /* indicate end of attributes data */ + sd->signal(BNET_EOD); /* indicate end of attributes data */ return stat; } @@ -1047,13 +1018,13 @@ static bool setup_compression(bctx_t &bctx) if ((bctx.ff_pkt->flags & FO_COMPRESS) && bctx.ff_pkt->Compress_algo == COMPRESS_GZIP) { if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) { - bctx.cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE; - bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE; + bctx.cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE; + bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE; } else { - bctx.cbuf = (Bytef *)jcr->compress_buf; - bctx.max_compress_len = jcr->compress_buf_size; /* set max length */ + bctx.cbuf = (Bytef *)jcr->compress_buf; + bctx.max_compress_len = jcr->compress_buf_size; /* set max length */ } - bctx.wbuf = jcr->compress_buf; /* compressed output here */ + bctx.wbuf = jcr->compress_buf; /* compressed output here */ bctx.cipher_input = (uint8_t *)jcr->compress_buf; /* encrypt compressed data */ /** @@ -1063,13 +1034,13 @@ static bool setup_compression(bctx_t &bctx) */ if (((z_stream*)jcr->pZLIB_compress_workset)->total_in == 0) { - /** set gzip compression level - must be done per file */ - if ((zstat=deflateParams((z_stream*)jcr->pZLIB_compress_workset, - bctx.ff_pkt->Compress_level, Z_DEFAULT_STRATEGY)) != Z_OK) { - Jmsg(jcr, M_FATAL, 0, _("Compression deflateParams error: %d\n"), zstat); - jcr->setJobStatus(JS_ErrorTerminated); - return false; - } + /** set gzip compression level - must be done per file */ + if ((zstat=deflateParams((z_stream*)jcr->pZLIB_compress_workset, + bctx.ff_pkt->Compress_level, Z_DEFAULT_STRATEGY)) != Z_OK) { + Jmsg(jcr, M_FATAL, 0, _("Compression deflateParams error: %d\n"), zstat); + jcr->setJobStatus(JS_ErrorTerminated); + return false; + } } } #endif @@ -1079,17 +1050,17 @@ static bool setup_compression(bctx_t &bctx) if ((bctx.ff_pkt->flags & FO_COMPRESS) && bctx.ff_pkt->Compress_algo == COMPRESS_LZO1X) { if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) { - bctx.cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE; - bctx.cbuf2 = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE + sizeof(comp_stream_header); - bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE; + bctx.cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE; + bctx.cbuf2 = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE + sizeof(comp_stream_header); + bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE; } else { - bctx.cbuf = (Bytef *)jcr->compress_buf; - bctx.cbuf2 = (Bytef *)jcr->compress_buf + sizeof(comp_stream_header); - bctx.max_compress_len = jcr->compress_buf_size; /* set max length */ + bctx.cbuf = (Bytef *)jcr->compress_buf; + bctx.cbuf2 = (Bytef *)jcr->compress_buf + sizeof(comp_stream_header); + bctx.max_compress_len = jcr->compress_buf_size; /* set max length */ } bctx.ch.magic = COMPRESS_LZO1X; bctx.ch.version = COMP_HEAD_VERSION; - bctx.wbuf = jcr->compress_buf; /* compressed output here */ + bctx.wbuf = jcr->compress_buf; /* compressed output here */ bctx.cipher_input = (uint8_t *)jcr->compress_buf; /* encrypt compressed data */ } #endif @@ -1114,32 +1085,32 @@ static bool send_resource_fork(bctx_t &bctx) if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) && ff_pkt->flags & FO_HFSPLUS)) { if (ff_pkt->hfsinfo.rsrclength > 0) { - int flags; - int rsrc_stream; - if (bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) { - ff_pkt->ff_errno = errno; - berrno be; - Jmsg(jcr, M_NOTSAVED, -1, _(" Cannot open resource fork for \"%s\": ERR=%s.\n"), - ff_pkt->fname, be.bstrerror()); - jcr->JobErrors++; - if (is_bopen(&ff_pkt->bfd)) { - bclose(&ff_pkt->bfd); - } - return true; - } - flags = ff_pkt->flags; - ff_pkt->flags &= ~(FO_COMPRESS|FO_SPARSE|FO_OFFSETS); - if (flags & FO_ENCRYPT) { - rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA; - } else { - rsrc_stream = STREAM_MACOS_FORK_DATA; - } - stat = send_data(bctx, rsrc_stream); - ff_pkt->flags = flags; - bclose(&ff_pkt->bfd); - if (!stat) { - return false; - } + int flags; + int rsrc_stream; + if (bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) { + ff_pkt->ff_errno = errno; + berrno be; + Jmsg(jcr, M_NOTSAVED, -1, _(" Cannot open resource fork for \"%s\": ERR=%s.\n"), + ff_pkt->fname, be.bstrerror()); + jcr->JobErrors++; + if (is_bopen(&ff_pkt->bfd)) { + bclose(&ff_pkt->bfd); + } + return true; + } + flags = ff_pkt->flags; + ff_pkt->flags &= ~(FO_COMPRESS|FO_SPARSE|FO_OFFSETS); + if (flags & FO_ENCRYPT) { + rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA; + } else { + rsrc_stream = STREAM_MACOS_FORK_DATA; + } + stat = send_data(bctx, rsrc_stream); + ff_pkt->flags = flags; + bclose(&ff_pkt->bfd); + if (!stat) { + return false; + } } Dmsg1(300, "Saving Finder Info for \"%s\"\n", ff_pkt->fname); @@ -1148,10 +1119,10 @@ static bool send_resource_fork(bctx_t &bctx) pm_memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32); sd->msglen = 32; if (bctx.digest) { - crypto_digest_update(bctx.digest, (uint8_t *)sd->msg, sd->msglen); + crypto_digest_update(bctx.digest, (uint8_t *)sd->msg, sd->msglen); } if (bctx.signing_digest) { - crypto_digest_update(bctx.signing_digest, (uint8_t *)sd->msg, sd->msglen); + crypto_digest_update(bctx.signing_digest, (uint8_t *)sd->msg, sd->msglen); } sd->send(); sd->signal(BNET_EOD); @@ -1172,27 +1143,27 @@ static bool do_libz_compression(bctx_t &bctx) Dmsg3(400, "cbuf=0x%x rbuf=0x%x len=%u\n", bctx.cbuf, bctx.rbuf, sd->msglen); ((z_stream*)jcr->pZLIB_compress_workset)->next_in = (Bytef *)bctx.rbuf; - ((z_stream*)jcr->pZLIB_compress_workset)->avail_in = sd->msglen; + ((z_stream*)jcr->pZLIB_compress_workset)->avail_in = sd->msglen; ((z_stream*)jcr->pZLIB_compress_workset)->next_out = bctx.cbuf; - ((z_stream*)jcr->pZLIB_compress_workset)->avail_out = bctx.max_compress_len; + ((z_stream*)jcr->pZLIB_compress_workset)->avail_out = bctx.max_compress_len; if ((zstat=deflate((z_stream*)jcr->pZLIB_compress_workset, Z_FINISH)) != Z_STREAM_END) { - Jmsg(jcr, M_FATAL, 0, _("Compression deflate error: %d\n"), zstat); - jcr->setJobStatus(JS_ErrorTerminated); - return false; + Jmsg(jcr, M_FATAL, 0, _("Compression deflate error: %d\n"), zstat); + jcr->setJobStatus(JS_ErrorTerminated); + return false; } bctx.compress_len = ((z_stream*)jcr->pZLIB_compress_workset)->total_out; /** reset zlib stream to be able to begin from scratch again */ if ((zstat=deflateReset((z_stream*)jcr->pZLIB_compress_workset)) != Z_OK) { - Jmsg(jcr, M_FATAL, 0, _("Compression deflateReset error: %d\n"), zstat); - jcr->setJobStatus(JS_ErrorTerminated); - return false; + Jmsg(jcr, M_FATAL, 0, _("Compression deflateReset error: %d\n"), zstat); + jcr->setJobStatus(JS_ErrorTerminated); + return false; } Dmsg2(400, "GZIP compressed len=%d uncompressed len=%d\n", bctx.compress_len, - sd->msglen); + sd->msglen); - sd->msglen = bctx.compress_len; /* set compressed length */ + sd->msglen = bctx.compress_len; /* set compressed length */ bctx.cipher_input_len = bctx.compress_len; } #endif @@ -1208,7 +1179,7 @@ static bool do_lzo_compression(bctx_t &bctx) /** Do compression if turned on */ if (bctx.ff_pkt->flags & FO_COMPRESS && bctx.ff_pkt->Compress_algo == COMPRESS_LZO1X && jcr->LZO_compress_workset) { - lzo_uint len; /* TODO: See with the latest patch how to handle lzo_uint with 64bit */ + lzo_uint len; /* TODO: See with the latest patch how to handle lzo_uint with 64bit */ ser_declare; ser_begin(bctx.cbuf, sizeof(comp_stream_header)); @@ -1216,26 +1187,26 @@ static bool do_lzo_compression(bctx_t &bctx) Dmsg3(400, "cbuf=0x%x rbuf=0x%x len=%u\n", bctx.cbuf, bctx.rbuf, sd->msglen); lzores = lzo1x_1_compress((const unsigned char*)bctx.rbuf, sd->msglen, bctx.cbuf2, - &len, jcr->LZO_compress_workset); + &len, jcr->LZO_compress_workset); bctx.compress_len = len; if (lzores == LZO_E_OK && bctx.compress_len <= bctx.max_compress_len) { - /* complete header */ - ser_uint32(COMPRESS_LZO1X); - ser_uint32(bctx.compress_len); - ser_uint16(bctx.ch.level); - ser_uint16(bctx.ch.version); + /* complete header */ + ser_uint32(COMPRESS_LZO1X); + ser_uint32(bctx.compress_len); + ser_uint16(bctx.ch.level); + ser_uint16(bctx.ch.version); } else { - /** this should NEVER happen */ - Jmsg(jcr, M_FATAL, 0, _("Compression LZO error: %d\n"), lzores); - jcr->setJobStatus(JS_ErrorTerminated); - return false; + /** this should NEVER happen */ + Jmsg(jcr, M_FATAL, 0, _("Compression LZO error: %d\n"), lzores); + jcr->setJobStatus(JS_ErrorTerminated); + return false; } Dmsg2(400, "LZO compressed len=%d uncompressed len=%d\n", bctx.compress_len, - sd->msglen); + sd->msglen); bctx.compress_len += sizeof(comp_stream_header); /* add size of header */ - sd->msglen = bctx.compress_len; /* set compressed length */ + sd->msglen = bctx.compress_len; /* set compressed length */ bctx.cipher_input_len = bctx.compress_len; } #endif @@ -1258,13 +1229,13 @@ static bool do_snap_strip(FF_PKT *ff) last = MAX(last - 1, 0); if (ff->snap_fname[last] == '/') { - if (ff->fname[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */ - ff->snap_fname[last] = 0; - } + if (ff->fname[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */ + ff->snap_fname[last] = 0; + } } else { - if (ff->fname[sp_first] != '/') { - pm_strcat(ff->snap_fname, "/"); - } + if (ff->fname[sp_first] != '/') { + pm_strcat(ff->snap_fname, "/"); + } } pm_strcat(ff->snap_fname, ff->fname + sp_first); @@ -1277,13 +1248,13 @@ static bool do_snap_strip(FF_PKT *ff) last = MAX(last - 1, 0); if (ff->snap_fname[last] == '/') { - if (ff->link[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */ - ff->snap_fname[last] = 0; - } + if (ff->link[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */ + ff->snap_fname[last] = 0; + } } else { - if (ff->link[sp_first] != '/') { - pm_strcat(ff->snap_fname, "/"); - } + if (ff->link[sp_first] != '/') { + pm_strcat(ff->snap_fname, "/"); + } } pm_strcat(ff->snap_fname, ff->link + sp_first); @@ -1308,29 +1279,29 @@ static bool do_strip(int count, char *in) while (*in && !IsPathSeparator(*in)) { out++; in++; } - if (*in) { /* Not at the end of the string */ + if (*in) { /* Not at the end of the string */ out++; in++; - numsep++; /* one separator seen */ + numsep++; /* one separator seen */ } for (stripped=0; strippedcount=%d\n", - stripped, count, numsep, numsep>count); + stripped, count, numsep, numsep>count); return stripped==count && numsep>count; } @@ -1346,7 +1317,7 @@ static bool do_strip(int count, char *in) */ void strip_path(FF_PKT *ff_pkt) { - if (!ff_pkt->strip_snap_path && + if (!ff_pkt->strip_snap_path && (!(ff_pkt->flags & FO_STRIPPATH) || ff_pkt->strip_path <= 0)) { Dmsg1(200, "No strip for %s\n", ff_pkt->fname); @@ -1362,15 +1333,15 @@ void strip_path(FF_PKT *ff_pkt) if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) { pm_strcpy(ff_pkt->link_save, ff_pkt->link); Dmsg2(500, "strcpy link_save=%d link=%d\n", strlen(ff_pkt->link_save), - strlen(ff_pkt->link)); + strlen(ff_pkt->link)); Dsm_check(200); - } + } if (ff_pkt->strip_snap_path) { if (!do_snap_strip(ff_pkt)) { - Dmsg1(0, "Something wrong with do_snap_strip(%s)\n", ff_pkt->fname); - unstrip_path(ff_pkt); - goto rtn; + Dmsg1(0, "Something wrong with do_snap_strip(%s)\n", ff_pkt->fname); + unstrip_path(ff_pkt); + goto rtn; } } @@ -1394,7 +1365,7 @@ void strip_path(FF_PKT *ff_pkt) /** Strip links but not symlinks */ if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) { if (!do_strip(ff_pkt->strip_path, ff_pkt->link)) { - unstrip_path(ff_pkt); + unstrip_path(ff_pkt); } } @@ -1410,14 +1381,14 @@ void unstrip_path(FF_PKT *ff_pkt) { return; } - + strcpy(ff_pkt->fname, ff_pkt->fname_save); if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) { Dmsg2(10, "strcpy link=%s link_save=%s\n", ff_pkt->link, - ff_pkt->link_save); + ff_pkt->link_save); strcpy(ff_pkt->link, ff_pkt->link_save); Dmsg2(10, "strcpy link=%d link_save=%d\n", strlen(ff_pkt->link), - strlen(ff_pkt->link_save)); + strlen(ff_pkt->link_save)); Dsm_check(200); } } @@ -1429,28 +1400,28 @@ static void close_vss_backup_session(JCR *jcr) /* tell vss to close the backup session */ if (jcr->Snapshot) { if (g_pVSSClient->CloseBackup()) { - /* inform user about writer states */ - for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) { - int msg_type = M_INFO; - if (g_pVSSClient->GetWriterState(i) < 1) { - msg_type = M_WARNING; - jcr->JobErrors++; - } - Jmsg(jcr, msg_type, 0, _("VSS Writer (BackupComplete): %s\n"), g_pVSSClient->GetWriterInfo(i)); - } + /* inform user about writer states */ + for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) { + int msg_type = M_INFO; + if (g_pVSSClient->GetWriterState(i) < 1) { + msg_type = M_WARNING; + jcr->JobErrors++; + } + Jmsg(jcr, msg_type, 0, _("VSS Writer (BackupComplete): %s\n"), g_pVSSClient->GetWriterInfo(i)); + } } /* Generate Job global writer metadata */ WCHAR *metadata = g_pVSSClient->GetMetadata(); if (metadata) { - FF_PKT *ff_pkt = jcr->ff; - ff_pkt->fname = (char *)"*all*"; /* for all plugins */ - ff_pkt->type = FT_RESTORE_FIRST; - ff_pkt->LinkFI = 0; - ff_pkt->object_name = (char *)"job_metadata.xml"; - ff_pkt->object = (char *)metadata; - ff_pkt->object_len = (wcslen(metadata) + 1) * sizeof(WCHAR); - ff_pkt->object_index = (int)time(NULL); - save_file(jcr, ff_pkt, true); + FF_PKT *ff_pkt = jcr->ff; + ff_pkt->fname = (char *)"*all*"; /* for all plugins */ + ff_pkt->type = FT_RESTORE_FIRST; + ff_pkt->LinkFI = 0; + ff_pkt->object_name = (char *)"job_metadata.xml"; + ff_pkt->object = (char *)metadata; + ff_pkt->object_len = (wcslen(metadata) + 1) * sizeof(WCHAR); + ff_pkt->object_index = (int)time(NULL); + save_file(jcr, ff_pkt, true); } } #endif diff --git a/bacula/src/filed/filed.h b/bacula/src/filed/filed.h index cc156939da..735021e16e 100644 --- a/bacula/src/filed/filed.h +++ b/bacula/src/filed/filed.h @@ -1,7 +1,7 @@ /* Bacula(R) - The Network Backup Solution - Copyright (C) 2000-2015 Kern Sibbald + Copyright (C) 2000-2016 Kern Sibbald The original author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -11,7 +11,7 @@ Public License, v3.0 ("AGPLv3") and some additional permissions and terms pursuant to its AGPLv3 Section 7. - This notice must be preserved when any source code is + This notice must be preserved when any source code is conveyed and/or propagated. Bacula(R) is a registered trademark of Kern Sibbald. @@ -24,30 +24,16 @@ */ //#define TEST_WORKER -#ifdef TEST_WORKER +#ifdef TEST_WORKER #define ERROR_BUFFER_OVERFLOW 1 #define ERROR_SUCCESS 0 #endif - + /* acl errors to report per job. */ #define ACL_MAX_ERROR_PRINT_PER_JOB 25 - + /* xattr errors to report per job. */ #define XATTR_MAX_ERROR_PRINT_PER_JOB 25 - -/* Return values from acl subroutines. */ -enum bacl_rtn_code { - bacl_rtn_fatal = -1, - bacl_rtn_error = 0, - bacl_rtn_ok = 1 -}; - -/* Return values from xattr subroutines. */ -enum bxattr_rtn_code { - bxattr_rtn_fatal = -1, - bxattr_rtn_error = 0, - bxattr_rtn_ok = 1 -}; #define FILE_DAEMON 1 #include "lib/htable.h" @@ -55,14 +41,13 @@ enum bxattr_rtn_code { #include "fd_plugins.h" #include "fd_snapshot.h" #include "findlib/find.h" -#include "acl.h" -#include "xattr.h" +#include "xacl.h" #include "jcr.h" -#include "protos.h" /* file daemon prototypes */ +#include "protos.h" /* file daemon prototypes */ #include "lib/runscript.h" #include "lib/breg.h" #ifdef HAVE_LIBZ -#include /* compression headers */ +#include /* compression headers */ #else #define uLongf uint32_t #endif @@ -71,8 +56,8 @@ enum bxattr_rtn_code { #include #endif -extern CLIENT *me; /* "Global" Client resource */ -extern bool win32decomp; /* Use decomposition of BackupRead data */ +extern CLIENT *me; /* "Global" Client resource */ +extern bool win32decomp; /* Use decomposition of BackupRead data */ extern bool no_win32_write_errors; /* Ignore certain errors */ void terminate_filed(int sig); diff --git a/bacula/src/filed/protos.h b/bacula/src/filed/protos.h index 39160becb5..155432bde3 100644 --- a/bacula/src/filed/protos.h +++ b/bacula/src/filed/protos.h @@ -1,7 +1,7 @@ /* Bacula(R) - The Network Backup Solution - Copyright (C) 2000-2015 Kern Sibbald + Copyright (C) 2000-2016 Kern Sibbald The original author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -11,7 +11,7 @@ Public License, v3.0 ("AGPLv3") and some additional permissions and terms pursuant to its AGPLv3 Section 7. - This notice must be preserved when any source code is + This notice must be preserved when any source code is conveyed and/or propagated. Bacula(R) is a registered trademark of Kern Sibbald. @@ -52,11 +52,6 @@ void stop_heartbeat_monitor(JCR *jcr); void start_dir_heartbeat(JCR *jcr); void stop_dir_heartbeat(JCR *jcr); -/* From acl.c */ -bool backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt); -bacl_rtn_code restore_acl_streams(JCR *jcr, int stream, char *content, - uint32_t content_length); - /* from accurate.c */ bool accurate_finish(JCR *jcr); bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt); @@ -67,11 +62,6 @@ void accurate_free(JCR *jcr); void strip_path(FF_PKT *ff_pkt); void unstrip_path(FF_PKT *ff_pkt); -/* from xattr.c */ -bool backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt); -bxattr_rtn_code restore_xattr_streams(JCR *jcr, int stream, - char *content, uint32_t content_length); - /* from job.c */ findINCEXE *new_exclude(JCR *jcr); findINCEXE *new_preinclude(JCR *jcr); diff --git a/bacula/src/filed/restore.c b/bacula/src/filed/restore.c index b6c2819e85..20ef9abf22 100644 --- a/bacula/src/filed/restore.c +++ b/bacula/src/filed/restore.c @@ -1,7 +1,7 @@ /* Bacula(R) - The Network Backup Solution - Copyright (C) 2000-2015 Kern Sibbald + Copyright (C) 2000-2016 Kern Sibbald Copyright (C) 2000-2014 Free Software Foundation Europe e.V. The original author of Bacula is Kern Sibbald, with contributions @@ -12,7 +12,7 @@ Public License, v3.0 ("AGPLv3") and some additional permissions and terms pursuant to its AGPLv3 Section 7. - This notice must be preserved when any source code is + This notice must be preserved when any source code is conveyed and/or propagated. Bacula(R) is a registered trademark of Kern Sibbald. @@ -141,7 +141,7 @@ static bool restore_finderinfo(JCR *jcr, POOLMEM *buf, int32_t buflen) /* * Cleanup of delayed restore stack with streams for later processing. - */ + */ static void drop_delayed_restore_streams(r_ctx &rctx, bool reuse) { RESTORE_DATA_STREAM *rds; @@ -197,23 +197,22 @@ static inline void push_delayed_restore_stream(r_ctx &rctx, char *msg, int msgle * This can either be a delayed restore or direct restore. */ static inline bool do_restore_acl(JCR *jcr, int stream, char *content, - uint32_t content_length) + uint32_t content_length) { - switch (restore_acl_streams(jcr, stream, content, content_length)) { - case bacl_rtn_fatal: - return false; - case bacl_rtn_error: - /* - * Non-fatal errors, count them and when the number is under ACL_MAX_ERROR_PRINT_PER_JOB - * print the error message set by the lower level routine in jcr->errmsg. - */ - if (jcr->acl_ctx->nr_errors < ACL_MAX_ERROR_PRINT_PER_JOB) { - Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); - } - jcr->acl_ctx->nr_errors++; - break; - case bacl_rtn_ok: - break; + switch (jcr->xacl->restore_acl(jcr, stream, content, content_length)) { + case bRC_XACL_fatal: + return false; + case bRC_XACL_error: + /* + * Non-fatal errors, count them and when the number is under ACL_MAX_ERROR_PRINT_PER_JOB + * print the error message set by the lower level routine in jcr->errmsg. + */ + if (jcr->xacl->get_acl_nr_errors() < ACL_MAX_ERROR_PRINT_PER_JOB) { + Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + } + break; + default: + break; } return true; } @@ -223,23 +222,22 @@ static inline bool do_restore_acl(JCR *jcr, int stream, char *content, * This can either be a delayed restore or direct restore. */ static inline bool do_restore_xattr(JCR *jcr, int stream, char *content, - uint32_t content_length) + uint32_t content_length) { - switch (restore_xattr_streams(jcr, stream, content, content_length)) { - case bxattr_rtn_fatal: - return false; - case bxattr_rtn_error: - /* - * Non-fatal errors, count them and when the number is under XATTR_MAX_ERROR_PRINT_PER_JOB - * print the error message set by the lower level routine in jcr->errmsg. - */ - if (jcr->xattr_ctx->nr_errors < XATTR_MAX_ERROR_PRINT_PER_JOB) { - Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); - } - jcr->xattr_ctx->nr_errors++; - break; - case bxattr_rtn_ok: - break; + switch (jcr->xacl->restore_xattr(jcr, stream, content, content_length)) { + case bRC_XACL_fatal: + return false; + case bRC_XACL_error: + /* + * Non-fatal errors, count them and when the number is under XATTR_MAX_ERROR_PRINT_PER_JOB + * print the error message set by the lower level routine in jcr->errmsg. + */ + if (jcr->xacl->get_xattr_nr_errors() < XATTR_MAX_ERROR_PRINT_PER_JOB) { + Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + } + break; + default: + break; } return true; } @@ -279,40 +277,40 @@ static inline bool pop_delayed_data_streams(r_ctx &rctx) switch (rds->stream) { case STREAM_UNIX_ACCESS_ACL: case STREAM_UNIX_DEFAULT_ACL: - case STREAM_ACL_AIX_TEXT: - case STREAM_ACL_DARWIN_ACCESS: - case STREAM_ACL_FREEBSD_DEFAULT: - case STREAM_ACL_FREEBSD_ACCESS: - case STREAM_ACL_HPUX_ACL_ENTRY: - case STREAM_ACL_IRIX_DEFAULT: - case STREAM_ACL_IRIX_ACCESS: - case STREAM_ACL_LINUX_DEFAULT: - case STREAM_ACL_LINUX_ACCESS: - case STREAM_ACL_TRU64_DEFAULT: - case STREAM_ACL_TRU64_DEFAULT_DIR: - case STREAM_ACL_TRU64_ACCESS: - case STREAM_ACL_SOLARIS_POSIX: - case STREAM_ACL_SOLARIS_NFS4: - case STREAM_ACL_AFS_TEXT: - case STREAM_ACL_AIX_AIXC: - case STREAM_ACL_AIX_NFS4: - case STREAM_ACL_FREEBSD_NFS4: - case STREAM_ACL_HURD_DEFAULT: - case STREAM_ACL_HURD_ACCESS: + case STREAM_XACL_AIX_TEXT: + case STREAM_XACL_DARWIN_ACCESS: + case STREAM_XACL_FREEBSD_DEFAULT: + case STREAM_XACL_FREEBSD_ACCESS: + case STREAM_XACL_HPUX_ACL_ENTRY: + case STREAM_XACL_IRIX_DEFAULT: + case STREAM_XACL_IRIX_ACCESS: + case STREAM_XACL_LINUX_DEFAULT: + case STREAM_XACL_LINUX_ACCESS: + case STREAM_XACL_TRU64_DEFAULT: + case STREAM_XACL_TRU64_DEFAULT_DIR: + case STREAM_XACL_TRU64_ACCESS: + case STREAM_XACL_SOLARIS_POSIX: + case STREAM_XACL_SOLARIS_NFS4: + case STREAM_XACL_AFS_TEXT: + case STREAM_XACL_AIX_AIXC: + case STREAM_XACL_AIX_NFS4: + case STREAM_XACL_FREEBSD_NFS4: + case STREAM_XACL_HURD_DEFAULT: + case STREAM_XACL_HURD_ACCESS: if (!do_restore_acl(jcr, rds->stream, rds->content, rds->content_length)) { goto get_out; } break; - case STREAM_XATTR_HURD: - case STREAM_XATTR_IRIX: - case STREAM_XATTR_TRU64: - case STREAM_XATTR_AIX: - case STREAM_XATTR_OPENBSD: - case STREAM_XATTR_SOLARIS_SYS: - case STREAM_XATTR_DARWIN: - case STREAM_XATTR_FREEBSD: - case STREAM_XATTR_LINUX: - case STREAM_XATTR_NETBSD: + case STREAM_XACL_HURD_XATTR: + case STREAM_XACL_IRIX_XATTR: + case STREAM_XACL_TRU64_XATTR: + case STREAM_XACL_AIX_XATTR: + case STREAM_XACL_OPENBSD_XATTR: + case STREAM_XACL_SOLARIS_SYS_XATTR: + case STREAM_XACL_DARWIN_XATTR: + case STREAM_XACL_FREEBSD_XATTR: + case STREAM_XACL_LINUX_XATTR: + case STREAM_XACL_NETBSD_XATTR: if (!do_restore_xattr(jcr, rds->stream, rds->content, rds->content_length)) { goto get_out; } @@ -327,7 +325,7 @@ static inline bool pop_delayed_data_streams(r_ctx &rctx) rds->content = NULL; } } - + drop_delayed_restore_streams(rctx, true); return true; @@ -444,14 +442,7 @@ void do_restore(JCR *jcr) binit(&rctx.bfd); binit(&rctx.forkbfd); attr = rctx.attr = new_attr(jcr); - if (have_acl) { - jcr->acl_ctx = (acl_ctx_t *)malloc(sizeof(acl_ctx_t)); - memset(jcr->acl_ctx, 0, sizeof(acl_ctx_t)); - } - if (have_xattr) { - jcr->xattr_ctx = (xattr_ctx_t *)malloc(sizeof(xattr_ctx_t)); - memset(jcr->xattr_ctx, 0, sizeof(xattr_ctx_t)); - } + jcr->xacl = (XACL*)new_xacl(); Dsm_check(200); while (fdmsg->bget_msg(&bmsg) >= 0 && !job_canceled(jcr)) { @@ -829,26 +820,26 @@ void do_restore(JCR *jcr) case STREAM_UNIX_ACCESS_ACL: case STREAM_UNIX_DEFAULT_ACL: - case STREAM_ACL_AIX_TEXT: - case STREAM_ACL_DARWIN_ACCESS: - case STREAM_ACL_FREEBSD_DEFAULT: - case STREAM_ACL_FREEBSD_ACCESS: - case STREAM_ACL_HPUX_ACL_ENTRY: - case STREAM_ACL_IRIX_DEFAULT: - case STREAM_ACL_IRIX_ACCESS: - case STREAM_ACL_LINUX_DEFAULT: - case STREAM_ACL_LINUX_ACCESS: - case STREAM_ACL_TRU64_DEFAULT: - case STREAM_ACL_TRU64_DEFAULT_DIR: - case STREAM_ACL_TRU64_ACCESS: - case STREAM_ACL_SOLARIS_POSIX: - case STREAM_ACL_SOLARIS_NFS4: - case STREAM_ACL_AFS_TEXT: - case STREAM_ACL_AIX_AIXC: - case STREAM_ACL_AIX_NFS4: - case STREAM_ACL_FREEBSD_NFS4: - case STREAM_ACL_HURD_DEFAULT: - case STREAM_ACL_HURD_ACCESS: + case STREAM_XACL_AIX_TEXT: + case STREAM_XACL_DARWIN_ACCESS: + case STREAM_XACL_FREEBSD_DEFAULT: + case STREAM_XACL_FREEBSD_ACCESS: + case STREAM_XACL_HPUX_ACL_ENTRY: + case STREAM_XACL_IRIX_DEFAULT: + case STREAM_XACL_IRIX_ACCESS: + case STREAM_XACL_LINUX_DEFAULT: + case STREAM_XACL_LINUX_ACCESS: + case STREAM_XACL_TRU64_DEFAULT: + case STREAM_XACL_TRU64_DEFAULT_DIR: + case STREAM_XACL_TRU64_ACCESS: + case STREAM_XACL_SOLARIS_POSIX: + case STREAM_XACL_SOLARIS_NFS4: + case STREAM_XACL_AFS_TEXT: + case STREAM_XACL_AIX_AIXC: + case STREAM_XACL_AIX_NFS4: + case STREAM_XACL_FREEBSD_NFS4: + case STREAM_XACL_HURD_DEFAULT: + case STREAM_XACL_HURD_ACCESS: /* * Do not restore ACLs when * a) The current file is not extracted @@ -877,16 +868,16 @@ void do_restore(JCR *jcr) } break; - case STREAM_XATTR_HURD: - case STREAM_XATTR_IRIX: - case STREAM_XATTR_TRU64: - case STREAM_XATTR_AIX: - case STREAM_XATTR_OPENBSD: - case STREAM_XATTR_SOLARIS_SYS: - case STREAM_XATTR_DARWIN: - case STREAM_XATTR_FREEBSD: - case STREAM_XATTR_LINUX: - case STREAM_XATTR_NETBSD: + case STREAM_XACL_HURD_XATTR: + case STREAM_XACL_IRIX_XATTR: + case STREAM_XACL_TRU64_XATTR: + case STREAM_XACL_AIX_XATTR: + case STREAM_XACL_OPENBSD_XATTR: + case STREAM_XACL_SOLARIS_SYS_XATTR: + case STREAM_XACL_DARWIN_XATTR: + case STREAM_XACL_FREEBSD_XATTR: + case STREAM_XACL_LINUX_XATTR: + case STREAM_XACL_NETBSD_XATTR: /* * Do not restore Extended Attributes when * a) The current file is not extracted @@ -915,7 +906,7 @@ void do_restore(JCR *jcr) } break; - case STREAM_XATTR_SOLARIS: + case STREAM_XACL_SOLARIS_XATTR: /* * Do not restore Extended Attributes when * a) The current file is not extracted @@ -1014,34 +1005,34 @@ ok_out: */ Dmsg2(10, "End Do Restore. Files=%d Bytes=%s\n", jcr->JobFiles, edit_uint64(jcr->JobBytes, ec1)); - if (have_acl && jcr->acl_ctx->nr_errors > 0) { - Jmsg(jcr, M_WARNING, 0, _("Encountered %ld acl errors while doing restore\n"), - jcr->acl_ctx->nr_errors); + + if (jcr->xacl->get_acl_nr_errors() > 0) { + Jmsg(jcr, M_WARNING, 0, _("Encountered %ld acl errors while doing restore\n"), jcr->xacl->get_acl_nr_errors()); } - if (have_xattr && jcr->xattr_ctx->nr_errors > 0) { - Jmsg(jcr, M_WARNING, 0, _("Encountered %ld xattr errors while doing restore\n"), - jcr->xattr_ctx->nr_errors); + if (jcr->xacl->get_xattr_nr_errors() > 0) { + Jmsg(jcr, M_WARNING, 0, _("Encountered %ld xattr errors while doing restore\n"), jcr->xacl->get_xattr_nr_errors()); } + if (non_suppored_data > 1 || non_suppored_attr > 1) { Jmsg(jcr, M_WARNING, 0, _("%d non-supported data streams and %d non-supported attrib streams ignored.\n"), non_suppored_data, non_suppored_attr); - } + } if (non_suppored_rsrc) { Jmsg(jcr, M_INFO, 0, _("%d non-supported resource fork streams ignored.\n"), non_suppored_rsrc); - } + } if (non_suppored_finfo) { Jmsg(jcr, M_INFO, 0, _("%d non-supported Finder Info streams ignored.\n"), non_suppored_finfo); - } + } if (non_suppored_acl) { Jmsg(jcr, M_INFO, 0, _("%d non-supported acl streams ignored.\n"), non_suppored_acl); - } + } if (non_suppored_crypto) { Jmsg(jcr, M_INFO, 0, _("%d non-supported crypto streams ignored.\n"), non_suppored_acl); - } + } if (non_suppored_xattr) { Jmsg(jcr, M_INFO, 0, _("%d non-supported xattr streams ignored.\n"), non_suppored_xattr); - } - + } + /* Free Signature & Crypto Data */ free_signature(rctx); free_session(rctx); @@ -1055,7 +1046,7 @@ ok_out: crypto_cipher_free(rctx.cipher_ctx.cipher); rctx.cipher_ctx.cipher = NULL; } - + if (rctx.cipher_ctx.buf) { free_pool_memory(rctx.cipher_ctx.buf); rctx.cipher_ctx.buf = NULL; @@ -1076,22 +1067,11 @@ ok_out: jcr->compress_buf = NULL; jcr->compress_buf_size = 0; } - - if (have_acl && jcr->acl_ctx) { - if (jcr->acl_ctx->content) { - free(jcr->acl_ctx->content); - } - free(jcr->acl_ctx); - jcr->acl_ctx = NULL; - } - - if (have_xattr && jcr->xattr_ctx) { - if (jcr->xattr_ctx->content) { - free(jcr->xattr_ctx->content); - } - free(jcr->xattr_ctx); - jcr->xattr_ctx = NULL; - } + + if (jcr->xacl) { + delete(jcr->xacl); + jcr->xacl = NULL; + } /* Free the delayed stream stack list. */ if (rctx.delayed_streams) { @@ -1241,7 +1221,7 @@ bool decompress_data(JCR *jcr, int32_t stream, char **data, uint32_t *length) * NOTE! We only use uLong and Byte because they are * needed by the zlib routines, they should not otherwise * be used in Bacula. - */ + */ compress_len = jcr->compress_buf_size; Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, *length); while ((stat=uncompress((Byte *)jcr->compress_buf, &compress_len, @@ -1395,7 +1375,7 @@ int32_t extract_data(r_ctx &rctx, POOLMEM *buf, int32_t buflen) * and then buffer any remaining data. This should be effecient * as long as Bacula's block size is not significantly smaller than the * encryption block size (extremely unlikely!) - */ + */ unser_crypto_packet_len(cipher_ctx); Dmsg1(500, "Crypto unser block size=%d\n", cipher_ctx->packet_len - CRYPTO_LEN_SIZE); @@ -1438,7 +1418,7 @@ int32_t extract_data(r_ctx &rctx, POOLMEM *buf, int32_t buflen) memmove(cipher_ctx->buf, &cipher_ctx->buf[cipher_ctx->packet_len], cipher_ctx->buf_len); } - /* The packet was successfully written, reset the length so that + /* The packet was successfully written, reset the length so that * the next packet length may be re-read by unser_crypto_packet_len() */ cipher_ctx->packet_len = 0; } @@ -1480,7 +1460,7 @@ static bool close_previous_stream(r_ctx &rctx) /* Now perform the delayed restore of some specific data streams. */ rtn = pop_delayed_data_streams(rctx); - + /* Verify the cryptographic signature, if any */ rctx.type = rctx.attr->type; verify_signature(rctx); @@ -1497,7 +1477,7 @@ static bool close_previous_stream(r_ctx &rctx) } return rtn; -} +} /* * In the context of jcr, flush any remaining data from the cipher context, @@ -1672,7 +1652,7 @@ static bool verify_signature(r_ctx &rctx) } } if (jcr->crypto.digest) { - /* Use digest computed while writing the file to verify + /* Use digest computed while writing the file to verify * the signature */ if ((err = crypto_sign_verify(sig, keypair, jcr->crypto.digest)) != CRYPTO_ERROR_NONE) { Dmsg1(50, "Bad signature on %s\n", jcr->last_fname); @@ -1686,7 +1666,7 @@ static bool verify_signature(r_ctx &rctx) jcr->crypto.digest = digest; /* Checksum the entire file - * Make sure we don't modify JobBytes by saving and + * Make sure we don't modify JobBytes by saving and * restoring it */ saved_bytes = jcr->JobBytes; if (find_one_file(jcr, jcr->ff, do_file_digest, jcr->last_fname, (dev_t)-1, 1) != 0) { diff --git a/bacula/src/filed/xacl.c b/bacula/src/filed/xacl.c new file mode 100644 index 0000000000..5b60d7d4d6 --- /dev/null +++ b/bacula/src/filed/xacl.c @@ -0,0 +1,1332 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + * + * A specialized class to handle ACL and XATTR in Bacula Enterprise. + * The runtime consist of two parts: + * 1. OS independent class: XACL + * 2. OS dependent subclass: XACL_* + * + * OS dependent subclasses are available for the following OS: + * - Darwin (OSX) + * - FreeBSD (POSIX and NFSv4/ZFS acls) + * - Linux + * - Solaris (POSIX and NFSv4/ZFS acls) + * + * OS dependend subclasses in progress: + * - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface) + * - HPUX + * - IRIX + * - Tru64 + * + * OS independent class support AFS acls using the pioctl interface. + * + * ACLs are saved in OS native text format provided by acl(3) API and uses + * different streams for all different platforms. + * XATTRs are saved in OS independent format (Bacula own) and uses different streams + * for all different platforms. In theory it is possible to restore XATTRs from + * particular OS on different OS platform. But this functionality is not available. + * Above behavior is a backward compatibility with previous Bacula implementation + * we need to maintain. + * + * During OS specyfic implementation of XACL you need to implement a following methods: + * + * [xacl] - indicates xacl function/method to call + * [os] - indicates OS specyfic function, which could be different on specyfic OS + * (we use a Linux api calls as an example) + * + * ::os_get_acl(JCR *jcr, XACL_type xacltype) + * + * 1. get binary form of the acl - acl_get_file[os] + * 2. check if acl is trivial if required - call acl_issimple[xacl] + * 3. translate binary form into text representation - acl_to_text[os] + * 4. save acl text into content - set_content[xacl] + * 5. if acl not supported on filesystem - call clear_flag(XACL_FLAG_NATIVE)[xacl] + * + * ::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt) + * + * 1. call os_get_acl[xacl] for all supported ACL_TYPES + * 2. call send_acl_stream[xacl] for all supported ACL_STREAMS + * + * ::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length) + * + * 1. prepare acl binary form from text representation stored in content - acl_from_text[os] + * 2. set acl on file - acl_set_file[os] + * 3. if acl not supported on filesystem, clear_flag(XACL_FLAG_NATIVE) + * + * ::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length) + * + * 1. call os_set_acl for all supported ACL_TYPES + * + * ::os_get_xattr_names (JCR *jcr, int namespace, POOLMEM ** pxlist, uint32_t * xlen) + * + * 1. get a size of the extended attibutes list for the file - llistxattr[os] + * in most os'es it is required to have a sufficient space for attibutes list + * and we wont allocate too much and too low space + * 2. allocate the buffer of required space + * 3. get an extended attibutes list for file - llistxattr[os] + * 4. return allocated space buffer in pxlist and length of the buffer in xlen + * + * ::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen) + * + * 1. get a size of the extended attibute value for the file - lgetxattr[os] + * in most os'es it is required to have a sufficient space for attibute value + * and we wont allocate too much and too low space + * 2. allocate the buffer of required space + * 3. get an extended attibute value for file - lgetxattr[os] + * 4. return allocated space buffer in pvalue and length of the buffer in plen + * + * ::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt) + * + * 1. get a list of extended attributes (name and value) for a file; in most implementations + * it require to get a separate list of attributes names and separate values for every name, + * so it is: + * 1A. get a list of xattr attribute names available on file - os_get_xattr_names[xacl] + * 1B. for every attribute name get a value - os_get_xattr_value[xacl] + * You should skip some OS specyfic attributes like ACL attributes or NFS4; you can use + * check_xattr_skiplists[xacl] for this + * 1C. build a list [type alist] of name/value pairs stored in XACL_xattr struct + * 2. if the xattr list is not empty then serialize the list using serialize_xattr_stream[xacl] + * 3. call send_xattr_stream[xacl] + * + * ::os_set_xattr (JCR *jcr, XACL_xattr *xattr) + * + * 1. set xattr on file using name/value in xattr - lsetxattr[os] + * 2. if xattr not supported on filesystem - call clear_flag(XACL_FLAG_NATIVE)[xacl] + * + * ::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length) + * + * 1. unserialize backup stream + * 2. for every extended attribute restored call os_set_xattr[xacl] to set this attribute on file + */ + +#include "bacula.h" +#include "filed.h" + +/* + * This is a constructor of the base XACL class which is OS independent + * + * - for initialization it uses ::init() + * + */ +XACL::XACL (){ + init(); +}; + +/* + * This is a destructor of the XACL class + */ +XACL::~XACL (){ + free_pool_memory(content); +}; + +/* + * Initialization routine + * - initializes all variables to required status + * - allocates required memory + */ +void XACL::init(){ +#if defined(HAVE_ACL) + acl_ena = TRUE; +#else + acl_ena = FALSE; +#endif + +#if defined(HAVE_XATTR) + xattr_ena = TRUE; +#else + xattr_ena = FALSE; +#endif + + /* generic variables */ + flags = XACL_FLAG_NONE; + current_dev = 0; + content = get_pool_memory(PM_BSOCK); /* it is better to have a 4k buffer */ + content_len = 0; + acl_nr_errors = 0; + xattr_nr_errors = 0; + acl_streams = NULL; + default_acl_streams = NULL; + xattr_streams = NULL; + xattr_skiplist = NULL; + xattr_acl_skiplist = NULL; +}; + +/* + * Enables ACL handling in runtime, could be disabled with disable_acl + * when ACL is not configured then cannot change status + */ +void XACL::enable_acl(){ +#if defined(HAVE_ACL) + acl_ena = TRUE; +#endif +}; + +/* + * Disables ACL handling in runtime, could be enabled with enable_acl + * when ACL is configured + */ +void XACL::disable_acl(){ + acl_ena = FALSE; +}; + +/* + * Enables XATTR handling in runtime, could be disabled with disable_xattr + * when XATTR is not configured then cannot change status + */ +void XACL::enable_xattr(){ +#ifdef HAVE_XATTR + xattr_ena = TRUE; +#endif +}; + +/* + * Disables XATTR handling in runtime, could be enabled with enable_xattr + * when XATTR is configured + */ +void XACL::disable_xattr(){ + xattr_ena = FALSE; +}; + +/* + * Copies a text into a content variable and sets a constent_len respectively + * + * in: + * text - a standard null terminated string + * out: + * pointer to content variable to use externally + */ +POOLMEM * XACL::set_content(char *text){ + content_len = pm_strcpy(&content, text); + return content; +}; + +/* + * Copies a data with length of len into a content variable + * + * in: + * data - data pointer to copy into content buffer + * out: + * pointer to content variable to use externally + */ +POOLMEM * XACL::set_content(char *data, int len){ + content_len = pm_memcpy(&content, data, len); + return content; +}; + +/* + * Check if we changed the device, + * if so setup a flags + * + * in: + * jcr - Job Control Record + * out: + * bRC_XACL_ok - change of device checked and finish succesful + * bRC_XACL_error - encountered error + * bRC_XACL_skip - cannot verify device - no file found + * bRC_XACL_inval - invalid input data + */ +bRC_XACL XACL::check_dev (JCR *jcr){ + + int lst; + struct stat st; + + /* sanity check of input variables */ + if (jcr == NULL || jcr->last_fname == NULL){ + return bRC_XACL_inval; + } + + lst = lstat(jcr->last_fname, &st); + switch (lst){ + case -1: { + berrno be; + switch (errno){ + case ENOENT: + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("Unable to stat file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "Unable to stat file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + break; + } + case 0: + break; + } + + check_dev(jcr, st.st_dev); + + return bRC_XACL_ok; +}; + +/* + * Check if we changed the device, if so setup a flags + * + * in: + * jcr - Job Control Record + * out: + * internal flags status set + */ +void XACL::check_dev (JCR *jcr, uint32_t dev){ + + /* sanity check of input variables */ + if (jcr == NULL || jcr->last_fname == NULL){ + return; + } + + if (current_dev != dev){ + flags = XACL_FLAG_NONE; +#if defined(HAVE_AFS_ACL) + /* handle special fs: AFS */ + if (fstype_equals(jcr->last_fname, "afs")){ + set_flag(XACL_FLAG_AFS); + } else { + set_flag(XACL_FLAG_NATIVE); + } +#else + set_flag(XACL_FLAG_NATIVE); +#endif + current_dev = dev; + } +}; + +/* + * It sends a stream located in this->content to Storage Daemon, so the main Bacula + * backup loop is free from this. It sends a header followed by data. + * + * in: + * jcr - Job Control Record + * stream - a stream number to save + * out: + * bRC_XACL_inval - when supplied variables are incorrect + * bRC_XACL_fatal - when we can't send data to the SD + * bRC_XACL_ok - send finish without errors + */ +bRC_XACL XACL::send_acl_stream(JCR *jcr, int stream){ + + BSOCK * sd; + POOLMEM * msgsave; +#ifdef FD_NO_SEND_TEST + return bRC_XACL_ok; +#endif + + /* sanity check of input variables */ + if (jcr == NULL || jcr->store_bsock == NULL){ + return bRC_XACL_inval; + } + if (content_len <= 0){ + return bRC_XACL_ok; + } + + sd = jcr->store_bsock; + /* send header */ + if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)){ + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); + return bRC_XACL_fatal; + } + + /* send the buffer to the storage deamon */ + Dmsg1(400, "Backing up ACL <%s>\n", content); + msgsave = sd->msg; + sd->msg = content; + sd->msglen = content_len + 1; + if (!sd->send()){ + sd->msg = msgsave; + sd->msglen = 0; + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); + return bRC_XACL_fatal; + } + + jcr->JobBytes += sd->msglen; + sd->msg = msgsave; + if (!sd->signal(BNET_EOD)){ + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); + return bRC_XACL_fatal; + } + + Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname); + return bRC_XACL_ok; +}; + +/* + * It sends a stream located in this->content to Storage Daemon, so the main Bacula + * backup loop is free from this. It sends a header followed by data. + * + * in: + * jcr - Job Control Record + * stream - a stream number to save + * out: + * bRC_XACL_inval - when supplied variables are incorrect + * bRC_XACL_fatal - when we can't send data to the SD + * bRC_XACL_ok - send finish without errors + */ +bRC_XACL XACL::send_xattr_stream(JCR *jcr, int stream){ + + BSOCK * sd; + POOLMEM * msgsave; +#ifdef FD_NO_SEND_TEST + return bRC_XACL_ok; +#endif + + /* sanity check of input variables */ + if (jcr == NULL || jcr->store_bsock == NULL){ + return bRC_XACL_inval; + } + if (content_len <= 0){ + return bRC_XACL_ok; + } + + sd = jcr->store_bsock; + /* send header */ + if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)){ + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); + return bRC_XACL_fatal; + } + + /* send the buffer to the storage deamon */ + Dmsg1(400, "Backing up XATTR <%s>\n", content); + msgsave = sd->msg; + sd->msg = content; + sd->msglen = content_len; + if (!sd->send()){ + sd->msg = msgsave; + sd->msglen = 0; + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); + return bRC_XACL_fatal; + } + + jcr->JobBytes += sd->msglen; + sd->msg = msgsave; + if (!sd->signal(BNET_EOD)){ + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); + return bRC_XACL_fatal; + } + Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname); + return bRC_XACL_ok; +}; + +/* + * The main public backup method for ACL + * + * in: + * jcr - Job Control Record + * ff_pkt - file backup record + * out: + * bRC_XACL_fatal - when ACL backup is not compiled in Bacula + * bRC_XACL_ok - backup finish without problems + * bRC_XACL_error - when you can't backup acl data because some error + */ +bRC_XACL XACL::backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + +#if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL) + Jmsg(jcr, M_FATAL, 0, "ACL backup requested but not configured in Bacula.\n"); + return bRC_XACL_fatal; +#else + /* sanity check of input variables and verify if engine is enabled */ + if (acl_ena && jcr != NULL && ff_pkt != NULL){ + /* acl engine enabled, proceed */ + bRC_XACL rc; + /* + * No acl request for link or plugin + * + * TODO: it should be possible to handle ACL/XATTR for cmd plugins + */ + if (!(ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK && !ff_pkt->cmd_plugin)){ + return bRC_XACL_ok; + } + + jcr->errmsg[0] = 0; + check_dev(jcr, ff_pkt->statp.st_dev); + +#if defined(HAVE_AFS_ACL) + if (flags & XACL_FLAG_AFS){ + Dmsg0(400, "make AFS ACL call\n"); + rc = afs_backup_acl(jcr, ff_pkt); + goto bail_out; + } +#endif + +#if defined(HAVE_ACL) + if (flags & XACL_FLAG_NATIVE){ + Dmsg0(400, "make Native ACL call\n"); + rc = os_backup_acl(jcr, ff_pkt); + } else { + /* skip acl backup */ + return bRC_XACL_ok; + } +#endif + +#if defined(HAVE_AFS_ACL) + bail_out: +#endif + if (rc == bRC_XACL_error){ + if (acl_nr_errors < ACL_MAX_ERROR_PRINT_PER_JOB){ + if (!jcr->errmsg[0]){ + Jmsg(jcr, M_WARNING, 0, "No OS ACL configured.\n"); + } else { + Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + } + inc_acl_errors(); + } + return bRC_XACL_ok; + } + return rc; + } + return bRC_XACL_ok; +#endif +}; + +/* + * The main public restore method for ACL + * + * in: + * jcr - Job Control Record + * stream - a backup stream type number to restore_acl + * data - a potinter to the data stream to restore + * length - a data stream length + * out: + * bRC_XACL_fatal - when ACL restore is not compiled in Bacula + * bRC_XACL_ok - restore finish without problems + * bRC_XACL_error - when you can't restore a stream because some error + */ +bRC_XACL XACL::restore_acl (JCR *jcr, int stream, char *data, uint32_t length){ + +#if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL) + Jmsg(jcr, M_FATAL, 0, "ACL retore requested but not configured in Bacula.\n"); + return bRC_XACL_fatal; +#else + /* sanity check of input variables and verify if engine is enabled */ + if (acl_ena && jcr != NULL && data != NULL){ + /* acl engine enabled, proceed */ + int a; + bRC_XACL rc = check_dev(jcr); + + switch (rc){ + case bRC_XACL_skip: + return bRC_XACL_ok; + case bRC_XACL_ok: + break; + default: + return rc; + } + + /* copy a data into a content buffer */ + set_content(data, length); + + switch (stream){ +#if defined(HAVE_AFS_ACL) + case STREAM_XACL_AFS_TEXT: + if (flags & XACL_FLAG_AFS){ + return afs_restore_acl(jcr, stream); + } else { + /* + * Increment error count but don't log an error again for the same filesystem. + */ + inc_acl_errors(); + return bRC_XACL_ok; + } +#endif +#if defined(HAVE_ACL) + case STREAM_UNIX_ACCESS_ACL: + case STREAM_UNIX_DEFAULT_ACL: + if (flags & XACL_FLAG_NATIVE){ + return os_restore_acl(jcr, stream, content, content_len); + } else { + inc_acl_errors(); + return bRC_XACL_ok; + } + break; + default: + if (flags & XACL_FLAG_NATIVE){ + for (a = 0; acl_streams[a] > 0; a++){ + if (acl_streams[a] == stream){ + return os_restore_acl(jcr, stream, content, content_len); + } + } + for (a = 0; default_acl_streams[a] > 0; a++){ + if (default_acl_streams[a] == stream){ + return os_restore_acl(jcr, stream, content, content_len); + } + } + } else { + inc_acl_errors(); + return bRC_XACL_ok; + } + break; +#else + default: + break; +#endif + } + /* cannot find a valid stream to support */ + Qmsg2(jcr, M_WARNING, 0, _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"), jcr->last_fname, stream); + return bRC_XACL_error; + } + return bRC_XACL_ok; +#endif +}; + +/* + * The main public backup method for XATTR + * + * in: + * jcr - Job Control Record + * ff_pkt - file backup record + * out: + * bRC_XACL_fatal - when XATTR backup is not compiled in Bacula + * bRC_XACL_ok - backup finish without problems + * bRC_XACL_error - when you can't backup xattr data because some error + */ +bRC_XACL XACL::backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + +#if !defined(HAVE_XATTR) + Jmsg(jcr, M_FATAL, 0, "XATTR backup requested but not configured in Bacula.\n"); + return bRC_XACL_fatal; +#else + /* sanity check of input variables and verify if engine is enabled */ + if (xattr_ena && jcr != NULL && ff_pkt != NULL){ + /* xattr engine enabled, proceed */ + bRC_XACL rc; + /* + * No xattr request for plugin + * + * TODO: it should be possible to handle ACL/XATTR for cmd plugins + */ + if (!(ff_pkt->flags & FO_XATTR && !ff_pkt->cmd_plugin)){ + return bRC_XACL_ok; + } + + jcr->errmsg[0] = 0; + check_dev(jcr, ff_pkt->statp.st_dev); + + if (flags & XACL_FLAG_NATIVE){ + Dmsg0(400, "make Native XATTR call\n"); + rc = os_backup_xattr(jcr, ff_pkt); + } else { + /* skip xattr backup */ + return bRC_XACL_ok; + } + + if (rc == bRC_XACL_error){ + if (xattr_nr_errors < XATTR_MAX_ERROR_PRINT_PER_JOB){ + if (!jcr->errmsg[0]){ + Jmsg(jcr, M_WARNING, 0, "No OS XATTR configured.\n"); + } else { + Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + } + inc_xattr_errors(); + } + return bRC_XACL_ok; + } + return rc; + } + return bRC_XACL_ok; +#endif +}; + +/* + * The main public restore method for XATTR + * + * in: + * jcr - Job Control Record + * stream - a backup stream type number to restore_acl + * data - a potinter to the data stream to restore + * length - a data stream length + * out: + * bRC_XACL_fatal - when XATTR restore is not compiled in Bacula + * bRC_XACL_ok - restore finish without problems + * bRC_XACL_error - when you can't restore a stream because some error + */ +bRC_XACL XACL::restore_xattr (JCR *jcr, int stream, char *data, uint32_t length){ + +#if !defined(HAVE_XATTR) + Jmsg(jcr, M_FATAL, 0, "XATTR retore requested but not configured in Bacula.\n"); + return bRC_XACL_fatal; +#else + /* sanity check of input variables and verify if engine is enabled */ + if (xattr_ena && jcr != NULL && data != NULL){ + /* xattr engine enabled, proceed */ + int a; + bRC_XACL rc = check_dev(jcr); + + switch (rc){ + case bRC_XACL_skip: + return bRC_XACL_ok; + case bRC_XACL_ok: + break; + default: + return rc; + } + + /* copy a data into a content buffer */ + set_content(data, length); + + if (flags & XACL_FLAG_NATIVE){ + for (a = 0; xattr_streams[a] > 0; a++){ + if (xattr_streams[a] == stream){ + return os_restore_xattr(jcr, stream, content, content_len); + } + } + } else { + inc_xattr_errors(); + return bRC_XACL_ok; + } + /* cannot find a valid stream to support */ + Qmsg2(jcr, M_WARNING, 0, _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"), jcr->last_fname, stream); + return bRC_XACL_error; + } + return bRC_XACL_ok; +#endif +}; + +/* + * Performs a generic ACL backup using OS specyfic methods for + * getting acl data from file + * + * in: + * jcr - Job Control Record + * ff_pkt - file to backup control package + * out: + * bRC_XACL_ok - backup of acl's was successful + * bRC_XACL_fatal - was an error during acl backup + */ +bRC_XACL XACL::generic_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_XACL_inval; + } + + if (os_get_acl(jcr, XACL_TYPE_ACCESS) == bRC_XACL_fatal){ + /* XXX: check if os_get_acl return fatal and decide what to do when error is returned */ + return bRC_XACL_fatal; + } + + if (content_len > 0){ + if (send_acl_stream(jcr, acl_streams[0]) == bRC_XACL_fatal){ + return bRC_XACL_fatal; + } + } + + if (ff_pkt->type == FT_DIREND){ + if (os_get_acl(jcr, XACL_TYPE_DEFAULT) == bRC_XACL_fatal){ + return bRC_XACL_fatal; + } + if (content_len > 0){ + if (send_acl_stream(jcr, default_acl_streams[0]) == bRC_XACL_fatal){ + return bRC_XACL_fatal; + } + } + } + return bRC_XACL_ok; +}; + +/* + * Performs a generic ACL restore using OS specyfic methods for + * setting acl data on file. + * + * in: + * jcr - Job Control Record + * stream - a stream number to restore + * out: + * bRC_XACL_ok - backup of acl's was successful + * bRC_XACL_error - was an error during acl backup + * bRC_XACL_fatal - was a fatal error during acl backup or input data is invalid + */ +bRC_XACL XACL::generic_restore_acl (JCR *jcr, int stream){ + + unsigned int count; + + /* sanity check of input variables */ + if (jcr == NULL){ + return bRC_XACL_inval; + } + + switch (stream){ + case STREAM_UNIX_ACCESS_ACL: + return os_set_acl(jcr, XACL_TYPE_ACCESS, content, content_len); + case STREAM_UNIX_DEFAULT_ACL: + return os_set_acl(jcr, XACL_TYPE_DEFAULT, content, content_len); + default: + for (count = 0; acl_streams[count] > 0; count++){ + if (acl_streams[count] == stream){ + return os_set_acl(jcr, XACL_TYPE_ACCESS, content, content_len); + } + } + for (count = 0; default_acl_streams[count] > 0; count++){ + if (default_acl_streams[count] == stream){ + return os_set_acl(jcr, XACL_TYPE_DEFAULT, content, content_len); + } + } + break; + } + return bRC_XACL_error; +}; + +/* + * Checks if supplied xattr attribute name is indicated on OS specyfic lists + * + * in: + * jcr - Job Control Record + * ff_pkt - file to backup control package + * name - a name of the attribute to check + * out: + * TRUE - the attribute name is found on OS specyfic skip lists and should be skipped during backup + * FALSE - the attribute should be saved on backup stream + */ +bool XACL::check_xattr_skiplists (JCR *jcr, FF_PKT *ff_pkt, char * name){ + + bool skip = FALSE; + int count; + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL || name == NULL){ + return false; + } + + /* + * On some OSes you also get the acls in the extented attribute list. + * So we check if we are already backing up acls and if we do we + * don't store the extended attribute with the same info. + */ + if (ff_pkt->flags & FO_ACL){ + for (count = 0; xattr_acl_skiplist[count] != NULL; count++){ + if (bstrcmp(name, xattr_acl_skiplist[count])){ + skip = true; + break; + } + } + } + /* on some OSes we want to skip certain xattrs which are in the xattr_skiplist array. */ + if (!skip){ + for (count = 0; xattr_skiplist[count] != NULL; count++){ + if (bstrcmp(name, xattr_skiplist[count])){ + skip = true; + break; + } + } + } + + return skip; +}; + + +/* + * Performs generic XATTR backup using OS specyfic methods for + * getting xattr data from files - os_get_xattr_names and os_get_xattr_value + * + * in: + * jcr - Job Control Record + * ff_pkt - file to backup control package + * out: + * bRC_XACL_ok - xattr backup ok or no xattr to backup found + * bRC_XACL_error/fatal - an error or fatal error occurred + * bRC_XACL_inval - input variables was invalid + */ +bRC_XACL XACL::generic_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_XACL rc; + POOLMEM *xlist; + uint32_t xlen; + char *name; + uint32_t name_len; + POOLMEM *value; + uint32_t value_len; + bool skip; + alist *xattr_list = NULL; + int xattr_count = 0; + uint32_t len = 0; + XACL_xattr *xattr; + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_XACL_inval; + } + + /* xlist is allocated as POOLMEM by os_get_xattr_names */ + rc = os_get_xattr_names(jcr, &xlist, &xlen); + switch (rc){ + case bRC_XACL_ok: + /* it's ok, so go further */ + break; + case bRC_XACL_skip: + case bRC_XACL_cont: + /* no xattr available, so skip rest of it */ + return bRC_XACL_ok; + default: + return rc; + } + + /* follow the list of xattr names and get the values + * TODO: change a standard NULL-terminated list of names into alist of structures */ + for (name = xlist; (name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){ + + name_len = strlen(name); + skip = check_xattr_skiplists(jcr, ff_pkt, name); + if (skip || name_len == 0){ + Dmsg1(100, "Skipping xattr named \"%s\"\n", name); + continue; + } + + /* value is allocated as POOLMEM by os_get_xattr_value */ + rc = os_get_xattr_value(jcr, name, &value, &value_len); + switch (rc){ + case bRC_XACL_ok: + /* it's ok, so go further */ + break; + case bRC_XACL_skip: + /* no xattr available, so skip rest of it */ + free_pool_memory(xlist); + return bRC_XACL_ok; + default: + /* error / fatal */ + free_pool_memory(xlist); + return rc; + } + + /* + * we have a name of the extended attribute in the name variable + * and value of the extended attribute in the value variable + * so we need to build a list + */ + xattr = (XACL_xattr*)malloc(sizeof(XACL_xattr)); + xattr->name_len = name_len; + xattr->name = name; + xattr->value_len = value_len; + xattr->value = value; + /* magic name_len name value_len value */ + len += sizeof(uint32_t) + sizeof(uint32_t) + name_len + sizeof(uint32_t) + value_len; + + if (xattr_list == NULL){ + xattr_list = New(alist(10, not_owned_by_alist)); + } + xattr_list->append(xattr); + xattr_count++; + } + if (xattr_count > 0){ + /* serialize the stream */ + rc = serialize_xattr_stream(jcr, len, xattr_list); + if (rc != bRC_XACL_ok){ + Mmsg(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), jcr->last_fname); + Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", jcr->last_fname); + goto bailout; + } else { + /* send data to SD */ + rc = send_xattr_stream(jcr, xattr_streams[0]); + } + } else { + rc = bRC_XACL_ok; + } + +bailout: + /* free allocated data */ + if (xattr_list != NULL){ + foreach_alist(xattr, xattr_list){ + if (xattr == NULL){ + break; + } + if (xattr->value){ + free_pool_memory(xattr->value); + } + free(xattr); + } + delete xattr_list; + } + if (xlist != NULL){ + free_pool_memory(xlist); + } + + return rc; +}; + +/* + * Performs a generic XATTR restore using OS specyfic methods for + * setting XATTR data on file. + * + * in: + * jcr - Job Control Record + * stream - a stream number to restore + * out: + * bRC_XACL_ok - restore of acl's was successful + * bRC_XACL_error - was an error during xattr restore + * bRC_XACL_fatal - was a fatal error during xattr restore + * bRC_XACL_inval - input variables was invalid + */ +bRC_XACL XACL::generic_restore_xattr (JCR *jcr, int stream){ + + bRC_XACL rc = bRC_XACL_ok; + alist *xattr_list; + XACL_xattr *xattr; + + /* sanity check of input variables */ + if (jcr == NULL){ + return bRC_XACL_inval; + } + + /* empty list */ + xattr_list = New(alist(10, not_owned_by_alist)); + + /* unserialize data */ + unserialize_xattr_stream(jcr, content, content_len, xattr_list); + + /* follow the list to set all attributes */ + foreach_alist(xattr, xattr_list){ + rc = os_set_xattr(jcr, xattr); + if (rc != bRC_XACL_ok){ + Dmsg2(100, "Failed to set extended attribute %s on file \"%s\"\n", xattr->name, jcr->last_fname); + goto bailout; + } + } + +bailout: + /* free allocated data */ + if (xattr_list != NULL){ + foreach_alist(xattr, xattr_list){ + if (xattr == NULL){ + break; + } + if (xattr->name){ + free(xattr->name); + } + if (xattr->value){ + free(xattr->value); + } + free(xattr); + } + delete xattr_list; + } + return rc; +}; + +/* + * Initialize variables acl_streams and default_acl_streams for a specified OS. + * The rutine should be called from object instance constructor + * + * in: + * pacl - acl streams supported for specyfic OS + * pacl_def - default (directory) acl streams supported for specyfic OS + */ +void XACL::set_acl_streams (const int *pacl, const int *pacl_def){ + + acl_streams = pacl; + default_acl_streams = pacl_def; +}; + +/* + * Initialize a variable xattr_streams for a specified OS. + * The rutine should be called from object instance constructor + * + * in: + * pxattr - xattr streams supported for specyfic OS + */ +void XACL::set_xattr_streams (const int *pxattr){ + + xattr_streams = pxattr; +}; + +/* + * Initialize variables xattr_skiplist and xattr_acl_skiplist for a specified OS. + * The rutine should be called from object instance constructor + * + * in: + * pxattr - xattr skip list for specyfic OS + * pxattr_acl - xattr acl names skip list for specyfic OS + */ +void XACL::set_xattr_skiplists (const char **pxattr, const char **pxattr_acl){ + + xattr_skiplist = pxattr; + xattr_acl_skiplist = pxattr_acl; +}; + +/* + * Serialize the XATTR stream which will be saved into archive. Serialization elements cames from + * a list and for backward compatibility we produce the same stream as prievous Bacula versions. + * + * serialized stream consists of the following elements: + * magic - A magic string which makes it easy to detect any binary incompatabilites + * required for backward compatibility + * name_len - The length of the following xattr name + * name - The name of the extended attribute + * value_len - The length of the following xattr data + * value - The actual content of the extended attribute only if value_len is greater then zero + * + * in: + * jcr - Job Control Record + * len - expected serialize length + * list - a list of xattr elements to serialize + * out: + * bRC_XACL_ok - when serialization was perfect + * bRC_XACL_inval - when we have invalid variables + * bRC_XACL_error - illegal attribute name + */ +bRC_XACL XACL::serialize_xattr_stream(JCR *jcr, uint32_t len, alist *list){ + + ser_declare; + XACL_xattr *xattr; + + /* sanity check of input variables */ + if (jcr == NULL || list == NULL){ + return bRC_XACL_inval; + } + + /* we serialize data direct to content buffer, so check if data fits */ + content = check_pool_memory_size(content, len + 20); + ser_begin(content, len + 20); + + foreach_alist(xattr, list){ + if (xattr == NULL){ + break; + } + /* + * serialize data + * + * we have to start with the XATTR_MAGIC for backward compatibility (the magic is silly) + */ + ser_uint32(XATTR_MAGIC); + /* attribute name length and name itself */ + if (xattr->name_len > 0 && xattr->name){ + ser_uint32(xattr->name_len); + ser_bytes(xattr->name, xattr->name_len); + } else { + /* error - name cannot be empty */ + Mmsg0(jcr->errmsg, _("Illegal empty xattr attribute name\n")); + Dmsg0(100, "Illegal empty xattr attribute name\n"); + return bRC_XACL_error; + } + /* attibute value length and value itself */ + ser_uint32(xattr->value_len); + if (xattr->value_len > 0 && xattr->value){ + ser_bytes(xattr->value, xattr->value_len); + Dmsg3(100, "Backup xattr named %s, value %*.s\n", xattr->name, xattr->value_len, xattr->value); + } else { + Dmsg1(100, "Backup empty xattr named %s\n", xattr->name); + } + } + + ser_end(content, len + 20); + content_len = ser_length(content); + + return bRC_XACL_ok; +}; + +/* + * Unserialize XATTR stream on *content and produce a xattr *list which contain + * key => value pairs + * + * in: + * jcr - Job Control Record + * content - a stream content to unserialize + * length - a content length + * list - a pointer to the xattr list to populate + * out: + * bRC_XACL_ok - when unserialize was perfect + * bRC_XACL_inval - when we have invalid variables + * list - key/value pairs populated xattr list + */ +bRC_XACL XACL::unserialize_xattr_stream(JCR *jcr, char *content, uint32_t length, alist *list){ + + unser_declare; + uint32_t magic; + XACL_xattr *xattr; + + /* sanity check of input variables */ + if (jcr == NULL || content == NULL || list == NULL){ + return bRC_XACL_inval; + } + + unser_begin(content, length); + while (unser_length(content) < length){ + /* + * Sanity check of correct stream magic number + * Someone was too paranoid to implement this kind of verification in original Bacula code + * Unfortunate for backward compatibility we have to follow this insane implementation + * + * XXX: design a new xattr stream format + */ + unser_uint32(magic); + if (magic != XATTR_MAGIC){ + Mmsg(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"), jcr->last_fname); + Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n", jcr->last_fname); + return bRC_XACL_error; + } + /* first attribute name length */ + xattr = (XACL_xattr *)malloc(sizeof(XACL_xattr)); + unser_uint32(xattr->name_len); + if (xattr->name_len == 0){ + /* attribute name cannot be empty */ + Mmsg(jcr->errmsg, _("Illegal xattr stream, xattr name length <= 0 on file \"%s\"\n"), jcr->last_fname); + Dmsg1(100, "Illegal xattr stream, xattr name length <= 0 on file \"%s\"\n", jcr->last_fname); + free(xattr); + return bRC_XACL_error; + } + /* followed by attribute name itself */ + xattr->name = (char *)malloc(xattr->name_len + 1); + unser_bytes(xattr->name, xattr->name_len); + xattr->name[xattr->name_len] = '\0'; + /* attribute value */ + unser_uint32(xattr->value_len); + if (xattr->value_len > 0){ + /* we have a value */ + xattr->value = (char *)malloc(xattr->value_len + 1); + unser_bytes(xattr->value, xattr->value_len); + xattr->value[xattr->value_len] = '\0'; + Dmsg3(100, "Restoring xattr named %s, value %.*s\n", xattr->name, xattr->value_len, xattr->value); + } else { + /* value is empty */ + xattr->value = NULL; + Dmsg1(100, "Restoring empty xattr named %s\n", xattr->name); + } + list->append(xattr); + } + unser_end(content, length); + + return bRC_XACL_ok; +}; + +#if defined(HAVE_AFS_ACL) + +#if defined(HAVE_AFS_AFSINT_H) && defined(HAVE_AFS_VENUS_H) +#include +#include +#else +#error "configure failed to detect availability of afs/afsint.h and/or afs/venus.h" +#endif + +/* + * External references to functions in the libsys library function not in current include files. + */ +extern "C" { +long pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow); +} + +/* + * Backup ACL data of AFS + * + * in: + * jcr - Job Control Record + * ff_pkt - file backup record + * out: + * bRC_XACL_inval - input variables are invalid (NULL) + * bRC_XACL_ok - backup finish without problems + * bRC_XACL_error - when you can't backup acl data because some error + */ +bRC_XACL XACL::afs_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + + int rc; + struct ViceIoctl vip; + char data[BUFSIZ]; + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_XACL_inval; + } + + /* AFS ACLs can only be set on a directory, so no need to try other files */ + if (ff_pkt->type != FT_DIREND){ + return bRC_XACL_ok; + } + + vip.in = NULL; + vip.in_size = 0; + vip.out = data; + vip.out_size = BUFSIZE; + memset(data, 0, BUFSIZE); + + if ((rc = pioctl(jcr->last_fname, VIOCGETAL, &vip, 0)) < 0){ + berrno be; + + Mmsg2(jcr->errmsg, _("pioctl VIOCGETAL error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "pioctl VIOCGETAL error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + set_content(data); + return send_acl_stream(jcr, STREAM_XACL_AFS_TEXT); +}; + +/* + * Restore ACL data of AFS + * in: + * jcr - Job Control Record + * stream - a backup stream type number to restore_acl + * out: + * bRC_XACL_inval - input variables are invalid (NULL) + * bRC_XACL_ok - backup finish without problems + * bRC_XACL_error - when you can't backup acl data because some error + */ +bRC_XACL XACL::afs_restore_acl (JCR *jcr, int stream){ + + int rc; + struct ViceIoctl vip; + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_XACL_inval; + } + + vip.in = content; + vip.in_size = content_len; + vip.out = NULL; + vip.out_size = 0; + + if ((rc = pioctl(jcr->last_fname, VIOCSETAL, &vip, 0)) < 0){ + berrno be; + + Mmsg2(jcr->errmsg, _("pioctl VIOCSETAL error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "pioctl VIOCSETAL error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + + return bRC_XACL_error; + } + return bRC_XACL_ok; +}; +#endif /* HAVE_AFS_ACL */ + +#include "xacl_osx.h" +#include "xacl_linux.h" +#include "xacl_freebsd.h" +#include "xacl_solaris.h" +// #include "xacl_aix.h" + +/* + * Creating the corrent instance of the XACL for a supported OS + */ +void *new_xacl(){ +#if defined(HAVE_DARWIN_OS) + return new XACL_OSX(); +#elif defined(HAVE_LINUX_OS) + return new XACL_Linux(); +#elif defined(HAVE_FREEBSD_OS) + return new XACL_FreeBSD(); +#elif defined(HAVE_HURD_OS) + return new XACL_Hurd(); +#elif defined(HAVE_AIX_OS) + return new XACL_AIX(); +#elif defined(HAVE_IRIX_OS) + return new XACL_IRIX(); +#elif defined(HAVE_OSF1_OS) + return new XACL_OSF1(); +#elif defined(HAVE_SUN_OS) + return new XACL_Solaris(); +#endif +}; diff --git a/bacula/src/filed/xacl.h b/bacula/src/filed/xacl.h new file mode 100644 index 0000000000..19df6539d6 --- /dev/null +++ b/bacula/src/filed/xacl.h @@ -0,0 +1,308 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BXACL_H_ +#define __BXACL_H_ + +/* + * Magic used in the magic field of the xattr struct. + * This way we can see if we encounter a valid xattr struct. + * Used for backward compatibility only. + */ +#define XATTR_MAGIC 0x5C5884 + +/* + * Return value status enumeration + * You have an error when value is less then zero. + * You have a positive status when value is not negative + * (greater or equal to zero). + */ +enum bRC_XACL { + bRC_XACL_inval = -3, // input data invalid + bRC_XACL_fatal = -2, // a fatal error + bRC_XACL_error = -1, // standard error + bRC_XACL_ok = 0, // success + bRC_XACL_skip = 1, // processing should skip current runtime + bRC_XACL_cont = 2 // processing should skip current element + // and continue with next one +}; + +/* + * We support the following types of ACLs + */ +typedef enum { + XACL_TYPE_NONE = 0, + XACL_TYPE_ACCESS = 1, + XACL_TYPE_DEFAULT = 2, + XACL_TYPE_DEFAULT_DIR = 3, + XACL_TYPE_EXTENDED = 4, + XACL_TYPE_NFS4 = 5, + XACL_TYPE_PLUGIN = 6 +} XACL_type; + +/* + * Flags which control what ACL/XATTR engine + * to use for backup/restore + */ +#define XACL_FLAG_NONE 0 +#define XACL_FLAG_NATIVE 0x01 +#define XACL_FLAG_AFS 0x02 +#define XACL_FLAG_PLUGIN 0x04 + +/* + * Ensure we have none + */ +#ifndef ACL_TYPE_NONE +#define ACL_TYPE_NONE 0x0 +#endif + +/* + * Extended attribute (xattr) list element. + * + * Every xattr consist of a Key=>Value pair where + * both could be a binary data. + */ +struct XACL_xattr { + uint32_t name_len; + char *name; + uint32_t value_len; + char *value; +}; + +/* + * Basic ACL/XATTR class which is a foundation for any other OS specyfic implementation. + * + * This class cannot be used directly as it is an abstraction class with a lot of virtual + * methods laying around. As a basic class it has all public API available for backup and + * restore functionality. As a bonus it handles all ACL/XATTR generic functions and OS + * independent API, i.e. for AFS ACL/XATTR or Plugins ACL/XATTR (future functionality). + */ +class XACL { +private: + bool acl_ena; + bool xattr_ena; + uint32_t flags; + uint32_t current_dev; + POOLMEM *content; + uint32_t content_len; + uint32_t acl_nr_errors; + uint32_t xattr_nr_errors; + const int *acl_streams; + const int *default_acl_streams; + const int *xattr_streams; + const char **xattr_skiplist; + const char **xattr_acl_skiplist; + + void init(); + + /** + * Perform OS specyfic ACL backup. + * in: + * jcr - Job Control Record + * ff_pkt - file to backup information rector + * out: + * bRC_XACL_ok - backup performed without problems + * any other - some error ocurred + */ + virtual bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){return bRC_XACL_fatal;}; + + /** + * Perform OS specyfic ACL restore. Runtime is called only when stream is supported by OS. + * in: + * jcr - Job Control Record + * ff_pkt - file to backup information rector + * out: + * bRC_XACL_ok - backup performed without problems + * any other - some error ocurred + */ + virtual bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){return bRC_XACL_fatal;}; + + /** + * Perform OS specyfic XATTR backup. + * + * in: + * jcr - Job Control Record + * ff_pkt - file to backup control package + * out: + * bRC_XACL_ok - xattr backup ok or no xattr to backup found + * bRC_XACL_error/fatal - an error or fatal error occurred + */ + virtual bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){return bRC_XACL_fatal;}; + + /** + * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS. + * + * in: + * jcr - Job Control Record + * stream - backup stream number + * content - a buffer with data to restore + * length - a data restore length + * out: + * bRC_XACL_ok - xattr backup ok or no xattr to backup found + * bRC_XACL_error/fatal - an error or fatal error occurred + */ + virtual bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){return bRC_XACL_fatal;}; + + /** + * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer. + * + * in: + * jcr - Job Control Record + * xacltype - the acl type to restore + * out: + * bRC_XACL_ok - + * bRC_XACL_error/fatal - an error or fatal error occurred + */ + virtual bRC_XACL os_get_acl (JCR *jcr, XACL_type xacltype){return bRC_XACL_fatal;}; + + /** + * Low level OS specyfic runtime to set ACL data on file. + * + * in: + * jcr - Job Control Record + * xacltype - the acl type to restore + * content - a buffer with data to restore + * length - a data restore length + * out: + * bRC_XACL_ok - + * bRC_XACL_error/fatal - an error or fatal error occurred + */ + virtual bRC_XACL os_set_acl (JCR *jcr, XACL_type xacltype, char *content, uint32_t length){return bRC_XACL_fatal;}; + + /** + * Returns a list of xattr names in newly allocated pool memory and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. The list of xattr names is returned as an unordered array of NULL terminated + * character strings (attribute names are separated by NULL characters), like this: + * user.name1\0system.name1\0user.name2\0 + * The format of the list is based on standard "llistxattr" function call. + * TODO: change the format of the list from an array of NULL terminated strings into an alist of structures. + * + * in: + * jcr - Job Control Record + * xlen - non NULL pointer to the uint32_t variable for storing a length of the xattr names list + * pxlist - non NULL pointer to the char* variable for allocating a memoty data for xattr names list + * out: + * bRC_XACL_ok - we've got a xattr data to backup + * bRC_XACL_skip - no xattr data available, no fatal error, skip rest of the runtime + * bRC_XACL_fatal - when required buffers are unallocated + * bRC_XACL_error - in case of any error + */ + virtual bRC_XACL os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){return bRC_XACL_fatal;}; + + /** + * Returns a value of the requested attribute name and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in: + * jcr - Job Control Record + * name - a name of the extended attribute + * pvalue - the pointer for the buffer with value - it is allocated by function and should be freed when no needed + * plen - the pointer for the length of the allocated buffer + * + * out: + * pxlist - the atributes list + * bRC_XACL_ok - we've got a xattr data which could be empty when xlen=0 + * bRC_XACL_skip - no xattr data available, no fatal error, skip rest of the runtime + * bRC_XACL_error - error getting an attribute + * bRC_XACL_fatal - required buffers are unallocated + */ + virtual bRC_XACL os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){return bRC_XACL_fatal;}; + + /** + * Low level OS specyfic runtime to set extended attribute on file + * + * in: + * jcr - Job Control Record + * xattr - the struct with attribute/value to set + * + * out: + * bRC_XACL_ok - setting the attribute was ok + * bRC_XACL_error - error during extattribute set + * bRC_XACL_fatal - required buffers are unallocated + */ + virtual bRC_XACL os_set_xattr (JCR *jcr, XACL_xattr *xattr){return bRC_XACL_fatal;}; + + void inc_acl_errors(){ acl_nr_errors++;}; + void inc_xattr_errors(){ xattr_nr_errors++;}; + bRC_XACL check_dev (JCR *jcr); + void check_dev (JCR *jcr, uint32_t dev); + +public: + XACL (); + virtual ~XACL(); + + /* enable/disable functionality */ + void enable_acl(); + void disable_acl(); + void enable_xattr(); + void disable_xattr(); + + /* + * public methods used outside the class or derivatives + */ + bRC_XACL backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL restore_acl (JCR *jcr, int stream, char *content, uint32_t content_length); + bRC_XACL backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL restore_xattr (JCR *jcr, int stream, char *content, uint32_t content_length); + + /* utility functions */ + inline uint32_t get_acl_nr_errors(){ return acl_nr_errors;}; + inline uint32_t get_xattr_nr_errors(){ return xattr_nr_errors;}; + void set_acl_streams (const int *pacl, const int *pacl_def); + void set_xattr_streams (const int *pxattr); + void set_xattr_skiplists (const char **pxattr, const char **pxattr_acl); + inline void clear_flag (uint32_t flag){ flags &= ~flag;}; + inline void set_flag (uint32_t flag){ flags |= flag;}; + POOLMEM * set_content (char *text); + POOLMEM * set_content(char *data, int len); + inline POOLMEM * get_content (void){ return content;}; + inline uint32_t get_content_size (void){ return sizeof_pool_memory(content);}; + inline uint32_t get_content_len (void){ return content_len;}; + bool check_xattr_skiplists (JCR *jcr, FF_PKT *ff_pkt, char * name); + + /* sending data to the storage */ + bRC_XACL send_acl_stream (JCR *jcr, int stream); + bRC_XACL send_xattr_stream (JCR *jcr, int stream); + + /* serialize / unserialize stream */ + bRC_XACL unserialize_xattr_stream(JCR *jcr, char *content, uint32_t length, alist *list); + bRC_XACL serialize_xattr_stream(JCR *jcr, uint32_t len, alist *list); + + /* generic functions */ + bRC_XACL generic_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL generic_restore_acl (JCR *jcr, int stream); + bRC_XACL afs_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL afs_restore_acl (JCR *jcr, int stream); + bRC_XACL generic_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL generic_restore_xattr (JCR *jcr, int stream); +}; + +void *new_xacl(); + +#endif /* __BXACL_H_ */ diff --git a/bacula/src/filed/xacl_freebsd.c b/bacula/src/filed/xacl_freebsd.c new file mode 100644 index 0000000000..59d1554585 --- /dev/null +++ b/bacula/src/filed/xacl_freebsd.c @@ -0,0 +1,946 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#include "bacula.h" +#include "filed.h" +#include "xacl_freebsd.h" + +#if defined(HAVE_FREEBSD_OS) +/* + * Define the supported ACL streams for this OS + */ +static const int os_acl_streams[] = { + STREAM_XACL_FREEBSD_ACCESS, + STREAM_XACL_FREEBSD_NFS4, + 0 +}; + +static const int os_default_acl_streams[] = { + STREAM_XACL_FREEBSD_DEFAULT, + 0 +}; + +/* + * Define the supported XATTR streams for this OS + */ +static const int os_xattr_streams[] = { + STREAM_XACL_FREEBSD_XATTR, + 0 +}; + +static const int os_xattr_namespaces[] = { + EXTATTR_NAMESPACE_USER, + EXTATTR_NAMESPACE_SYSTEM, + -1 +}; + +static const char *os_xattr_acl_skiplist[] = { + "system.posix1e.acl_access", + "system.posix1e.acl_default", + "system.nfs4.acl", + NULL +}; + +static const char *os_xattr_skiplist[] = { + NULL +}; + +/* + * OS Specyfic constructor + */ +XACL_FreeBSD::XACL_FreeBSD(){ + + set_acl_streams(os_acl_streams, os_default_acl_streams); + set_xattr_streams(os_xattr_streams); + set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist); +}; + +/* + * Translates Bacula internal acl representation into + * acl type + * + * in: + * xacltype - internal Bacula acl type (XACL_type) + * out: + * acl_type_t - os dependent acl type + * when failed - ACL_TYPE_NONE is returned + */ +acl_type_t XACL_FreeBSD::get_acltype(XACL_type xacltype){ + + acl_type_t acltype; + + switch (xacltype){ +#ifdef HAVE_ACL_TYPE_NFS4 + case XACL_TYPE_NFS4: + acltype = ACL_TYPE_NFS4; + break; +#endif + case XACL_TYPE_ACCESS: + acltype = ACL_TYPE_ACCESS; + break; + case XACL_TYPE_DEFAULT: + acltype = ACL_TYPE_DEFAULT; + break; + default: + /* + * sanity check for acl's not supported by OS + */ + acltype = (acl_type_t)ACL_TYPE_NONE; + break; + } + return acltype; +}; + +/* + * Counts a number of acl entries + * + * in: + * acl - acl object + * out: + * int - number of entries in acl object + * when no acl entry available or any error then return zero '0' + */ +int XACL_FreeBSD::acl_nrentries(acl_t acl){ + + int nr = 0; + acl_entry_t aclentry; + int rc; + + rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry); + while (rc == 1){ + nr++; + rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry); + } + + return nr; +}; + +/* + * Checks if acl is simple. + * + * acl is simple if it has only the following entries: + * "user::", + * "group::", + * "other::" + * + * in: + * acl - acl object + * out: + * true - when acl object is simple + * false - when acl object is not simple + */ +bool XACL_FreeBSD::acl_issimple(acl_t acl){ + + acl_entry_t aclentry; + acl_tag_t acltag; + int rc; + + rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry); + while (rc == 1){ + if (acl_get_tag_type(aclentry, &acltag) < 0){ + return true; + } + /* + * Check for ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER to find out. + */ + if (acltag != ACL_USER_OBJ && + acltag != ACL_GROUP_OBJ && + acltag != ACL_OTHER){ + return false; + } + rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry); + } + return true; +}; + +/* + * Checks if ACL's are available for a specified file + * + * in: + * jcr - Job Control Record + * name - specifies the system variable to be queried + * out: + * bRC_XACL_ok - check successful, lets setup xacltype variable + * bRC_XACL_error - in case of error + * bRC_XACL_skip - you should skip all other routine + * bRC_XACL_cont - you need to continue your routine + */ +bRC_XACL XACL_FreeBSD::check_xacltype (JCR *jcr, int name){ + + int aclrc = 0; + + aclrc = pathconf(jcr->last_fname, name); + switch (aclrc){ + case -1: { + /* some error check why */ + berrno be; + if (errno == ENOENT){ + /* file does not exist skip it */ + return bRC_XACL_skip; + } else { + Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + case 0: + /* continue the routine */ + return bRC_XACL_cont; + default: + break; + } + return bRC_XACL_ok; +}; + +/* + * Perform OS specyfic ACL backup + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_FreeBSD::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_XACL rc; + XACL_type xacltype = XACL_TYPE_NONE; + +#if defined(_PC_ACL_NFS4) + /* + * Check if filesystem supports NFS4 acls. + */ + rc = check_xacltype(jcr, _PC_ACL_NFS4); + switch (rc){ + case bRC_XACL_ok: + xacltype = XACL_TYPE_NFS4; + break; + case bRC_XACL_skip: + return bRC_XACL_ok; + case bRC_XACL_cont: + break; + default: + /* errors */ + return rc; + } +#endif + if (xacltype == XACL_TYPE_NONE){ + /* + * Check if filesystem supports POSIX acls. + */ + rc = check_xacltype(jcr, _PC_ACL_EXTENDED); + switch (rc){ + case bRC_XACL_ok: + xacltype = XACL_TYPE_ACCESS; + break; + case bRC_XACL_skip: + return bRC_XACL_ok; + case bRC_XACL_cont: + break; + default: + /* errors */ + return rc; + } + } + + /* no ACL's available for file, so skip this filesystem */ + if (xacltype == XACL_TYPE_NONE){ + clear_flag(XACL_FLAG_NATIVE); + /* + * it is a bit of hardcore to clear a poolmemory with a NULL pointer, + * but it is working, hehe :) + * you may ask why it is working? it is simple, a pm_strcpy function is handling + * a null pointer with a substitiution of empty string. + */ + set_content(NULL); + return bRC_XACL_ok; + } + + switch (xacltype){ + case XACL_TYPE_NFS4: + /* + * Read NFS4 ACLs + */ + if (os_get_acl(jcr, XACL_TYPE_NFS4) == bRC_XACL_fatal) + return bRC_XACL_fatal; + + if (get_content_len() > 0){ + if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_NFS4) == bRC_XACL_fatal) + return bRC_XACL_fatal; + } + break; + case XACL_TYPE_ACCESS: + /* + * Read access ACLs + */ + if (os_get_acl(jcr, XACL_TYPE_ACCESS) == bRC_XACL_fatal) + return bRC_XACL_fatal; + + if (get_content_len() > 0){ + if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_ACCESS) == bRC_XACL_fatal) + return bRC_XACL_fatal; + } + + /* + * Directories can have default ACLs too + */ + if (ff_pkt->type == FT_DIREND){ + if (os_get_acl(jcr, XACL_TYPE_DEFAULT) == bRC_XACL_fatal) + return bRC_XACL_fatal; + if (get_content_len() > 0){ + if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_DEFAULT) == bRC_XACL_fatal) + return bRC_XACL_fatal; + } + } + break; + default: + break; + } + + return bRC_XACL_ok; +}; + +/* + * Perform OS specyfic ACL restore + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_FreeBSD::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){ + + int aclrc = 0; + const char *acl_type_name; + + switch (stream){ + case STREAM_UNIX_ACCESS_ACL: + case STREAM_XACL_FREEBSD_ACCESS: + case STREAM_UNIX_DEFAULT_ACL: + case STREAM_XACL_FREEBSD_DEFAULT: + aclrc = pathconf(jcr->last_fname, _PC_ACL_EXTENDED); + acl_type_name = "POSIX"; + break; + case STREAM_XACL_FREEBSD_NFS4: +#if defined(_PC_ACL_NFS4) + aclrc = pathconf(jcr->last_fname, _PC_ACL_NFS4); +#endif + acl_type_name = "NFS4"; + break; + default: + acl_type_name = "unknown"; + break; + } + + switch (aclrc){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + return bRC_XACL_ok; + default: + Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + case 0: + clear_flag(XACL_FLAG_NATIVE); + Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"), jcr->last_fname, acl_type_name); + return bRC_XACL_error; + default: + break; + } + + switch (stream){ + case STREAM_UNIX_ACCESS_ACL: + case STREAM_XACL_FREEBSD_ACCESS: + return os_set_acl(jcr, XACL_TYPE_ACCESS, content, length); + case STREAM_UNIX_DEFAULT_ACL: + case STREAM_XACL_FREEBSD_DEFAULT: + return os_set_acl(jcr, XACL_TYPE_DEFAULT, content, length); + case STREAM_XACL_FREEBSD_NFS4: + return os_set_acl(jcr, XACL_TYPE_NFS4, content, length); + default: + break; + } + return bRC_XACL_error; +}; + +/* + * Perform OS specyfic extended attribute backup + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_FreeBSD::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_XACL rc; + POOLMEM *xlist; + uint32_t xlen; + char *name; + uint32_t name_len; + POOLMEM *value; + uint32_t value_len; + POOLMEM *name_gen; + uint32_t name_gen_len; + char * namespace_str; + int namespace_len; + bool skip; + alist *xattr_list = NULL; + int xattr_count = 0; + uint32_t len = 0; + XACL_xattr *xattr; + int a; + + for (a = 0; os_xattr_namespaces[a] != -1; a++){ // loop through all available namespaces + /* xlist is allocated as POOLMEM by os_get_xattr_names */ + rc = os_get_xattr_names(jcr, os_xattr_namespaces[a], &xlist, &xlen); + switch (rc){ + case bRC_XACL_ok: + /* it's ok, so go further */ + break; + case bRC_XACL_skip: + case bRC_XACL_cont: + /* no xattr available, so skip rest of it */ + return bRC_XACL_ok; + default: + return rc; + } + + /* get a string representation of the namespace */ + if (extattr_namespace_to_string(os_xattr_namespaces[a], &namespace_str) != 0){ + Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"), os_xattr_namespaces[a], jcr->last_fname); + Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n", os_xattr_namespaces[a], jcr->last_fname); + goto bail_out; + } + namespace_len = strlen(namespace_str); + + /* follow the list of xattr names and get the values */ + for (name = xlist; (name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){ + name_len = strlen(name); + name_gen = get_pool_memory(PM_FNAME); + name_gen = check_pool_memory_size(name_gen, name_len + namespace_len + 2); + bsnprintf(name_gen, name_len + namespace_len + 2, "%s.%s", namespace_str, name); + name_gen_len = strlen(name_gen); + + skip = check_xattr_skiplists(jcr, ff_pkt, name_gen); + if (skip || name_len == 0){ + Dmsg1(100, "Skipping xattr named %s\n", name_gen); + continue; + } + + /* value is allocated as POOLMEM by os_get_xattr_value */ + rc = os_get_xattr_value(jcr, os_xattr_namespaces[a], name, &value, &value_len); + switch (rc){ + case bRC_XACL_ok: + /* it's ok, so go further */ + break; + case bRC_XACL_skip: + /* no xattr available, so skip rest of it */ + rc = bRC_XACL_ok; + goto bail_out; + default: + /* error / fatal */ + goto bail_out; + } + + /* + * we have a name of the extended attribute in the name variable + * and value of the extended attribute in the value variable + * so we need to build a list + */ + xattr = (XACL_xattr*)malloc(sizeof(XACL_xattr)); + xattr->name_len = name_gen_len; + xattr->name = name_gen; + xattr->value_len = value_len; + xattr->value = value; + /* magic name_len name value_len value */ + len += sizeof(uint32_t) + sizeof(uint32_t) + name_gen_len + sizeof(uint32_t) + value_len; + + if (xattr_list == NULL){ + xattr_list = New(alist(10, not_owned_by_alist)); + } + xattr_list->append(xattr); + xattr_count++; + } + if (xattr_count > 0){ + /* serialize the stream */ + rc = serialize_xattr_stream(jcr, len, xattr_list); + if (rc != bRC_XACL_ok){ + Mmsg(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), jcr->last_fname); + Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", jcr->last_fname); + goto bail_out; + } else { + /* send data to SD */ + rc = send_xattr_stream(jcr, STREAM_XACL_FREEBSD_XATTR); + } + } else { + rc = bRC_XACL_ok; + } + } +bail_out: + /* free allocated data */ + if (xattr_list != NULL){ + foreach_alist(xattr, xattr_list){ + if (xattr == NULL){ + break; + } + if (xattr->name){ + free_pool_memory(name_gen); + } + if (xattr->value){ + free(xattr->value); + } + free(xattr); + } + delete xattr_list; + } + if (xlist != NULL){ + free(xlist); + } + + return rc; +}; + +/* + * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_FreeBSD::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){ + return generic_restore_xattr(jcr, stream); +}; + +/* + * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_FreeBSD::os_get_acl(JCR *jcr, XACL_type xacltype){ + + acl_t acl; + acl_type_t acltype; + char *acltext; + bRC_XACL rc = bRC_XACL_ok; + + acltype = get_acltype(xacltype); + acl = acl_get_file(jcr->last_fname, acltype); + + if (acl){ + Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname); + if (acl_nrentries(acl) == 0){ + goto bail_out; + } + + /* check for fimple ACL which correspond to standard permissions only */ + if (xacltype == XACL_TYPE_ACCESS && acl_issimple(acl)){ + goto bail_out; + } + +#if defined(_PC_ACL_NFS4) + if (xacltype == XACL_TYPE_NFS4){ + int trivial; + if (acl_is_trivial_np(acl, &trivial) == 0){ + if (trivial == 1){ + goto bail_out; + } + } + } +#endif + + if ((acltext = acl_to_text(acl, NULL)) != NULL){ + set_content(acltext); + acl_free(acl); + acl_free(acltext); + return bRC_XACL_ok; + } + + berrno be; + + Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + + rc = bRC_XACL_error; + + } else { + berrno be; + + switch (errno){ + case EOPNOTSUPP: + /* fs does not support acl, skip it */ + Dmsg0(400, "Wow, ACL is not supported on this filesystem\n"); + clear_flag(XACL_FLAG_NATIVE); + break; + case ENOENT: + break; + default: + /* Some real error */ + Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + break; + } + } + +bail_out: + if (acl){ + acl_free(acl); + } + /* + * it is a bit of hardcore to clear a poolmemory with a NULL pointer, + * but it is working, hehe :) + * you may ask why it is working? it is simple, a pm_strcpy function is handling + * a null pointer with a substitiution of empty string. + */ + set_content(NULL); + return rc; +}; + +/* + * Low level OS specyfic runtime to set ACL data on file + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_FreeBSD::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length){ + + acl_t acl; + acl_type_t acltype; + + acltype = get_acltype(xacltype); + if (acltype == ACL_TYPE_DEFAULT && length == 0){ + /* delete ACl from file when no acl data available for default acl's */ + if (acl_delete_def_file(jcr->last_fname) == 0){ + return bRC_XACL_ok; + } + + berrno be; + switch (errno){ + case ENOENT: + return bRC_XACL_ok; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support acl's we clear the + * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(XACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname); + return bRC_XACL_error; + default: + Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + + acl = acl_from_text(content); + if (acl == NULL){ + berrno be; + + Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + + /* + * Restore the ACLs, but don't complain about links which really should + * not have attributes, and the file it is linked to may not yet be restored. + * This is only true for the old acl streams as in the new implementation we + * don't save acls of symlinks (which cannot have acls anyhow) + */ + if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){ + berrno be; + switch (errno){ + case ENOENT: + acl_free(acl); + return bRC_XACL_ok; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support ACLs we clear the + * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(XACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname); + Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname); + acl_free(acl); + return bRC_XACL_error; + default: + Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + acl_free(acl); + return bRC_XACL_error; + } + } + acl_free(acl); + return bRC_XACL_ok; +}; + +/* + * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in/out - check API at xacl.h + * + * As a FreeBSD uses a different attributes name schema/format then this method is a very different + * from a standard generic method because it uses a namespace (ns) value for os dependent optimization. + */ +bRC_XACL XACL_FreeBSD::os_get_xattr_names (JCR *jcr, int ns, POOLMEM ** pxlist, uint32_t * xlen){ + + int len; + POOLMEM * list; + int a; + int stra; + POOLMEM * genlist; + + /* check input data */ + if (jcr == NULL || xlen == NULL || pxlist == NULL){ + return bRC_XACL_inval; + } + /* get the length of the extended attributes */ + len = extattr_list_link(jcr->last_fname, ns, NULL, 0); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + case EOPNOTSUPP: + /* no xattr supported on filesystem, clear a flag and skip it */ + clear_flag(XACL_FLAG_NATIVE); + set_content(NULL); + return bRC_XACL_skip; + case EPERM: + if (ns == EXTATTR_NAMESPACE_SYSTEM){ + return bRC_XACL_cont; + } /* else show error */ + default: + Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + break; + } + case 0: + /* xattr available but empty, skip it */ + return bRC_XACL_skip; + default: + break; + } + + /* + * allocate memory for the extented attribute list + * default size is a 4k for PM_BSOCK, which should be sufficient on almost all + * Linux system where xattrs a limited in size to single filesystem block ~4kB + * so we need to check required size + */ + list = get_pool_memory(PM_BSOCK); + list = check_pool_memory_size(list, len + 1); + memset(list, 0, len + 1); + + /* get the list of extended attributes names for a file */ + len = extattr_list_link(jcr->last_fname, ns, list, len); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it, first release allocated memory */ + free_pool_memory(list); + return bRC_XACL_skip; + case EPERM: + if (ns == EXTATTR_NAMESPACE_SYSTEM){ + return bRC_XACL_cont; + } /* else show error */ + default: + Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + free_pool_memory(list); + return bRC_XACL_error; + } + break; + } + default: + break; + } + /* convert FreeBSD list type to the generic one */ + genlist = get_pool_memory(PM_BSOCK); + genlist = check_pool_memory_size(genlist, len + 1); + memset(genlist, 0, len + 1); + for (a = 0; a < len; a += list[a] + 1){ + stra = list[a]; + memcpy(genlist + a, list + a + 1, stra); + genlist[a + stra] = '\0'; + } + free_pool_memory(list); + /* setup return data */ + *pxlist = genlist; + *xlen = len; + return bRC_XACL_ok; +}; + +/* + * Return a value of the requested attribute name and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in/out - check API at xacl.h + * + * As a FreeBSD uses a different attributes name schema/format then this method is a very different + * from a standard generic method because it uses a namespace (ns) value for os dependent optimization. + */ +bRC_XACL XACL_FreeBSD::os_get_xattr_value (JCR *jcr, int ns, char * name, char ** pvalue, uint32_t * plen){ + + int len; + POOLMEM * value; + + /* check input data */ + if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){ + return bRC_XACL_inval; + } + /* get the length of the value for extended attribute */ + len = extattr_get_link(jcr->last_fname, ns, name, NULL, 0); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + default: + /* XXX: what about ENOATTR error value? */ + Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + break; + } + default: + break; + } + + if (len > 0){ + /* + * allocate memory for the extented attribute value + * default size is a 256B for PM_MESSAGE, so we need to check required size + */ + value = get_pool_memory(PM_MESSAGE); + value = check_pool_memory_size(value, len + 1); + memset(value, 0, len + 1); + /* value is not empty, get a data */ + len = extattr_get_link(jcr->last_fname, ns, name, value, len); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it, first release allocated memory */ + free_pool_memory(value); + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + free_pool_memory(value); + return bRC_XACL_error; + } + break; + } + default: + break; + } + /* ensure a value is nul terminated */ + value[len] = '\0'; + } else { + /* empty value */ + value = NULL; + len = 0; + } + /* setup return data */ + *pvalue = value; + *plen = len; + return bRC_XACL_ok; +}; + +/* + * Low level OS specyfic runtime to set extended attribute on file + * + * in/out - check API at xacl.h + * + * xattr->name should be in '.' format which + * function handle without problem, otherwise it returns an error + * TODO: it is possible to handle a different attributes name format + * for os portability where default namespace 'user' can be used + */ +bRC_XACL XACL_FreeBSD::os_set_xattr (JCR *jcr, XACL_xattr *xattr){ + + char * name; + char * nspace; + int ns; + int rc; + + /* check input data */ + if (jcr == NULL || xattr == NULL){ + return bRC_XACL_inval; + } + + /* search for attribute namespace which is distinguished from attribute name by a dot '.' character */ + if ((name = strchr(xattr->name, '.')) == (char *)NULL){ + Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"), xattr->name, jcr->last_fname); + Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n", xattr->name, jcr->last_fname); + return bRC_XACL_error; + } + + /* split namespace and name of the attribute */ + nspace = xattr->name; + *name++ = '\0'; + + /* check if namespace is valid on this system */ + if (extattr_string_to_namespace(nspace, &ns) != 0){ + Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"), nspace, jcr->last_fname); + Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n", nspace, jcr->last_fname); + return bRC_XACL_error; + } + + /* set extattr on file */ + rc = extattr_set_link(jcr->last_fname, ns, name, xattr->value, xattr->value_len); + if (rc < 0 || rc != (int)xattr->value_len){ + berrno be; + + switch (errno){ + case ENOENT: + break; + default: + Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + return bRC_XACL_ok; +}; + +#endif /* HAVE_FREEBSD_OS */ diff --git a/bacula/src/filed/xacl_freebsd.h b/bacula/src/filed/xacl_freebsd.h new file mode 100644 index 0000000000..b3e1d653be --- /dev/null +++ b/bacula/src/filed/xacl_freebsd.h @@ -0,0 +1,95 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __XACL_FreeBSD_H_ +#define __XACL_FreeBSD_H_ + +#if defined(HAVE_FREEBSD_OS) +#include + +#ifdef HAVE_SYS_ACL_H +#include +#else +#error "configure failed to detect availability of sys/acl.h" +#endif + +#if (!defined(HAVE_EXTATTR_GET_LINK) && !defined(HAVE_EXTATTR_GET_FILE)) || \ + (!defined(HAVE_EXTATTR_SET_LINK) && !defined(HAVE_EXTATTR_SET_FILE)) || \ + (!defined(HAVE_EXTATTR_LIST_LINK) && !defined(HAVE_EXTATTR_LIST_FILE)) || \ + !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \ + !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE) +#error "Missing full support for the extattr functions." +#endif + +#ifdef HAVE_SYS_EXTATTR_H +#include +#include +#else +#error "Missing sys/extattr.h header file" +#endif + +#ifdef HAVE_LIBUTIL_H +#include +#endif + +#if !defined(HAVE_EXTATTR_GET_LINK) && defined(HAVE_EXTATTR_GET_FILE) +#define extattr_get_link extattr_get_file +#endif +#if !defined(HAVE_EXTATTR_SET_LINK) && defined(HAVE_EXTATTR_SET_FILE) +#define extattr_set_link extattr_set_file +#endif +#if !defined(HAVE_EXTATTR_LIST_LINK) && defined(HAVE_EXTATTR_LIST_FILE) +#define extattr_list_link extattr_list_file +#endif + +/* + * + * + */ +class XACL_FreeBSD : public XACL { +private: + bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_get_acl(JCR *jcr, XACL_type xacltype); + bRC_XACL os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length); + bRC_XACL os_get_xattr_names (JCR *jcr, const int ns, POOLMEM **list, uint32_t *length); + bRC_XACL os_get_xattr_value (JCR *jcr, const int ns, char * name, char ** pvalue, uint32_t * plen); + bRC_XACL os_set_xattr (JCR *jcr, XACL_xattr *xattr); + /* requires acl.h available */ + bool acl_issimple(acl_t acl); + acl_type_t get_acltype(XACL_type xacltype); + int acl_nrentries(acl_t acl); + bRC_XACL check_xacltype (JCR *jcr, int name); +public: + XACL_FreeBSD (); +}; + +#endif /* HAVE_FREEBSD_OS */ + +#endif /* __XACL_FreeBSD_H_ */ diff --git a/bacula/src/filed/xacl_linux.c b/bacula/src/filed/xacl_linux.c new file mode 100644 index 0000000000..409d6b6cc7 --- /dev/null +++ b/bacula/src/filed/xacl_linux.c @@ -0,0 +1,583 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#include "bacula.h" +#include "filed.h" +#include "xacl_linux.h" + +#if defined(HAVE_LINUX_OS) +/* + * Define the supported ACL streams for this OS + */ +static const int os_acl_streams[] = { + STREAM_XACL_LINUX_ACCESS, + 0 +}; + +static const int os_default_acl_streams[] = { + STREAM_XACL_LINUX_DEFAULT, + 0 +}; + +/* + * Define the supported XATTR streams for this OS + */ +static const int os_xattr_streams[] = { + STREAM_XACL_LINUX_XATTR, + 0 +}; + +static const char *os_xattr_acl_skiplist[] = { + "system.posix_acl_access", + "system.posix_acl_default", + NULL +}; + +static const char *os_xattr_skiplist[] = { + NULL +}; + +/* + * OS Specyfic constructor + */ +XACL_Linux::XACL_Linux(){ + set_acl_streams(os_acl_streams, os_default_acl_streams); + set_xattr_streams(os_xattr_streams); + set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist); +}; + +/* + * Translates Bacula internal acl representation into + * acl type + * + * in: + * xacltype - internal Bacula acl type (XACL_type) + * out: + * acl_type_t - os dependent acl type + * when failed - ACL_TYPE_NONE is returned + */ +acl_type_t XACL_Linux::get_acltype(XACL_type xacltype){ + + acl_type_t acltype; + + switch (xacltype){ + case XACL_TYPE_ACCESS: + acltype = ACL_TYPE_ACCESS; + break; + case XACL_TYPE_DEFAULT: + acltype = ACL_TYPE_DEFAULT; + break; + default: + /* + * sanity check for acl's not supported by OS + */ + acltype = (acl_type_t)ACL_TYPE_NONE; + break; + } + return acltype; +}; + +/* + * Counts a number of acl entries + * + * in: + * acl - acl object + * out: + * int - number of entries in acl object + * when no acl entry available or any error then return zero '0' + */ +int XACL_Linux::acl_nrentries(acl_t acl){ + + int nr = 0; + acl_entry_t aclentry; + int rc; + + rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry); + while (rc == 1){ + nr++; + rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry); + } + + return nr; +}; + +/* + * Checks if acl is simple. + * + * acl is simple if it has only the following entries: + * "user::", + * "group::", + * "other::" + * + * in: + * acl - acl object + * out: + * true - when acl object is simple + * false - when acl object is not simple + */ +bool XACL_Linux::acl_issimple(acl_t acl){ + + acl_entry_t aclentry; + acl_tag_t acltag; + int rc; + + rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry); + while (rc == 1){ + if (acl_get_tag_type(aclentry, &acltag) < 0){ + return true; + } + /* + * Check for ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER to find out. + */ + if (acltag != ACL_USER_OBJ && + acltag != ACL_GROUP_OBJ && + acltag != ACL_OTHER){ + return false; + } + rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry); + } + return true; +}; + +/* + * Perform OS specyfic ACL backup + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + return generic_backup_acl(jcr, ff_pkt); +}; + +/* + * Perform OS specyfic ACL restore + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){ + return generic_restore_acl(jcr, stream); +}; + +/* + * Perform OS specyfic extended attribute backup + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + return generic_backup_xattr(jcr, ff_pkt); +}; + +/* + * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){ + return generic_restore_xattr(jcr, stream); +}; + +/* + * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_get_acl(JCR *jcr, XACL_type xacltype){ + + acl_t acl; + acl_type_t acltype; + char *acltext; + bRC_XACL rc = bRC_XACL_ok; + + /* check input data */ + if (jcr == NULL){ + return bRC_XACL_inval; + } + + acltype = get_acltype(xacltype); + acl = acl_get_file(jcr->last_fname, acltype); + + if (acl){ + Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname); + if (acl_nrentries(acl) == 0){ + goto bail_out; + } + + /* check for fimple ACL which correspond to standard permissions only */ + if (xacltype == XACL_TYPE_ACCESS && acl_issimple(acl)){ + goto bail_out; + } + + if ((acltext = acl_to_text(acl, NULL)) != NULL){ + set_content(acltext); + acl_free(acl); + acl_free(acltext); + return bRC_XACL_ok; + } + + berrno be; + + Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + + rc = bRC_XACL_error; + } else { + berrno be; + + switch (errno){ + case EOPNOTSUPP: + /* fs does not support acl, skip it */ + Dmsg0(400, "Wow, ACL is not supported on this filesystem\n"); + clear_flag(XACL_FLAG_NATIVE); + break; + case ENOENT: + break; + default: + /* Some real error */ + Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + break; + } + } + +bail_out: + if (acl){ + acl_free(acl); + } + /* + * it is a bit of hardcore to clear a poolmemory with a NULL pointer, + * but it is working, hehe :) + * you may ask why it is working? it is simple, a pm_strcpy function is handling + * a null pointer with a substitiution of empty string. + */ + set_content(NULL); + return rc; +}; + +/* + * Low level OS specyfic runtime to set ACL data on file + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length){ + + acl_t acl; + acl_type_t acltype; + + /* check input data */ + if (jcr == NULL || content == NULL){ + return bRC_XACL_inval; + } + + acl = acl_from_text(content); + if (acl == NULL){ + berrno be; + + Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + + if (acl_valid(acl) != 0){ + berrno be; + + Mmsg2(jcr->errmsg, _("acl_valid error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + acl_free(acl); + return bRC_XACL_error; + } + + /* handle different acl types for Linux */ + acltype = get_acltype(xacltype); + if (acltype == ACL_TYPE_DEFAULT && length == 0){ + /* delete ACl from file when no acl data available for default acl's */ + if (acl_delete_def_file(jcr->last_fname) == 0){ + return bRC_XACL_ok; + } + + berrno be; + switch (errno){ + case ENOENT: + return bRC_XACL_ok; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support acl's we clear the + * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(XACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname); + return bRC_XACL_error; + default: + Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + + /* + * Restore the ACLs, but don't complain about links which really should + * not have attributes, and the file it is linked to may not yet be restored. + * This is only true for the old acl streams as in the new implementation we + * don't save acls of symlinks (which cannot have acls anyhow) + */ + if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){ + berrno be; + switch (errno){ + case ENOENT: + acl_free(acl); + return bRC_XACL_ok; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support ACLs we clear the + * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(XACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname); + Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname); + acl_free(acl); + return bRC_XACL_error; + default: + Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + acl_free(acl); + return bRC_XACL_error; + } + } + acl_free(acl); + return bRC_XACL_ok; +}; + +/* + * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){ + + int len; + POOLMEM * list; + + /* check input data */ + if (jcr == NULL || xlen == NULL || pxlist == NULL){ + return bRC_XACL_inval; + } + + /* get the length of the extended attributes */ + len = llistxattr(jcr->last_fname, NULL, 0); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + case EOPNOTSUPP: + /* no xattr supported on filesystem, clear a flag and skip it */ + clear_flag(XACL_FLAG_NATIVE); + set_content(NULL); + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + break; + } + case 0: + /* xattr available but empty, skip it */ + return bRC_XACL_skip; + default: + break; + } + + /* + * allocate memory for the extented attribute list + * default size is a 4k for PM_BSOCK, which should be sufficient on almost all + * Linux system where xattrs a limited in size to single filesystem block ~4kB + * so we need to check required size + */ + list = get_pool_memory(PM_BSOCK); + list = check_pool_memory_size(list, len + 1); + memset(list, 0, len + 1); + + /* get the list of extended attributes names for a file */ + len = llistxattr(jcr->last_fname, list, len); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it, first release allocated memory */ + free_pool_memory(list); + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + free_pool_memory(list); + return bRC_XACL_error; + } + break; + } + default: + break; + } + /* ensure a list is nul terminated */ + list[len] = '\0'; + /* setup return data */ + *pxlist = list; + *xlen = len; + return bRC_XACL_ok; +}; + +/* + * Return a value of the requested attribute name and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){ + + int len; + POOLMEM * value; + + /* check input data */ + if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){ + return bRC_XACL_inval; + } + + /* get the length of the value for extended attribute */ + len = lgetxattr(jcr->last_fname, name, NULL, 0); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + default: + /* XXX: what about ENOATTR error value? */ + Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + break; + } + default: + break; + } + + if (len > 0){ + /* + * allocate memory for the extented attribute value + * default size is a 256B for PM_MESSAGE, so we need to check required size + */ + value = get_pool_memory(PM_MESSAGE); + value = check_pool_memory_size(value, len + 1); + memset(value, 0, len + 1); + /* value is not empty, get a data */ + len = lgetxattr(jcr->last_fname, name, value, len); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it, first release allocated memory */ + free_pool_memory(value); + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + free_pool_memory(value); + return bRC_XACL_error; + } + break; + } + default: + break; + } + /* ensure a value is nul terminated */ + value[len] = '\0'; + } else { + /* empty value */ + value = NULL; + len = 0; + } + /* setup return data */ + *pvalue = value; + *plen = len; + return bRC_XACL_ok; +}; + +/* + * Low level OS specyfic runtime to set extended attribute on file + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Linux::os_set_xattr (JCR *jcr, XACL_xattr *xattr){ + + /* check input data */ + if (jcr == NULL || xattr == NULL){ + return bRC_XACL_inval; + } + + /* set extattr on file */ + if (lsetxattr(jcr->last_fname, xattr->name, xattr->value, xattr->value_len, 0) != 0){ + berrno be; + + switch (errno){ + case ENOENT: + break; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support XATTR we clear the + * XACL_FLAG_NATIVE flag so we skip XATTR restores on all other files + * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(XACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("setxattr error on file \"%s\": filesystem doesn't support XATTR\n"), jcr->last_fname); + Dmsg3(100, "setxattr error name=%s value=%s file=%s filesystem doesn't support XATTR\n", xattr->name, xattr->value, jcr->last_fname); + break; + default: + Mmsg2(jcr->errmsg, _("setxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "setxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + return bRC_XACL_ok; +}; + +#endif /* HAVE_LINUX_OS */ diff --git a/bacula/src/filed/xacl_linux.h b/bacula/src/filed/xacl_linux.h new file mode 100644 index 0000000000..4f835ca873 --- /dev/null +++ b/bacula/src/filed/xacl_linux.h @@ -0,0 +1,75 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __XACL_LINUX_H_ +#define __XACL_LINUX_H_ + +#if defined(HAVE_LINUX_OS) +#include + +#ifdef HAVE_SYS_ACL_H +#include +#else +#error "configure failed to detect availability of sys/acl.h" +#endif + +#if !defined(HAVE_LLISTXATTR) || !defined(HAVE_LGETXATTR) || !defined(HAVE_LSETXATTR) +#error "Missing full support for the XATTR functions." +#endif + +#ifdef HAVE_SYS_XATTR_H +#include +#else +#error "Missing sys/xattr.h header file" +#endif + +/* + * + * + */ +class XACL_Linux : public XACL { +private: + bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_get_acl(JCR *jcr, XACL_type xacltype); + bRC_XACL os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length); + bRC_XACL os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length); + bRC_XACL os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen); + bRC_XACL os_set_xattr (JCR *jcr, FF_PKT *ff_pkt, char *list, uint32_t length); + bRC_XACL os_set_xattr (JCR *jcr, XACL_xattr *xattr); + acl_type_t get_acltype(XACL_type xacltype); + int acl_nrentries(acl_t acl); + bool acl_issimple(acl_t acl); +public: + XACL_Linux (); +}; + +#endif /* HAVE_LINUX_OS */ + +#endif /* __XACL_LINUX_H_ */ diff --git a/bacula/src/filed/xacl_osx.c b/bacula/src/filed/xacl_osx.c new file mode 100644 index 0000000000..ef2fc9a1f5 --- /dev/null +++ b/bacula/src/filed/xacl_osx.c @@ -0,0 +1,533 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#include "bacula.h" +#include "filed.h" +#include "xacl_osx.h" + +#if defined(HAVE_DARWIN_OS) +/* + * Define the supported ACL streams for this OS + */ +static const int os_acl_streams[] = { + STREAM_XACL_DARWIN_ACCESS, + 0 +}; + +static const int os_default_acl_streams[] = { + 0 +}; + +/* + * Define the supported XATTR streams for this OS + */ +static const int os_xattr_streams[] = { + STREAM_XACL_DARWIN_XATTR, + 0 +}; + +static const char *os_xattr_acl_skiplist[] = { + "com.apple.system.Security", + NULL +}; + +static const char *os_xattr_skiplist[] = { + "com.apple.system.extendedsecurity", + "com.apple.ResourceFork", + NULL +}; + +/* + * OS Specyfic constructor + */ +XACL_OSX::XACL_OSX(){ + + set_acl_streams(os_acl_streams, os_default_acl_streams); + set_xattr_streams(os_xattr_streams); + set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist); +}; + +/* + * Translates Bacula internal acl representation into + * acl type + * + * in: + * xacltype - internal Bacula acl type (XACL_type) + * out: + * acl_type_t - os dependent acl type + * when failed - ACL_TYPE_NONE is returned + */ +acl_type_t XACL_OSX::get_acltype(XACL_type xacltype){ + + acl_type_t acltype; + + switch (xacltype){ + case XACL_TYPE_ACCESS: + acltype = ACL_TYPE_ACCESS; + break; + #ifdef HAVE_ACL_TYPE_EXTENDED + case XACL_TYPE_EXTENDED: + acltype = ACL_TYPE_EXTENDED; + break; + #endif + default: + /* + * sanity check for acl's not supported by OS + */ + acltype = (acl_type_t)ACL_TYPE_NONE; + break; + } + return acltype; +}; + +/* + * Counts a number of acl entries + * + * in: + * acl - acl object + * out: + * int - number of entries in acl object + * when no acl entry available or any error then return zero '0' + */ +int XACL_OSX::acl_nrentries(acl_t acl){ + + int nr = 0; + acl_entry_t entry; + int rc; + + rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); + while (rc == 0){ + nr++; + rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); + } + + return nr; +}; + +/* + * Perform OS specyfic ACL backup + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + + /* check input data */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_XACL_inval; + } + +#if defined(HAVE_ACL_TYPE_EXTENDED) + /* + * Use XACL_TYPE_EXTENDED only when available + */ + Dmsg0(400, "MacOSX Extended ACL computed\n"); + if (os_get_acl(jcr, XACL_TYPE_EXTENDED) == bRC_XACL_fatal){ + return bRC_XACL_fatal; + } +#else + Dmsg0(400, "MacOSX standard ACL computed\n"); + if (os_get_acl(jcr, XACL_TYPE_ACCESS) == bRC_XACL_fatal){ + return bRC_XACL_fatal; + } +#endif + + return send_acl_stream(jcr, STREAM_XACL_DARWIN_ACCESS); +}; + +/* + * Perform OS specyfic ACL restore + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){ + +#if defined(HAVE_ACL_TYPE_EXTENDED) + return os_set_acl(jcr, XACL_TYPE_EXTENDED, content, length); +#else + return os_set_acl(jcr, XACL_TYPE_ACCESS, content, length); +#endif +}; + +/* + * Perform OS specyfic extended attribute backup + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + return generic_backup_xattr(jcr, ff_pkt); +}; + +/* + * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){ + return generic_restore_xattr(jcr, stream); +}; + +/* + * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_get_acl(JCR *jcr, XACL_type xacltype){ + + acl_t acl; + acl_type_t acltype; + char *acltext; + bRC_XACL rc = bRC_XACL_ok; + + /* check input data */ + if (jcr == NULL){ + return bRC_XACL_inval; + } + + acltype = get_acltype(xacltype); + acl = acl_get_file(jcr->last_fname, acltype); + + if (acl){ + Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname); + if (acl_nrentries(acl) == 0){ + goto bail_out; + } + + if ((acltext = acl_to_text(acl, NULL)) != NULL){ + set_content(acltext); + acl_free(acl); + acl_free(acltext); + return bRC_XACL_ok; + } + + berrno be; + + Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + + rc = bRC_XACL_error; + } else { + berrno be; + + switch (errno){ + case EOPNOTSUPP: + /* fs does not support acl, skip it */ + Dmsg0(400, "Wow, ACL is not supported on this filesystem\n"); + clear_flag(XACL_FLAG_NATIVE); + break; + case ENOENT: + break; + default: + /* Some real error */ + Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + break; + } + } + +bail_out: + if (acl){ + acl_free(acl); + } + /* + * it is a bit of hardcore to clear a poolmemory with a NULL pointer, + * but it is working, hehe :) + * you may ask why it is working? it is simple, a pm_strcpy function is handling + * a null pointer with a substitiution of empty string. + */ + set_content(NULL); + return rc; +}; + +/* + * Low level OS specyfic runtime to set ACL data on file + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length){ + + acl_t acl; + acl_type_t acltype; + + /* check input data */ + if (jcr == NULL || content == NULL){ + return bRC_XACL_inval; + } + + acl = acl_from_text(content); + if (acl == NULL){ + berrno be; + + Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + + acltype = get_acltype(xacltype); + + /* + * Restore the ACLs, but don't complain about links which really should + * not have attributes, and the file it is linked to may not yet be restored. + * This is only true for the old acl streams as in the new implementation we + * don't save acls of symlinks (which cannot have acls anyhow) + */ + if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){ + berrno be; + switch (errno){ + case ENOENT: + acl_free(acl); + return bRC_XACL_ok; + case EOPNOTSUPP: + /* + * If the filesystem reports it doesn't support ACLs we clear the + * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(XACL_FLAG_NATIVE); + Mmsg1(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname); + Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname); + acl_free(acl); + return bRC_XACL_error; + default: + Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + acl_free(acl); + return bRC_XACL_error; + } + } + acl_free(acl); + return bRC_XACL_ok; +}; + +/* + * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){ + + int len; + POOLMEM * list; + + /* check input data */ + if (jcr == NULL || xlen == NULL || pxlist == NULL){ + return bRC_XACL_inval; + } + /* get the length of the extended attributes */ + len = listxattr(jcr->last_fname, NULL, 0, XATTR_NOFOLLOW); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + case ENOTSUP: + /* no xattr supported on filesystem, clear a flag and skip it */ + clear_flag(XACL_FLAG_NATIVE); + set_content(NULL); + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + break; + } + case 0: + /* xattr available but empty, skip it */ + return bRC_XACL_skip; + default: + break; + } + + /* + * allocate memory for the extented attribute list + * default size is a 4k for PM_BSOCK, which should be sufficient on almost all + * Linux system where xattrs a limited in size to single filesystem block ~4kB + * so we need to check required size + */ + list = get_pool_memory(PM_BSOCK); + list = check_pool_memory_size(list, len + 1); + memset(list, 0, len + 1); + + /* get the list of extended attributes names for a file */ + len = listxattr(jcr->last_fname, list, len, XATTR_NOFOLLOW); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it, first release allocated memory */ + free_pool_memory(list); + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + free_pool_memory(list); + return bRC_XACL_error; + } + break; + } + default: + break; + } + /* ensure a list is nul terminated */ + list[len] = '\0'; + /* setup return data */ + *pxlist = list; + *xlen = len; + return bRC_XACL_ok; +}; + +/* + * Return a value of the requested attribute name and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){ + + int len; + POOLMEM * value; + + /* check input data */ + if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){ + return bRC_XACL_inval; + } + + /* get the length of the value for extended attribute */ + len = getxattr(jcr->last_fname, name, NULL, 0, 0, XATTR_NOFOLLOW); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + default: + /* XXX: what about ENOATTR error value? */ + Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + break; + } + default: + break; + } + + if (len > 0){ + /* + * allocate memory for the extented attribute value + * default size is a 256B for PM_MESSAGE, so we need to check required size + */ + value = get_pool_memory(PM_MESSAGE); + value = check_pool_memory_size(value, len + 1); + memset(value, 0, len + 1); + /* value is not empty, get a data */ + len = getxattr(jcr->last_fname, name, value, len, 0, XATTR_NOFOLLOW); + switch (len){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it, first release allocated memory */ + free_pool_memory(value); + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + free_pool_memory(value); + return bRC_XACL_error; + } + break; + } + default: + break; + } + /* ensure a value is nul terminated */ + value[len] = '\0'; + } else { + /* empty value */ + value = NULL; + len = 0; + } + /* setup return data */ + *pvalue = value; + *plen = len; + return bRC_XACL_ok; +}; + +/* + * Low level OS specyfic runtime to set extended attribute on file + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_OSX::os_set_xattr (JCR *jcr, XACL_xattr *xattr){ + + /* check input data */ + if (jcr == NULL || xattr == NULL){ + return bRC_XACL_inval; + } + + /* set extattr on file */ + if (setxattr(jcr->last_fname, xattr->name, xattr->value, xattr->value_len, 0, XATTR_NOFOLLOW) != 0){ + berrno be; + + switch (errno){ + case ENOENT: + break; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support XATTR we clear the + * XACL_FLAG_NATIVE flag so we skip XATTR restores on all other files + * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(XACL_FLAG_NATIVE); + Mmsg1(jcr->errmsg, _("setxattr error on file \"%s\": filesystem doesn't support XATTR\n"), jcr->last_fname); + Dmsg3(100, "setxattr error name=%s value=%s file=%s filesystem doesn't support XATTR\n", xattr->name, xattr->value, jcr->last_fname); + break; + default: + Mmsg2(jcr->errmsg, _("setxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "setxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + return bRC_XACL_ok; +}; + +#endif /* HAVE_DARWIN_OS */ diff --git a/bacula/src/filed/xacl_osx.h b/bacula/src/filed/xacl_osx.h new file mode 100644 index 0000000000..c8d4b2a875 --- /dev/null +++ b/bacula/src/filed/xacl_osx.h @@ -0,0 +1,74 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __XACL_OSX_H_ +#define __XACL_OSX_H_ + +#if defined(HAVE_DARWIN_OS) +#include + +#ifdef HAVE_SYS_ACL_H +#include +#else +#error "configure failed to detect availability of sys/acl.h" +#endif + +#if !defined(HAVE_LISTXATTR) || !defined(HAVE_GETXATTR) || !defined(HAVE_SETXATTR) +#error "Missing full support for the XATTR functions." +#endif + +#ifdef HAVE_SYS_XATTR_H +#include +#else +#error "Missing sys/xattr.h header file" +#endif + +/* + * + * + */ +class XACL_OSX : public XACL { +private: + bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_get_acl(JCR *jcr, XACL_type xacltype); + bRC_XACL os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length); + bRC_XACL os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length); + bRC_XACL os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen); + bRC_XACL os_set_xattr (JCR *jcr, XACL_xattr *xattr); + /* requires acl.h available */ + acl_type_t get_acltype(XACL_type xacltype); + int acl_nrentries(acl_t acl); +public: + XACL_OSX (); +}; + +#endif /* HAVE_DARWIN_OS */ + +#endif /* __XACL_OSX_H_ */ diff --git a/bacula/src/filed/xacl_solaris.c b/bacula/src/filed/xacl_solaris.c new file mode 100644 index 0000000000..c1701d32a0 --- /dev/null +++ b/bacula/src/filed/xacl_solaris.c @@ -0,0 +1,1177 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#include "bacula.h" +#include "filed.h" +#include "xacl_solaris.h" + +#if defined(HAVE_SUN_OS) +/* + * Define the supported ACL streams for this OS + */ +static const int os_acl_streams[] = { + STREAM_XACL_SOLARIS_POSIX, + STREAM_XACL_SOLARIS_NFS4, + 0 +}; + +static const int os_default_acl_streams[] = { + 0 +}; + +/* + * Define the supported XATTR streams for this OS + */ +static const int os_xattr_streams[] = { + STREAM_XACL_SOLARIS_XATTR, +#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) + STREAM_XACL_SOLARIS_SYS_XATTR, +#endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */ + 0 +}; + + +static const char *os_xattr_acl_skiplist[] = { + NULL +}; + +static const char *os_xattr_skiplist[] = { + "..", +#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) + VIEW_READONLY, +#endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */ + NULL +}; + +/* + * OS Specyfic constructor + */ +XACL_Solaris::XACL_Solaris(){ + + set_acl_streams(os_acl_streams, os_default_acl_streams); + set_xattr_streams(os_xattr_streams); + set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist); + cache = NULL; +}; + +/* + * OS Specyfic destructor + */ +XACL_Solaris::~XACL_Solaris(){ + + delete_xattr_cache(); +}; + +/* + * Checks if ACL's are available for a specified file + * + * in: + * jcr - Job Control Record + * name - specifies the system variable to be queried + * out: + * bRC_XACL_ok - check successful, lets setup xacltype variable + * bRC_XACL_error - in case of error + * bRC_XACL_skip - you should skip all other routine + */ +bRC_XACL XACL_Solaris::check_xacltype (JCR *jcr, int name){ + + int rc = 0; + + rc = pathconf(jcr->last_fname, name); + switch (rc){ + case -1: { + /* some error check why */ + berrno be; + if (errno == ENOENT){ + /* file does not exist skip it */ + return bRC_XACL_skip; + } else { + Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + case 0: + /* No support for ACLs */ + clear_flag(XACL_FLAG_NATIVE); + set_content(NULL); + return bRC_XACL_skip; + default: + break; + } + return bRC_XACL_ok; +}; + +/* + * Perform OS specyfic ACL backup + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Solaris::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_XACL rc; + int stream; + + /* + * See if filesystem supports acls. + */ + rc = check_xacltype(jcr, _PC_ACL_ENABLED); + switch (rc){ + case bRC_XACL_ok: + break; + case bRC_XACL_skip: + return bRC_XACL_ok; + default: + /* errors */ + return rc; + } + + rc = os_get_acl(jcr, &stream); + switch (rc){ + case bRC_XACL_ok: + if (get_content_len() > 0){ + if (send_acl_stream(jcr, stream) == bRC_XACL_fatal){ + return bRC_XACL_fatal; + } + } + break; + default: + return rc; + } + + return bRC_XACL_ok; +}; + +/* + * Perform OS specyfic ACL restore + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Solaris::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){ + + int aclrc = 0; + + switch (stream){ + case STREAM_UNIX_ACCESS_ACL: + case STREAM_XACL_SOLARIS_POSIX: + case STREAM_XACL_SOLARIS_NFS4: + aclrc = pathconf(jcr->last_fname, _PC_ACL_ENABLED); + break; + default: + return bRC_XACL_error; + } + + switch (aclrc){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + return bRC_XACL_ok; + default: + Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + case 0: + clear_flag(XACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"), jcr->last_fname); + return bRC_XACL_error; + default: + break; + } + + switch (stream){ + case STREAM_XACL_SOLARIS_POSIX: + if ((aclrc & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0){ + Mmsg(jcr->errmsg, _("Trying to restore POSIX acl on file \"%s\" on filesystem without aclent acl support\n"), jcr->last_fname); + return bRC_XACL_error; + } + break; + case STREAM_XACL_SOLARIS_NFS4: + if ((aclrc & _ACL_ACE_ENABLED) == 0){ + Mmsg(jcr->errmsg, _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without ace acl support\n"), jcr->last_fname); + return bRC_XACL_error; + } + break; + default: + break; + } + + return os_set_acl(jcr, stream, content, length); +}; + +/* + * Perform OS specyfic extended attribute backup + * + * in/out - check API at xacl.h + * + * The Solaris implementation of XATTR is very, very different then all other "generic" unix implementations, + * so the original author of the Bacula XATTR support for Solaris OS decided to totally change the Xattr Stream + * content, and we need to follow this design to support previous behavior. The stream consist of a number of + * "files" with STREAM_XACL_SOLARIS_XATTR or STREAM_XACL_SOLARIS_SYS_XATTR stream' id. Every singe stream represents + * a single attibute. The content is a NULL-terminated array with a following data: + * \0\0\0 + * when an attribute file has a hardlinked other attributes then a content stream changes a bit into: + * \0\0\0 + * where: + * is an attribute name - a file name in Solaris + * is a standard file stat struct encoded by Bacula (the same encoding goes with a regular file) + * is a Solaris dependent acltotext data + * is the attribute file raw content + * is a name of the first hardlinked attribute file which a current attribute has to linked to + * + * The raw content of the attribute is copied into memory before send to the SD and for a very large attribute + * data can allocate a large amount of additional memory. In most cases it should not be a problem because most + * xattrs should has a few /hundred/ bytes in size. This is the same behavior as in previous implementation. + */ +bRC_XACL XACL_Solaris::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_XACL rc; + POOLMEM *xlist = NULL; + uint32_t xlen; + char *name; + char *lnkname; + uint32_t name_len; + POOLMEM *value = NULL; + uint32_t value_len; + char * xacltext; + uint32_t xacltext_len; + POOLMEM *data = NULL; + bool skip; + struct stat st; + char attribs[MAXSTRING]; + int stream; + int attrfd; + int len; + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_XACL_inval; + } + + /* check if extended/extensible attributes are present */ + if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0){ + /* xlist is allocated as POOLMEM by os_get_xattr_names */ + rc = os_get_xattr_names(jcr, &xlist, &xlen); + switch (rc){ + case bRC_XACL_ok: + /* it's ok, so go further */ + break; + case bRC_XACL_skip: + case bRC_XACL_cont: + /* no xattr available, so skip rest of it */ + return bRC_XACL_ok; + default: + return rc; + } + + data = get_pool_memory(PM_BSOCK); + /* follow the list of xattr names and get the values */ + for (name = xlist; (name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){ + name_len = strlen(name); + /* skip read-only or other unused attribute names */ + skip = check_xattr_skiplists(jcr, ff_pkt, name); + if (skip || name_len == 0){ + Dmsg1(100, "Skipping xattr named \"%s\"\n", name); + continue; + } + /* set a correct stream */ + stream = STREAM_XACL_SOLARIS_XATTR; + +#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) + /* check for system attributes name */ + if (bstrcmp(name, VIEW_READWRITE)){ + stream = STREAM_XACL_SOLARIS_SYS_XATTR; + } +#endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */ + + /* open an attribute descriptor, it will be used for backup */ + attrfd = attropen(jcr->last_fname, name, O_RDONLY); + + /* get the stat of the attribute */ + if (fstat(attrfd, &st) < 0){ + berrno be; + + switch (errno){ + case ENOENT: + rc = bRC_XACL_ok; + goto bailout; + default: + Mmsg3(jcr->errmsg, _("Unable to get status on xattr \"%s\" on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n", name, jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + goto bailout; + } + } + + /* we have a struct stat of the attribute so encode it to the buffer */ + encode_stat(attribs, &st, sizeof(st), st.st_ino, stream); + + /* get xattr acl data, but only when it is not trivial acls */ + rc = os_get_xattr_acl(jcr, attrfd, &xacltext); + if (rc != bRC_XACL_ok){ + goto bailout; + } + xacltext_len = strlen(xacltext); + + /* + * Solaris support only S_IFREG and S_IFDIR as an attribute file type, no other types are supported + * the previous Solaris xattr implementation in Bacula had an unverified and untested code for other + * types of attribute files which was a nonsense and unnecessarily complicate the code. We decided + * to remove unsupported code. To check if the current Solaris version support for xattr was extended + * simply verify a man fsattr(5) for it. + */ + switch (st.st_mode & S_IFMT){ + case S_IFREG: + /* check for hardlinked attributes which solaris support */ + if (st.st_nlink > 1){ + /* search for already saved file of the same inode number */ + lnkname = find_xattr_cache(jcr, st.st_ino, name); + if (lnkname != NULL){ + /* found a previous saved file, link to it and render xattr data for hardlinked attribute */ + len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, lnkname, 0); + set_content(data, len); + /* content is ready */ + break; + } + } + /* value is allocated as POOLMEM by os_get_xattr_value */ + rc = os_get_xattr_value(jcr, name, &value, &value_len); + switch (rc){ + case bRC_XACL_ok: + /* it's ok, so go further */ + break; + case bRC_XACL_skip: + /* no xattr available, so skip rest of it */ + rc = bRC_XACL_ok; + goto bailout; + default: + /* error / fatal */ + goto bailout; + } + /* save xattr info */ + len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, (xacltext) ? xacltext : "", 0); + /* append value data to the end of the xattr info */ + check_pool_memory_size(data, len + value_len); + memcpy(data + len, value, value_len); + set_content(data,len + value_len); + free_pool_memory(value); + value = NULL; + break; + case S_IFDIR: + /* save xattr info */ + len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, (xacltext) ? xacltext : "", 0); + set_content(data); + default: + Mmsg3(jcr->errmsg, _("Unsupported extended attribute type: %i for \"%s\" on file \"%s\"\n"), st.st_mode & S_IFMT, name, jcr->last_fname); + Dmsg3(100, "Unsupported extended attribute type: %i for \"%s\" on file \"%s\"\n", st.st_mode & S_IFMT, name, jcr->last_fname); + rc = bRC_XACL_error; + goto bailout; + } + /* send stream to the sd */ + rc = send_xattr_stream(jcr, stream); + if (rc != bRC_XACL_ok){ + Mmsg2(jcr->errmsg, _("Failed to send extended attribute \"%s\" on file \"%s\"\n"), name, jcr->last_fname); + Dmsg2(100, "Failed to send extended attribute \"%s\" on file \"%s\"\n", name, jcr->last_fname); + goto bailout; + } + } + +bailout: + /* free allocated data: xlist, value (if not freed), data, etc. */ + free_pool_memory(data); + if (value != NULL){ + free_pool_memory(value); + } + if (xlist != NULL){ + free_pool_memory(xlist); + } + /* this is a cache for a particular file, so no needed after backup of this file */ + delete_xattr_cache(); + + return rc; + } + return bRC_XACL_ok; +}; + +/* + * XACL_Solaris cache is a simple linked list cache of inode number and names used to handle + * xattr hard linked data. The function is searching for cached entry. When not found it append + * entry to the cache. + * in: + * jcr - Job Control Record (well, it is not used here) + * ino - inode number to compare/search for + * name - the name of the current attribute + * out: + * NULL - when entry not found in cache and new entry was added + * - a name of the linked entry + */ +inline char * XACL_Solaris::find_xattr_cache(JCR *jcr, ino_t ino, char * name){ + + XACL_Solaris_Cache *entry; + + if (cache != NULL){ + foreach_alist(entry, cache){ + if (entry && entry->inode == ino){ + /* found in cache, return name */ + return entry->name; + } + } + } else { + cache = New (alist(10, not_owned_by_alist)); + } + /* not found, so add this one to the cache */ + entry = (XACL_Solaris_Cache*) malloc (sizeof(XACL_Solaris_Cache)); + entry->inode = ino; + entry->name = name; + cache->append(entry); + return NULL; +} + +/* + * The function deletes a cache + * in/out - void + */ +inline void XACL_Solaris::delete_xattr_cache(){ + + XACL_Solaris_Cache *entry; + + if (cache != NULL){ + foreach_alist(entry, cache){ + free(entry); + } + delete cache; + cache = NULL; + } +} + +/* + * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS. + * + * The way Solaris xattr support is designed in Bacula we will have a single attribute restore + * with every call to this function. So multiple attributes are restored with multiple calls. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Solaris::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){ + + bRC_XACL rc = bRC_XACL_error; + bool extended = false; + + /* check input data */ + if (jcr == NULL || content == NULL){ + return bRC_XACL_inval; + } + + /* First make sure we can restore xattr on the filesystem */ + switch (stream){ +#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) + case STREAM_XACL_SOLARIS_SYS_XATTR: + if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0){ + Mmsg(jcr->errmsg, _("Failed to restore extensible attributes on file \"%s\"\n"), jcr->last_fname); + Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n", jcr->last_fname); + goto bail_out; + } + extended = true; + break; +#endif + case STREAM_XACL_SOLARIS_XATTR: + if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0){ + Mmsg(jcr->errmsg, _("Failed to restore extended attributes on file \"%s\"\n"), jcr->last_fname); + Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n", jcr->last_fname); + goto bail_out; + } + break; + default: + goto bail_out; + } + + rc = os_set_xattr(jcr, extended, content, length); + +bail_out: + return rc; +}; + +/* + * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Solaris::os_get_acl(JCR *jcr, int *stream){ + + int flags; + acl_t *aclp; + char *acl_text; + bRC_XACL rc = bRC_XACL_fatal; + + if (!stream){ + return bRC_XACL_fatal; + } + + if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0){ + /* we've got some error */ + berrno be; + switch (errno){ + case ENOENT: + /* file does not exist */ + return bRC_XACL_ok; + default: + Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(errno)); + Dmsg2(100, "acl_get error file=%s ERR=%s\n", jcr->last_fname, acl_strerror(errno)); + return bRC_XACL_error; + } + } + + if (!aclp){ + /* + * The ACLs simply reflect the (already known) standard permissions + * So we don't send an ACL stream to the SD. + */ + set_content(NULL); + return bRC_XACL_ok; + } + +#if defined(ACL_SID_FMT) + /* new format flag added in newer Solaris versions */ + flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT; +#else + flags = ACL_APPEND_ID | ACL_COMPACT_FMT; +#endif /* ACL_SID_FMT */ + + if ((acl_text = acl_totext(aclp, flags)) != NULL){ + set_content(acl_text); + actuallyfree(acl_text); + + switch (acl_type(aclp)){ + case ACLENT_T: + *stream = STREAM_XACL_SOLARIS_POSIX; + break; + case ACE_T: + *stream = STREAM_XACL_SOLARIS_NFS4; + break; + default: + rc = bRC_XACL_error; + break; + } + + acl_free(aclp); + } + return rc; +}; + +/* + * Low level OS specyfic runtime to get ACL on XATTR. The ACL data is set in supplied buffer + * + * in: + * jcr - Job Control Record + * fd - an opened file descriptor of the saved attribute + * buffer - a pointer to the memory buffer where we will render an acl text + * out: + * bRC_XACL_ok - backup acl for extended attribute finish without problems + * bRC_XACL_error - backup acl unsuccessful + * bRC_XACL_inval - input variables are invalid (null) + * + */ +bRC_XACL XACL_Solaris::os_get_xattr_acl(JCR *jcr, int fd, char **buffer){ + +// a function is valid only when Bacula have a support for ACL +#ifdef HAVE_ACL + bRC_XACL rc = bRC_XACL_error; + + /* sanity check of input variables */ + if (jcr == NULL || buffer == NULL || *buffer == NULL || fd < 0){ + return bRC_XACL_inval; + } + +#ifdef HAVE_EXTENDED_ACL + + int flags; + acl_t *aclp = NULL; + + /* check if an attribute has acl on it which we can save */ + if (fpathconf(fd, _PC_ACL_ENABLED) > 0){ + /* check for non trivial acl on the file */ + if (facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0){ + berrno be; + + switch (errno){ + case ENOENT: + rc = bRC_XACL_ok; + goto bail_out; + default: + Mmsg2(jcr->errmsg, _("Unable to get xattr acl on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "facl_get/acl_get of xattr on \"%s\" failed: ERR=%s\n", jcr->last_fname, be.bstrerror()); + goto bail_out; + } + } + + if (aclp != NULL){ +#if defined(ACL_SID_FMT) + /* New format flag added in newer Solaris versions. */ + flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT; +#else + flags = ACL_APPEND_ID | ACL_COMPACT_FMT; +#endif /* ACL_SID_FMT */ + + *buffer = acl_totext(aclp, flags); + acl_free(aclp); + } else { + *buffer = NULL; + } + } else { + *buffer = NULL; + } + rc = bRC_XACL_ok; +bail_out: + +#else /* !HAVE_EXTENDED_ACL */ + + int n; + aclent_t *acls = NULL; + + /* See if this attribute has an ACL */ + if (fd != -1){ + n = facl(fd, GETACLCNT, 0, NULL); + } else { + n = acl(attrname, GETACLCNT, 0, NULL); + } + + if (n >= MIN_ACL_ENTRIES){ + acls = (aclent_t *)malloc(n * sizeof(aclent_t)); + if ((fd != -1 && facl(fd, GETACL, n, acls) != n) || + acl(attrname, GETACL, n, acls) != n){ + berrno be; + + switch (errno){ + case ENOENT: + free(acls); + retval = bRC_XACL_ok; + goto bail_out; + default: + Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"), attrname, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n", attrname, jcr->last_fname, be.bstrerror()); + free(acls); + goto bail_out; + } + } + + /* See if there is a non trivial acl on the file. */ + if (!acl_is_trivial(n, acls)){ + if ((*acl_text = acltotext(acls, n)) == NULL){ + berrno be; + + Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"), attrname, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n", attrname, jcr->last_fname, be.bstrerror()); + free(acls); + goto bail_out; + } + } else { + *buffer = NULL; + } + + free(acls); + } else { + *buffer = NULL; + } + rc = bRC_XACL_ok; +#endif /* HAVE_EXTENDED_ACL */ + return rc; +#else /* HAVE_ACL */ + return bRC_XACL_ok; +#endif /* HAVE_ACL */ +} + +/* + * Low level OS specyfic runtime to set ACL on XATTR. The ACL data is set from supplied text + * + * in: + * jcr - Job Control Record + * fd - an opened file descriptor of the restored attribute + * buffer - a pointer to the memory buffer where we will render an acl text + * out: + * bRC_XACL_ok - backup acl for extended attribute finish without problems + * bRC_XACL_inval - input variables are invalid (null) + * + */ +bRC_XACL XACL_Solaris::os_set_xattr_acl(JCR *jcr, int fd, char *name, char *acltext){ + +// a function is valid only when Bacula have a support for ACL +#ifdef HAVE_ACL + + bRC_XACL rc = bRC_XACL_error; + + /* sanity check of input variables */ + if (jcr == NULL || name == NULL || acltext == NULL || fd < 0){ + return bRC_XACL_inval; + } + +#ifdef HAVE_EXTENDED_ACL + + int error; + acl_t *aclp = NULL; + + if ((error = acl_fromtext(acltext, &aclp)) != 0){ + Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"), jcr->last_fname); + return bRC_XACL_error; + } + + if (fd != -1 && facl_set(fd, aclp) != 0){ + berrno be; + + Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + } + +bail_out: + if (aclp){ + acl_free(aclp); + } + +#else /* !HAVE_EXTENDED_ACL */ + + int n; + aclent_t *acls = NULL; + + acls = aclfromtext(acltext, &n); + if (acls){ + if (fd != -1 && facl(fd, SETACL, n, acls) != 0){ + berrno be; + + Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + } + free(acls); + } + +#endif /* HAVE_EXTENDED_ACL */ + + return rc; +#else /* HAVE_ACL */ + return bRC_XACL_ok; +#endif /* HAVE_ACL */ +}; + +/* + * Low level OS specyfic runtime to set ACL data on file + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Solaris::os_set_acl(JCR *jcr, int stream, char *content, uint32_t length){ + + int rc; + acl_t *aclp; + + if ((rc = acl_fromtext(content, &aclp)) != 0){ + Mmsg2(jcr->errmsg, _("acl_fromtext error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(rc)); + Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, acl_strerror(rc)); + return bRC_XACL_error; + } + + switch (stream){ + case STREAM_XACL_SOLARIS_POSIX: + if (acl_type(aclp) != ACLENT_T){ + Mmsg(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"), jcr->last_fname); + return bRC_XACL_error; + } + break; + case STREAM_XACL_SOLARIS_NFS4: + if (acl_type(aclp) != ACE_T){ + Mmsg(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"), jcr->last_fname); + return bRC_XACL_error; + } + break; + default: + break; + } + + if ((rc = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK){ + switch (errno){ + case ENOENT: + acl_free(aclp); + return bRC_XACL_ok; + default: + Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(rc)); + Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, acl_strerror(rc)); + acl_free(aclp); + return bRC_XACL_error; + } + } + + acl_free(aclp); + return bRC_XACL_ok; +}; + +/* + * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Solaris::os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){ + + int xattrdfd; + DIR *dirp; + struct dirent *dp; + + int len; + int slen; + POOLMEM * list; + char * p; + + /* check input data */ + if (jcr == NULL || xlen == NULL || pxlist == NULL){ + return bRC_XACL_inval; + } + + /* Open the xattr stream on file */ + if ((xattrdfd = attropen(jcr->last_fname, ".", O_RDONLY)) < 0){ + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + case EINVAL: + /* no xattr supported on file skip it */ + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("Unable to open xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "Unable to open xattr on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + + /* open an extended file directory to read all xattr names */ + if ((dirp = fdopendir(xattrdfd)) == (DIR *)NULL){ + berrno be; + + Mmsg2(jcr->errmsg, _("Unable to list the xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg3(100, "Unable to fdopendir xattr on file \"%s\" using fd %d: ERR=%s\n", jcr->last_fname, xattrdfd, be.bstrerror()); + close(xattrdfd); + return bRC_XACL_error; + } + + /* + * allocate memory for the extented attribute list + * default size is a 4k for PM_BSOCK, which should be sufficient in most cases + */ + list = p = get_pool_memory(PM_BSOCK); + memset(list, 0, sizeof_pool_memory(list)); + len = 0; + + /* read all directory entries as a xattr names */ + while ((dp = readdir(dirp)) != NULL){ + + /* skip '..' name as it a file we backup */ + if (bstrcmp(dp->d_name, "..") == 0){ + continue; + } + +#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) + /* skip a read only attributes which we cant restore later */ + if (bstrcmp(dp->d_name, VIEW_READONLY)){ + Dmsg2(400, "Skipping readonly extensible attributes %s on file \"%s\"\n", dp->d_name, jcr->last_fname); + continue; + } +#endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */ + + /* compute a buffer length = string length and nul char */ + slen = strlen (dp->d_name); + len += slen + 1; + list = check_pool_memory_size(list, len); + + /* copy the name into a list */ + bstrncpy(p, dp->d_name, slen); + p[slen] = '\0'; + p += slen + 1; + } + if (closedir(dirp) < 0){ + berrno be; + + Mmsg2(jcr->errmsg, _("Unable to close xattr list on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "Unable to close xattr list on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + + *pxlist = list; + *xlen = len; + + return bRC_XACL_ok; +}; + +/* + * Return a value of the requested attribute name and a length of the allocated buffer. + * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed + * when not needed. + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Solaris::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){ + + int xattrfd; + int len; + POOLMEM * value; + struct stat st; + + /* check input data */ + if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){ + return bRC_XACL_inval; + } + + /* Open the xattr on file */ + if ((xattrfd = attropen(jcr->last_fname, name, O_RDONLY)) < 0){ + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + case EINVAL: + /* no xattr supported on file skip it */ + return bRC_XACL_skip; + default: + Mmsg2(jcr->errmsg, _("Unable to open xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "Unable to open xattr on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + + /* get some info about extended attribute */ + if (fstat(xattrfd, &st) < 0){ + berrno be; + + switch (errno){ + case ENOENT: + /* no file available, skip it */ + return bRC_XACL_skip; + default: + Mmsg3(jcr->errmsg, _("Unable to stat xattr \"%s\" on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "Unable to stat xattr \"%s\" on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror()); + return bRC_XACL_error; + } + } + + /* default empty value */ + value = NULL; + len = 0; + + /* only a file has a data/value we should care about */ + if ((st.st_mode & S_IFMT) != S_IFDIR){ + /* get size of the attribute data/value */ + len = lseek(xattrfd, 0, SEEK_END); + lseek(xattrfd, 0, SEEK_SET); + } + if (len > 0){ + /* + * allocate memory for the extented attribute value + * default size is a 256B for PM_MESSAGE, so we need to check required size + */ + value = get_pool_memory(PM_MESSAGE); + value = check_pool_memory_size(value, len); + memset(value, 0, len); + /* read teh data */ + read (xattrfd, value, len); + close(xattrfd); + } + + /* setup return data */ + *pvalue = value; + *plen = len; + return bRC_XACL_ok; +}; + +/* + * Low level OS specyfic runtime to set extended attribute on file + * + * in/out - check API at xacl.h + */ +bRC_XACL XACL_Solaris::os_set_xattr (JCR *jcr, bool extended, char *content, uint32_t length){ + + char *bp = content + 1; /* original code saves attribute name with '/' */ + char *name; + char *attribs; + char *acltext; + char *lntarget; + int attrfd = 0; + int attrdirfd = 0; + int cnt; + int len; + int inum; + struct stat st; + struct timeval times[2]; + bRC_XACL rc = bRC_XACL_ok; + + /* check input data */ + if (jcr == NULL || content == NULL){ + return bRC_XACL_inval; + } + /* + * Parse content stream and extract valuable data. + * STD/EXT: \0\0\0 + * LNK: \0\0\0 + */ + /* attribute name and length */ + name = bp; + len = strlen (bp); + bp += len + 1; + + /* attribute encoded stat */ + attribs = bp; + len = strlen (bp); + bp += len + 1; + /* decode it */ + decode_stat(attribs, &st, sizeof(st), &inum); + + /* acltext and link target name goes here */ + acltext = lntarget = bp; + len = strlen (bp); + /* now 'bp' should have a xattr data */ + bp += len + 1; + + /* + * Open the xattr on which to restore the xattrs read-only. + */ + if ((attrdirfd = attropen(jcr->last_fname, ".", O_RDONLY)) < 0){ + berrno be; + + Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + goto bail_out; + } + + switch (st.st_mode & S_IFMT){ + case S_IFREG: + if (inum != 0){ + /* it is a linked attribute, perform a link operation */ + unlinkat(attrdirfd, name, 0); + if (link(lntarget, name) < 0){ + berrno be; + + Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"), name, lntarget, jcr->last_fname, be.bstrerror()); + Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n", name, lntarget, jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + goto bail_out; + } + goto bail_out; + } else { + if (!extended){ + unlinkat(attrdirfd, name, 0); + } + if ((attrfd = openat(attrdirfd, name, O_CREAT|O_RDWR)) < 0){ + berrno be; + + Mmsg3(jcr->errmsg, _("Unable to open attribute \"%s\" at file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "Unable to open attribute \"%s\" at file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + goto bail_out; + } + /* restore any data if are available */ + if (st.st_size > 0){ + cnt = write (attrfd, bp, length - (bp - content) ); + if (cnt < 0){ + berrno be; + + Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + goto bail_out; + } + } + } + break; + case S_IFDIR: + /* if it is a current dir ob file then we can restore acl data only */ + if (bstrcmp(name, ".")){ + break; + } + default: + Mmsg2(jcr->errmsg, _("Unsupported xattr type %s on file \"%s\"\n"), name, jcr->last_fname); + Dmsg2(100, "Unsupported xattr type %s on file \"%s\"\n", name, jcr->last_fname); + goto bail_out; + } + + /* file data restored, so setup permissions and acl data */ + if (!extended){ + if (fchownat(attrdirfd, name, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0){ + berrno be; + + switch (errno){ + case EINVAL: + case ENOENT: + break; + default: + Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + } + goto bail_out; + } + } + +#ifdef HAVE_ACL + if (strlen(acltext)){ + rc = os_set_xattr_acl(jcr, attrfd, name, acltext); + if (rc != bRC_XACL_ok){ + goto bail_out; + } + } +#endif /* HAVE_ACL */ + + /* now restore a access and modification time - only for standard attribute */ + if (!extended){ + times[0].tv_sec = st.st_atime; + times[0].tv_usec = 0; + times[1].tv_sec = st.st_mtime; + times[1].tv_usec = 0; + + if (futimesat(attrdirfd, name, times) < 0){ + berrno be; + + Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror()); + Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror()); + rc = bRC_XACL_error; + goto bail_out; + } + } + +bail_out: + if (attrfd != 0){ + close(attrfd); + } + if (attrdirfd != 0){ + close(attrdirfd); + } + return rc; +}; + +#endif /* HAVE_SUN_OS */ diff --git a/bacula/src/filed/xacl_solaris.h b/bacula/src/filed/xacl_solaris.h new file mode 100644 index 0000000000..f8a8d5b773 --- /dev/null +++ b/bacula/src/filed/xacl_solaris.h @@ -0,0 +1,99 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. + */ +/** + * Major refactoring of ACL and XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __XACL_Solaris_H_ +#define __XACL_Solaris_H_ + +#if defined(HAVE_SUN_OS) +#ifdef HAVE_SYS_ACL_H +#include +#else +#error "configure failed to detect availability of sys/acl.h" +#endif + +#ifdef HAVE_SYS_ATTR_H +#include +#endif + +/* + * + */ +#if defined(HAVE_EXTENDED_ACL) +#if !defined(_SYS_ACL_IMPL_H) +typedef enum acl_type { + ACLENT_T = 0, + ACE_T = 1 +} acl_type_t; +#endif + +/* + * + */ +extern "C" { +int acl_type(acl_t *); +char *acl_strerror(int); +}; +#endif + +/* + * Cache structure in alist + */ +struct XACL_Solaris_Cache { + ino_t inode; + char * name; +}; + +/* + * + * + */ +class XACL_Solaris : public XACL { +private: + alist * cache; + bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_get_acl(JCR *jcr, int *stream); + bRC_XACL os_set_acl(JCR *jcr, int stream, char *content, uint32_t length); + bRC_XACL os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length); + bRC_XACL os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen); + bRC_XACL os_set_xattr (JCR *jcr, bool extended, char *content, uint32_t length); + bRC_XACL os_get_xattr_acl(JCR *jcr, int fd, char **buffer); + bRC_XACL os_set_xattr_acl(JCR *jcr, int fd, char *name, char *acltext); + /* requires acl.h available */ + bRC_XACL check_xacltype (JCR *jcr, int name); + inline char * find_xattr_cache(JCR *jcr, ino_t ino, char * name); + inline void delete_xattr_cache(); +public: + XACL_Solaris (); + ~XACL_Solaris (); +}; + +#endif /* HAVE_SUN_OS */ + +#endif /* __XACL_Solaris_H_ */ diff --git a/bacula/src/filed/xattr.c b/bacula/src/filed/xattr.c deleted file mode 100644 index 67623481fa..0000000000 --- a/bacula/src/filed/xattr.c +++ /dev/null @@ -1,3663 +0,0 @@ -/* - Bacula(R) - The Network Backup Solution - - Copyright (C) 2000-2015 Kern Sibbald - Copyright (C) 2008-2014 Free Software Foundation Europe e.V. - - The original author of Bacula is Kern Sibbald, with contributions - from many others, a complete list can be found in the file AUTHORS. - - You may use this file and others of this release according to the - license defined in the LICENSE file, which includes the Affero General - Public License, v3.0 ("AGPLv3") and some additional permissions and - terms pursuant to its AGPLv3 Section 7. - - This notice must be preserved when any source code is - conveyed and/or propagated. - - Bacula(R) is a registered trademark of Kern Sibbald. -*/ -/* - * Functions to handle Extended Attributes for bacula. - * - * Extended Attributes are so OS specific we only restore Extended Attributes if - * they were saved using a filed on the same platform. - * - * Currently we support the following OSes: - * - AIX (Extended Attributes) - * - Darwin (Extended Attributes) - * - FreeBSD (Extended Attributes) - * - GNU HURD (Extended Attributes) - * - IRIX (Extended Attributes) - * - Linux (Extended Attributes) - * - NetBSD (Extended Attributes) - * - OpenBSD (Extended Attributes) - * (As it seems either they never implemented xattr or they are removed - * the support as it stated it was in version 3.1 but the current syscall - * tabled shows the extattr_ functions are not implemented. So as such we - * might eventually support xattr on OpenBSD when they implemented them using - * the same interface as FreeBSD and NetBSD. - * - Solaris (Extended Attributes and Extensible Attributes) - * - Tru64 (Extended Attributes) - * - * Written by Marco van Wieringen, November 2008 - * Major overhaul January 2012 + June 2012 - * Simplfied by Kern Sibbald, June 2015 - */ - -#include "bacula.h" -#include "filed.h" - -#ifndef HAVE_XATTR -/* - * Entry points when compiled without support for XATTRs or on an unsupported platform. - */ -bool backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - Jmsg(jcr, M_FATAL, 0, "XATTR backup requested but not configured in Bacula.\n"); - return false; -} - -bxattr_rtn_code restore_xattr_streams(JCR *jcr, int stream, char *content, - uint32_t content_length) -{ - return bxattr_rtn_fatal; -} -#else -/* - * Send a XATTR stream to the SD. - */ -static bxattr_rtn_code send_xattr_stream(JCR *jcr, int stream) -{ - BSOCK *sd = jcr->store_bsock; - POOLMEM *msgsave; - -#ifdef FD_NO_SEND_TEST - return bxattr_rtn_ok; -#endif - - /* - * Sanity check - */ - if (jcr->xattr_ctx->content_length <= 0) { - return bxattr_rtn_ok; - } - - /* - * Send header - */ - if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); - return bxattr_rtn_fatal; - } - - /* - * Send the buffer to the storage deamon - */ - Dmsg1(400, "Backing up XATTR <%s>\n", jcr->xattr_ctx->content); - msgsave = sd->msg; - sd->msg = jcr->xattr_ctx->content; - sd->msglen = jcr->xattr_ctx->content_length; - if (!sd->send()) { - sd->msg = msgsave; - sd->msglen = 0; - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); - return bxattr_rtn_fatal; - } - - jcr->JobBytes += sd->msglen; - sd->msg = msgsave; - if (!sd->signal(BNET_EOD)) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), - sd->bstrerror()); - return bxattr_rtn_fatal; - } - Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname); - return bxattr_rtn_ok; -} - -/* - * First some generic functions for OSes that use the same xattr encoding scheme. - * Currently for all OSes except for Solaris. - */ -#ifndef HAVE_SUN_OS -static void xattr_drop_internal_table(alist *xattr_value_list) -{ - xattr_t *current_xattr; - - if (!xattr_value_list) { - return; - } - /* Walk the list of xattrs and free allocated memory. */ - foreach_alist(current_xattr, xattr_value_list) { - if (current_xattr->magic != XATTR_MAGIC) { - continue; - } - if (current_xattr->name) { - free(current_xattr->name); - } - if (current_xattr->value && current_xattr->value_length > 0) { - free(current_xattr->value); - } - free(current_xattr); - } - delete xattr_value_list; -} - -/* - * The xattr stream for OSX, FreeBSD, Linux and NetBSD is a serialized stream of bytes - * which encodes one or more xattr_t structures. - * - * The Serialized stream consists of the following elements: - * magic - A magic string which makes it easy to detect any binary incompatabilites - * name_length - The length of the following xattr name - * name - The name of the extended attribute - * value_length - The length of the following xattr data - * value - The actual content of the extended attribute - * - * This is repeated 1 or more times. - * - */ -static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, - alist *xattr_value_list) -{ - xattr_t *current_xattr; - ser_declare; - - /* - * Make sure the serialized stream fits in the poolmem buffer. - * We allocate some more to be sure the stream is gonna fit. - */ - jcr->xattr_ctx->content = - check_pool_memory_size(jcr->xattr_ctx->content, expected_serialize_len + 10); - ser_begin(jcr->xattr_ctx->content, expected_serialize_len + 10); - - /* - * Walk the list of xattrs and serialize the data. - */ - foreach_alist(current_xattr, xattr_value_list) { - if (current_xattr->magic != XATTR_MAGIC) { - continue; /* Don't write invalid xattr */ - } - - ser_uint32(current_xattr->magic); - ser_uint32(current_xattr->name_length); - ser_bytes(current_xattr->name, current_xattr->name_length); - - ser_uint32(current_xattr->value_length); - if (current_xattr->value_length > 0 && current_xattr->value) { - ser_bytes(current_xattr->value, current_xattr->value_length); - Dmsg3(100, "Backup xattr named %s, value %*s\n", - current_xattr->name, current_xattr->value, current_xattr->value); - } else { - Dmsg1(100, "Backup empty xattr named %s\n", current_xattr->name); - } - } - - ser_end(jcr->xattr_ctx->content, expected_serialize_len + 10); - jcr->xattr_ctx->content_length = ser_length(jcr->xattr_ctx->content); - return jcr->xattr_ctx->content_length; -} - -static bxattr_rtn_code unserialize_xattr_stream(JCR *jcr, - char *content, - uint32_t content_length, - alist *xattr_value_list) -{ - unser_declare; - xattr_t *current_xattr; - - /* - * Restore the stream and call restore_xattr_on_file for each extended attribute. - * - * Start unserializing the data. We keep on looping while we have not - * unserialized all bytes in the stream. - */ - unser_begin(content, content_length); - while (unser_length(content) < content_length) { - /* - * First make sure the magic is present. This way we can easily catch corruption. - * Any missing MAGIC is fatal we do NOT try to continue. - */ - current_xattr = (xattr_t *)malloc(sizeof(xattr_t)); - unser_uint32(current_xattr->magic); - if (current_xattr->magic != XATTR_MAGIC) { - Mmsg1(jcr->errmsg, _("Invalid xattr stream, no XATTR_MAGIC on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - free(current_xattr); - return bxattr_rtn_error; - } - - /* - * Decode the valuepair. First decode the length of the name. - */ - unser_uint32(current_xattr->name_length); - if (current_xattr->name_length == 0) { - Mmsg1(jcr->errmsg, _("Invalid xattr stream, xattr name length <= 0 on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - free(current_xattr); - return bxattr_rtn_error; - } - - /* - * Allocate room for the name and decode its content. - */ - current_xattr->name = (char *)malloc(current_xattr->name_length + 1); - unser_bytes(current_xattr->name, current_xattr->name_length); - - /* - * The xattr_name needs to be null terminated. - */ - current_xattr->name[current_xattr->name_length] = '\0'; - - /* - * Decode the value length. - */ - unser_uint32(current_xattr->value_length); - - if (current_xattr->value_length > 0) { - /* - * Allocate room for the value and decode its content. - */ - current_xattr->value = (char *)malloc(current_xattr->value_length); - unser_bytes(current_xattr->value, current_xattr->value_length); - - Dmsg3(100, "Restoring xattr named %s, value %*s\n", - current_xattr->name, current_xattr->value, current_xattr->value); - } else { - current_xattr->value = NULL; - Dmsg1(100, "Restoring empty xattr named %s\n", current_xattr->name); - } - - xattr_value_list->append(current_xattr); - } - - unser_end(content, content_length); - return bxattr_rtn_ok; -} -#endif - -/* - * This is a supported OS, See what kind of interface we should use. - */ -#if defined(HAVE_AIX_OS) - -#if (!defined(HAVE_LISTEA) && !defined(HAVE_LLISTEA)) || \ - (!defined(HAVE_GETEA) && !defined(HAVE_LGETEA)) || \ - (!defined(HAVE_SETEA) && !defined(HAVE_LSETEA)) -#error "Missing full support for the Extended Attributes (EA) functions." -#endif - -#ifdef HAVE_SYS_EA_H -#include -#else -#error "Missing sys/ea.h header file" -#endif - -/* - * Define the supported XATTR streams for this OS - */ -static int os_default_xattr_streams[1] = { - STREAM_XATTR_AIX -}; - -/* - * Fallback to the non l-functions when those are not available. - */ -#if defined(HAVE_GETEA) && !defined(HAVE_LGETEA) -#define lgetea getea -#endif -#if defined(HAVE_SETEA) && !defined(HAVE_LSETEA) -#define lsetea setea -#endif -#if defined(HAVE_LISTEA) && !defined(HAVE_LLISTEA) -#define llistea listea -#endif - -static bxattr_rtn_code aix_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - char *bp; - bool skip_xattr; - char *xattr_list = NULL; - int cnt, xattr_count = 0; - uint32_t name_length; - int32_t xattr_list_len, xattr_value_len; - uint32_t expected_serialize_len = 0; - xattr_t *current_xattr; - alist *xattr_value_list = NULL; - bxattr_rtn_code retval = bxattr_rtn_error; - - /* - * First get the length of the available list with extended attributes. - */ - xattr_list_len = llistea(jcr->last_fname, NULL, 0); - if (xattr_list_len < 0) { - berrno be; - if (errno == ENOENT || errno == EFORMAT) { - retval = bxattr_rtn_ok; - } else if (errno == ENOTSUP) { - /* - * If the filesystem reports it doesn't support XATTRs we clear the - * BXATTR_FLAG_SAVE_NATIVE flag so we skip XATTR saves on all other files - * on the same filesystem. The BXATTR_FLAG_SAVE_NATIVE flags gets sets again - * when we change from one filesystem to an other. - */ - jcr->xattr_ctx->flags &= ~BXATTR_FLAG_SAVE_NATIVE; - retval = bxattr_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("XATTR llistea error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } else if (xattr_list_len == 0) { - retval = bxattr_rtn_ok; - goto get_out; - } - - /* - * Allocate room for the extented attribute list. - */ - xattr_list = (char *)malloc(xattr_list_len + 1); - memset(xattr_list, 0, xattr_list_len + 1); - - /* - * Get the actual list of extended attributes names for a file. - */ - xattr_list_len = llistea(jcr->last_fname, xattr_list, xattr_list_len); - if (xattr_list_len < 0) { - berrno be; - if (errno == ENOENT || errno == EFORMAT) { - retval = bxattr_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("XATTR llistea error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - xattr_list[xattr_list_len] = '\0'; - - /* - * Walk the list of extended attributes names and retrieve the data. - * We already count the bytes needed for serializing the stream later on. - */ - for (bp = xattr_list; (bp - xattr_list) + 1 < xattr_list_len; - bp = strchr(bp, '\0') + 1) { - skip_xattr = false; - - /* - * We want to skip certain xattrs which start with a 0xF8 character on AIX. - */ - if (*bp == 0xF8) { - skip_xattr = true; - } - - name_length = strlen(bp); - if (skip_xattr || name_length == 0) { - Dmsg1(100, "Skipping xattr named %s\n", bp); - continue; - } - - /* - * First see how long the value is for the extended attribute. - */ - xattr_value_len = lgetea(jcr->last_fname, bp, NULL, 0); - if (xattr_value_len < 0) { - berrno be; - if (errno == ENOENT || errno == EFORMAT) { - retval = bxattr_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("XATTR lgetea error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - - /* - * Each xattr valuepair starts with a magic so we can restore it easier. - */ - current_xattr = (xattr_t *)malloc(sizeof(xattr_t)); - memset(current_xattr, 0, sizeof(xattr_t)); - current_xattr->magic = XATTR_MAGIC; - expected_serialize_len += sizeof(current_xattr->magic); - - /* - * Allocate space for storing the name. - */ - current_xattr->name_length = name_length; - current_xattr->name = (char *)malloc(current_xattr->name_length); - memcpy(current_xattr->name, bp, current_xattr->name_length); - - expected_serialize_len += sizeof(current_xattr->name_length) + - current_xattr->name_length; - - if (xattr_value_len == 0) { - current_xattr->value = NULL; - current_xattr->value_length = 0; - expected_serialize_len += sizeof(current_xattr->value_length); - } else { - /* - * Allocate space for storing the value. - */ - current_xattr->value = (char *)malloc(xattr_value_len); - memset(current_xattr->value, 0, xattr_value_len); - - xattr_value_len = lgetea(jcr->last_fname, bp, current_xattr->value, xattr_value_len); - if (xattr_value_len < 0) { - berrno be; - if (errno == ENOENT || errno == EFORMAT) { - retval = bxattr_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("XATTR lgetea error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - - /* - * Default failure path out when retrieval of attr fails. - */ - free(current_xattr->value); - free(current_xattr->name); - free(current_xattr); - goto get_out; - } - /* - * Store the actual length of the value. - */ - current_xattr->value_length = xattr_value_len; - expected_serialize_len += sizeof(current_xattr->value_length) + - current_xattr->value_length; - } - - if (xattr_value_list == NULL) { - xattr_value_list = New(alist(10, not_owned_by_alist)); - } - - xattr_value_list->append(current_xattr); - xattr_count++; - - /* - * Protect ourself against things getting out of hand. - */ - if (expected_serialize_len >= MAX_XATTR_LENGTH) { - Mmsg2(jcr->errmsg, - _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"), - jcr->last_fname, MAX_XATTR_LENGTH); - goto get_out; - } - } - - free(xattr_list); - xattr_list = (char *)NULL; - - /* - * If we found any xattr send them to the SD. - */ - if (xattr_count > 0) { - /* Serialize the datastream. */ - if (serialize_xattr_stream(jcr, expected_serialize_len, - xattr_value_list) < expected_serialize_len) { - Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Send the datastream to the SD. - */ - retval = send_xattr_stream(jcr, os_default_xattr_streams[0]); - } else { - retval = bxattr_rtn_ok; - } - -get_out: - if (xattr_list != NULL) { - free(xattr_list); - } - if (xattr_value_list != NULL) { - xattr_drop_internal_table(xattr_value_list); - } - - return retval; -} - -static bxattr_rtn_code aix_restore_xattr_streams(JCR *jcr, int stream, char *content, - uint32_t content_length) -{ - xattr_t *current_xattr; - alist *xattr_value_list; - bxattr_rtn_code retval = bxattr_rtn_error; - - xattr_value_list = New(alist(10, not_owned_by_alist)); - - if (unserialize_xattr_stream(jcr, content, content_length, - xattr_value_list) != bxattr_rtn_ok) { - goto get_out; - } - - foreach_alist(current_xattr, xattr_value_list) { - if (lsetea(jcr->last_fname, current_xattr->name, current_xattr->value, - current_xattr->value_length, 0) != 0) { - berrno be; - switch (errno) { - case ENOENT: - case EFORMAT: - break; - case ENOTSUP: - /* - * If the filesystem reports it doesn't support XATTRs we clear - * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores - * on all other files on the same filesystem. The - * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we - * change from one filesystem to an other. - */ - jcr->xattr_ctx->flags &= ~BXATTR_FLAG_RESTORE_NATIVE; - /* Failback wanted */ - default: - MmsgD2(100, jcr->errmsg, - _("XATTR lsetea error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - break; - } - goto get_out; - } - } - retval = bxattr_rtn_ok; - -get_out: - xattr_drop_internal_table(xattr_value_list); - - return retval; -} - -/* - * Function pointers to the build and restore function to use for these xattrs. - */ -static bxattr_rtn_code (*os_backup_xattr_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - aix_xattr_build_streams; -static bxattr_rtn_code (*os_restore_xattr_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - aix_restore_xattr_streams; - -#elif defined(HAVE_IRIX_OS) - -#include - -/* - * Define the supported XATTR streams for this OS - */ -static int os_default_xattr_streams[1] = { - STREAM_XATTR_IRIX -}; -static const char *xattr_acl_skiplist[1] = { - NULL -}; -static const char *xattr_skiplist[1] = { - NULL -}; - -struct xattr_naming_space { - const char *name; - int flags; -}; - -static xattr_naming_space xattr_naming_spaces[] = { - { - "user.", - ATTR_DONTFOLLOW - }, { - "root.", - ATTR_ROOT | ATTR_DONTFOLLOW - }, { - NULL, - 0 - } -}; - -static bxattr_rtn_code irix_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - char dummy[32]; - int cnt, length, xattr_count = 0; - attrlist_cursor_t cursor; - attrlist_t *attrlist; - attrlist_ent_t *attrlist_ent; - xattr_t *current_xattr; - alist *xattr_value_list = NULL; - uint32_t expected_serialize_len = 0; - bxattr_rtn_code retval = bxattr_rtn_error; - POOLMEM *xattrbuf = get_memory(ATTR_MAX_VALUELEN); - - for (cnt = 0; xattr_naming_spaces[cnt].name != NULL; cnt++) { - memset(&cursor, 0, sizeof(attrlist_cursor_t)); - for ( ;; } { - if (attr_list(jcr->last_fname, xattrbuf, ATTR_MAX_VALUELEN, - xattr_naming_spaces[cnt].flags, &cursor) != 0) { - berrno be; - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg2(jcr->errmsg, _("XATTR attr_list error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - attrlist = (attrlist_t *)xattrbuf; - - /* - * Walk the available attributes. - */ - for (cnt = 0; cnt < attrlist->al_count; cnt++) { - attrlist_ent = ATTR_ENTRY(xattrbuf, cnt); - - /* - * First determine if we can retrieve the xattr and how big it really is. - */ - length = sizeof(dummy); - if (attr_get(jcr->last_fname, attrlist_ent->a_name, dummy, - &length, xattr_naming_spaces[cnt].flags) != 0) { - berrno be; - switch (errno) { - case ENOENT: - case ENOATTR: - retval = bxattr_rtn_ok; - goto get_out; - case E2BIG: - /* - * Size of the xattr is bigger then the 32 bytes dummy which is - * likely. As length now contains its actual length we can allocate - * a properly size buffer for the real retrieval. - */ - break; - default: - Mmsg2(jcr->errmsg, _("XATTR attr_list error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - /* - * Each xattr valuepair starts with a magic so we can restore it easier. - */ - current_xattr = (xattr_t *)malloc(sizeof(xattr_t)); - memset(current_xattr, 0, sizeof(xattr_t)); - current_xattr->magic = XATTR_MAGIC; - expected_serialize_len += sizeof(current_xattr->magic); - - /* - * Allocate space for storing the name. - * We store the name as - */ - current_xattr->name_length = strlen(xattr_naming_spaces[cnt].name) + - strlen(attrlist_ent->a_name) + 1; - current_xattr->name = (char *)malloc(current_xattr->name_length); - bsnprintf(current_xattr->name, current_xattr->name_length, "%s%s", - xattr_naming_spaces[cnt].name, attrlist_ent->a_name); - - expected_serialize_len += sizeof(current_xattr->name_length) + - current_xattr->name_length; - - current_xattr->value_length = length; - current_xattr->value = (char *)malloc(current_xattr->value_length); - - /* - * Retrieve the actual value of the xattr. - */ - if (attr_get(jcr->last_fname, attrlist_ent->a_name, current_xattr->value, - &length, xattr_naming_spaces[cnt].flags) != 0) { - berrno be; - - switch (errno) { - case ENOENT: - case ENOATTR: - retval = bxattr_rtn_ok; - break; - case E2BIG: - /* - * The buffer for the xattr isn't big enough. the value of - * current_xattr->value_length is updated with the actual size - * of the xattr. So we free the old buffer and create a new one - * and try again. Normally this cannot happen as we size the - * buffer using a call to attr_get before but in case of an - * race condition it might happen. - */ - free(current_xattr->value); - current_xattr->value = (char *)malloc(length); - if (attr_get(jcr->last_fname, attrlist_ent->a_name, current_xattr->value, - &length, xattr_naming_spaces[cnt].flags) != 0) { - switch (errno) { - case ENOENT: - case ENOATTR: - retval = bxattr_rtn_ok; - break; - default: - Mmsg2(jcr->errmsg, - _("attr_list error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror(errno)); - Dmsg2(100, "attr_list error file=%s ERR=%s\n", - jcr->last_fname, be.bstrerror()); - break; - } - } else { - goto ok_continue; - } - break; - default: - Mmsg2(jcr->errmsg, _("XATTR attr_list error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - break; - } - - /* - * Default failure path out when retrieval of attr fails. - */ - free(current_xattr->value); - free(current_xattr->name); - free(current_xattr); - goto get_out; - } - -ok_continue: - current_xattr->value_length = length; - expected_serialize_len += sizeof(current_xattr->value_length) + - current_xattr->value_length; - - if (xattr_value_list == NULL) { - xattr_value_list = New(alist(10, not_owned_by_alist)); - } - - xattr_value_list->append(current_xattr); - xattr_count++; - - /* - * Protect ourself against things getting out of hand. - */ - if (expected_serialize_len >= MAX_XATTR_LENGTH) { - Mmsg2(jcr->errmsg, - _("XATTR stream on file \"%s\" exceeds maximum size of %d bytes\n"), - jcr->last_fname, MAX_XATTR_LENGTH); - goto get_out; - } - } - - /* - * See if there are more attributes available for a next run of attr_list. - */ - if (attrlist->al_more == 0) { - break; - } - } - } - - /* - * If we found any xattr send them to the SD. - */ - if (xattr_count > 0) { - /* Serialize the XATTR */ - if (serialize_xattr_stream(jcr, expected_serialize_len, - xattr_value_list) < expected_serialize_len) { - Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* Send the XATTR to the SD. */ - retval = send_xattr_stream(jcr, os_default_xattr_streams[0]); - } else { - retval = bxattr_rtn_ok; - } - -get_out: - free_pool_memory(xattrbuf); - - if (xattr_value_list != NULL) { - xattr_drop_internal_table(xattr_value_list); - } - - return retval; -} - -static bxattr_rtn_code irix_restore_xattr_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - char *bp; - int cnt, cmp_size, name_space_index, flags; - xattr_t *current_xattr; - alist *xattr_value_list; - bxattr_rtn_code retval = bxattr_rtn_error; - - xattr_value_list = New(alist(10, not_owned_by_alist)); - - if (unserialize_xattr_stream(jcr, - content, - content_length, - xattr_value_list) != bxattr_rtn_ok) { - goto get_out; - } - - foreach_alist(current_xattr, xattr_value_list) { - /* - * See to what namingspace this xattr belongs to. - */ - name_space_index = 0; - for (cnt = 0; xattr_naming_spaces[cnt].name != NULL; cnt++) { - cmp_size = strlen(xattr_naming_spaces[cnt].name); - if (!strncasecmp(current_xattr->name, - xattr_naming_spaces[cnt].name, - cmp_size)) { - name_space_index = cnt; - break; - } - } - - /* - * If we got a xattr that doesn't belong to an valid namespace complain. - */ - if (name_space_index == 0) { - Mmsg2(jcr->errmsg, - _("Received invalid xattr named %s on file \"%s\"\n"), - current_xattr->name, jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Restore the xattr first try to create the attribute from scratch. - */ - flags = xattr_naming_spaces[name_space_index].flags | ATTR_CREATE; - bp = strchr(current_xattr->name, '.'); - if (attr_set(jcr->last_fname, ++bp, current_xattr->value, - current_xattr->value_length, flags) != 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - case EEXIST: - /* - * The xattr already exists we need to replace it. - */ - flags = xattr_naming_spaces[name_space_index].flags | ATTR_REPLACE; - if (attr_set(jcr->last_fname, bp, current_xattr->value, - current_xattr->value_length, flags) != 0) { - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg2(jcr->errmsg, _("attr_set error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror(errno)); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - break; - default: - Mmsg2(jcr->errmsg, _("attr_set error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - } - - retval = bxattr_rtn_ok; - -get_out: - xattr_drop_internal_table(xattr_value_list); - - return retval; -} - -/* - * Function pointers to the build and restore function to use for these xattrs. - */ -static bxattr_rtn_code (*os_backup_xattr_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - irix_xattr_build_streams; -static bxattr_rtn_code (*os_restore_xattr_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - irix_restore_xattr_streams; - -#elif defined(HAVE_DARWIN_OS) || \ - defined(HAVE_LINUX_OS) || \ - defined(HAVE_HURD_OS) - -#if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \ - (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \ - (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)) -#error "Missing full support for the XATTR functions." -#endif - -#ifdef HAVE_SYS_XATTR_H -#include -#else -#error "Missing sys/xattr.h header file" -#endif - -/* - * Define the supported XATTR streams for this OS - */ -#if defined(HAVE_DARWIN_OS) -static int os_default_xattr_streams[1] = { - STREAM_XATTR_DARWIN -}; -static const char *xattr_acl_skiplist[2] = { - "com.apple.system.Security", - NULL -}; -static const char *xattr_skiplist[3] = { - "com.apple.system.extendedsecurity", - "com.apple.ResourceFork", - NULL -}; -#elif defined(HAVE_LINUX_OS) -static int os_default_xattr_streams[1] = { - STREAM_XATTR_LINUX -}; -static const char *xattr_acl_skiplist[3] = { - "system.posix_acl_access", - "system.posix_acl_default", - NULL -}; -static const char *xattr_skiplist[1] = { - NULL -}; -#elif defined(HAVE_HURD_OS) -static int os_default_xattr_streams[1] = { - STREAM_XATTR_HURD -}; -static const char *xattr_acl_skiplist[1] = { - NULL -}; -static const char *xattr_skiplist[1] = { - NULL -}; -#endif - -/* - * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has - * listxattr, getxattr and setxattr with an extra options argument - * which mimics the l variants of the functions when we specify - * XATTR_NOFOLLOW as the options value. - */ -#if defined(HAVE_DARWIN_OS) - #define llistxattr(path, list, size) \ - listxattr((path), (list), (size), XATTR_NOFOLLOW) - #define lgetxattr(path, name, value, size) \ - getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW) - #define lsetxattr(path, name, value, size, flags) \ - setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW) -#else - /* - * Fallback to the non l-functions when those are not available. - */ - #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR) - #define lgetxattr getxattr - #endif - #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR) - #define lsetxattr setxattr - #endif - #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR) - #define llistxattr listxattr - #endif -#endif - -static bxattr_rtn_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - char *bp; - bool skip_xattr; - char *xattr_list = NULL; - int cnt, xattr_count = 0; - uint32_t name_length; - int32_t xattr_list_len, - xattr_value_len; - uint32_t expected_serialize_len = 0; - xattr_t *current_xattr; - alist *xattr_value_list = NULL; - bxattr_rtn_code retval = bxattr_rtn_error; - - /* - * First get the length of the available list with extended attributes. - */ - xattr_list_len = llistxattr(jcr->last_fname, NULL, 0); - switch (xattr_list_len) { - case -1: { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - case BXATTR_ENOTSUP: - /* - * If the filesystem reports it doesn't support XATTRs we clear - * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores - * on all other files on the same filesystem. The - * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we - * change from one filesystem to an other. - */ - jcr->xattr_ctx->flags &= ~BXATTR_FLAG_SAVE_NATIVE; - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - } - case 0: - retval = bxattr_rtn_ok; - goto get_out; - default: - break; - } - - /* - * Allocate room for the extented attribute list. - */ - xattr_list = (char *)malloc(xattr_list_len + 1); - memset(xattr_list, 0, xattr_list_len + 1); - - /* - * Get the actual list of extended attributes names for a file. - */ - xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len); - if (xattr_list_len < 0) { - berrno be; - if (errno == ENOENT) { - retval = bxattr_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - xattr_list[xattr_list_len] = '\0'; - - /* - * Walk the list of extended attributes names and retrieve the data. - * We already count the bytes needed for serializing the stream later on. - */ - for (bp = xattr_list; - (bp - xattr_list) + 1 < xattr_list_len; - bp = strchr(bp, '\0') + 1) { - skip_xattr = false; - - /* - * On some OSes you also get the acls in the extented attribute list. - * So we check if we are already backing up acls and if we do we - * don't store the extended attribute with the same info. - */ - if (ff_pkt->flags & FO_ACL) { - for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) { - if (bstrcmp(bp, xattr_acl_skiplist[cnt])) { - skip_xattr = true; - break; - } - } - } - - /* - * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array. - */ - if (!skip_xattr) { - for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) { - if (bstrcmp(bp, xattr_skiplist[cnt])) { - skip_xattr = true; - break; - } - } - } - - name_length = strlen(bp); - if (skip_xattr || name_length == 0) { - Dmsg1(100, "Skipping xattr named %s\n", bp); - continue; - } - - /* - * First see how long the value is for the extended attribute. - */ - xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0); - if (xattr_value_len < 0) { - berrno be; - if (errno == ENOENT) { - retval = bxattr_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - - /* - * Each xattr valuepair starts with a magic so we can restore it easier. - */ - current_xattr = (xattr_t *)malloc(sizeof(xattr_t)); - current_xattr->magic = XATTR_MAGIC; - current_xattr->value = NULL; - expected_serialize_len += sizeof(current_xattr->magic); - - /* - * Allocate space for storing the name. - */ - current_xattr->name_length = name_length; - current_xattr->name = (char *)malloc(current_xattr->name_length); - memcpy(current_xattr->name, bp, current_xattr->name_length); - - expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length; - - switch (xattr_value_len) { - case 0: - current_xattr->value = NULL; - current_xattr->value_length = 0; - expected_serialize_len += sizeof(current_xattr->value_length); - break; - default: - /* - * Allocate space for storing the value. - */ - current_xattr->value = (char *)malloc(xattr_value_len); - memset(current_xattr->value, 0, xattr_value_len); - - xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len); - if (xattr_value_len < 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - break; - default: - Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - break; - } - - /* - * Default failure path out when retrieval of attr fails. - */ - free(current_xattr->value); - free(current_xattr->name); - free(current_xattr); - goto get_out; - } - - /* - * Store the actual length of the value. - */ - current_xattr->value_length = xattr_value_len; - expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length; - break; - } - - if (xattr_value_list == NULL) { - xattr_value_list = New(alist(10, not_owned_by_alist)); - } - - xattr_value_list->append(current_xattr); - xattr_count++; - - /* - * Protect ourself against things getting out of hand. - */ - if (expected_serialize_len >= MAX_XATTR_LENGTH) { - Mmsg2(jcr->errmsg, - _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"), - jcr->last_fname, MAX_XATTR_LENGTH); - goto get_out; - } - } - - free(xattr_list); - xattr_list = (char *)NULL; - - /* - * If we found any xattr send them to the SD. - */ - if (xattr_count > 0) { - /* - * Serialize the datastream. - */ - if (serialize_xattr_stream(jcr, expected_serialize_len, - xattr_value_list) < expected_serialize_len) { - Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Send the datastream to the SD. - */ - retval = send_xattr_stream(jcr, os_default_xattr_streams[0]); - } else { - retval = bxattr_rtn_ok; - } - -get_out: - if (xattr_list != NULL) { - free(xattr_list); - } - if (xattr_value_list != NULL) { - xattr_drop_internal_table(xattr_value_list); - } - - return retval; -} - -static bxattr_rtn_code generic_restore_xattr_streams(JCR *jcr, int stream, - char *content, - uint32_t content_length) -{ - xattr_t *current_xattr; - alist *xattr_value_list; - bxattr_rtn_code retval = bxattr_rtn_error; - - xattr_value_list = New(alist(10, not_owned_by_alist)); - - if (unserialize_xattr_stream(jcr, content, content_length, - xattr_value_list) != bxattr_rtn_ok) { - goto get_out; - } - - foreach_alist(current_xattr, xattr_value_list) { - if (lsetxattr(jcr->last_fname, current_xattr->name, current_xattr->value, current_xattr->value_length, 0) != 0) { - berrno be; - if (errno == ENOENT) { - goto get_out; - } else if (errno == BXATTR_ENOTSUP) { - /* - * If the filesystem reports it doesn't support XATTRs we clear - * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores - * on all other files on the same filesystem. The - * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we - * change from one filesystem to an other. - */ - jcr->xattr_ctx->flags &= ~BXATTR_FLAG_RESTORE_NATIVE; - } - MmsgD2(100, jcr->errmsg, - _("lsetxattr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - goto get_out; - } - } - - retval = bxattr_rtn_ok; - -get_out: - xattr_drop_internal_table(xattr_value_list); - return retval; -} - -/* - * Function pointers to the build and restore functions to use for these xattrs. - */ -static bxattr_rtn_code (*os_backup_xattr_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - generic_xattr_build_streams; -static bxattr_rtn_code (*os_restore_xattr_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - generic_restore_xattr_streams; - -#elif defined(HAVE_FREEBSD_OS) || \ - defined(HAVE_NETBSD_OS) || \ - defined(HAVE_OPENBSD_OS) - -#if (!defined(HAVE_EXTATTR_GET_LINK) && !defined(HAVE_EXTATTR_GET_FILE)) || \ - (!defined(HAVE_EXTATTR_SET_LINK) && !defined(HAVE_EXTATTR_SET_FILE)) || \ - (!defined(HAVE_EXTATTR_LIST_LINK) && !defined(HAVE_EXTATTR_LIST_FILE)) || \ - !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \ - !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE) -#error "Missing full support for the extattr functions." -#endif - -#ifdef HAVE_SYS_EXTATTR_H -#include -#else -#error "Missing sys/extattr.h header file" -#endif - -#ifdef HAVE_LIBUTIL_H -#include -#endif - -#if !defined(HAVE_EXTATTR_GET_LINK) && defined(HAVE_EXTATTR_GET_FILE) -#define extattr_get_link extattr_get_file -#endif -#if !defined(HAVE_EXTATTR_SET_LINK) && defined(HAVE_EXTATTR_SET_FILE) -#define extattr_set_link extattr_set_file -#endif -#if !defined(HAVE_EXTATTR_LIST_LINK) && defined(HAVE_EXTATTR_LIST_FILE) -#define extattr_list_link extattr_list_file -#endif - -#if defined(HAVE_FREEBSD_OS) -static int os_default_xattr_streams[1] = { - STREAM_XATTR_FREEBSD -}; -static int os_default_xattr_namespaces[2] = { - EXTATTR_NAMESPACE_USER, - EXTATTR_NAMESPACE_SYSTEM -}; -static const char *xattr_acl_skiplist[4] = { - "system.posix1e.acl_access", - "system.posix1e.acl_default", - "system.nfs4.acl", - NULL -}; -static const char *xattr_skiplist[1] = { - NULL -}; -#elif defined(HAVE_NETBSD_OS) -static int os_default_xattr_streams[1] = { - STREAM_XATTR_NETBSD -}; -static int os_default_xattr_namespaces[2] = { - EXTATTR_NAMESPACE_USER, - EXTATTR_NAMESPACE_SYSTEM -}; -static const char *xattr_acl_skiplist[1] = { - NULL -}; -static const char *xattr_skiplist[1] = { - NULL -}; -#elif defined(HAVE_OPENBSD_OS) -static int os_default_xattr_streams[1] = { - STREAM_XATTR_OPENBSD -}; -static int os_default_xattr_namespaces[2] = { - EXTATTR_NAMESPACE_USER, - EXTATTR_NAMESPACE_SYSTEM -}; -static const char *xattr_acl_skiplist[1] = { - NULL -}; -static const char *xattr_skiplist[1] = { - NULL -}; -#endif - -static bxattr_rtn_code bsd_backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - bool skip_xattr; - char *xattr_list = NULL; - int cnt, index, xattr_count = 0; - int32_t xattr_list_len, - xattr_value_len; - uint32_t expected_serialize_len = 0; - unsigned int namespace_index; - int attrnamespace; - char *current_attrnamespace = NULL; - char current_attrname[XATTR_BUFSIZ], current_attrtuple[XATTR_BUFSIZ]; - xattr_t *current_xattr; - alist *xattr_value_list = NULL; - bxattr_rtn_code retval = bxattr_rtn_error; - - /* - * Loop over all available xattr namespaces. - */ - for (namespace_index = 0; - namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int); - namespace_index++) { - attrnamespace = os_default_xattr_namespaces[namespace_index]; - - /* - * First get the length of the available list with extended attributes. - * If we get EPERM on system namespace, don't return error. - * This is expected for normal users trying to archive the system - * namespace on FreeBSD 6.2 and later. On NetBSD 3.1 and later, - * they've decided to return EOPNOTSUPP instead. - */ - xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, NULL, 0); - switch (xattr_list_len) { - case -1: { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; -#if defined(EOPNOTSUPP) - case EOPNOTSUPP: -#endif - case EPERM: - if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM) { - continue; - } - /* - * FALLTHROUGH - */ - default: - Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - } - case 0: - continue; - default: - break; - } - - /* - * Allocate room for the extented attribute list. - */ - xattr_list = (char *)malloc(xattr_list_len + 1); - memset(xattr_list, 0, xattr_list_len + 1); - - /* - * Get the actual list of extended attributes names for a file. - */ - xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, - xattr_list, xattr_list_len); - if (xattr_list_len < 0) { - berrno be; - if (errno == ENOENT) { - retval = bxattr_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - xattr_list[xattr_list_len] = '\0'; - - /* - * Convert the numeric attrnamespace into a string representation and make - * a private copy of that string. The extattr_namespace_to_string functions - * returns a strdupped string which we need to free. - */ - if (extattr_namespace_to_string(attrnamespace, ¤t_attrnamespace) != 0) { - Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"), - attrnamespace, jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Walk the list of extended attributes names and retrieve the data. - * We already count the bytes needed for serializing the stream later on. - */ - for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) { - skip_xattr = false; - - /* - * Print the current name into the buffer as its not null terminated - * we need to use the length encoded in the string for copying only - * the needed bytes. - */ - cnt = xattr_list[index]; - if (cnt > ((int)sizeof(current_attrname) - 1)) { - cnt = ((int)sizeof(current_attrname) - 1); - } - strncpy(current_attrname, xattr_list + (index + 1), cnt); - current_attrname[cnt] = '\0'; - - /* - * First make a xattr tuple of the current namespace and the name of - * the xattr. e.g. something like user. or system. - */ - bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s", - current_attrnamespace, current_attrname); - - /* - * On some OSes you also get the acls in the extented attribute list. - * So we check if we are already backing up acls and if we do we - * don't store the extended attribute with the same info. - */ - if (ff_pkt->flags & FO_ACL) { - for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) { - if (bstrcmp(current_attrtuple, xattr_acl_skiplist[cnt])) { - skip_xattr = true; - break; - } - } - } - - /* - * On some OSes we want to skip certain xattrs which are in the - * xattr_skiplist array. - */ - if (!skip_xattr) { - for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) { - if (bstrcmp(current_attrtuple, xattr_skiplist[cnt])) { - skip_xattr = true; - break; - } - } - } - - if (skip_xattr) { - Dmsg1(100, "Skipping xattr named %s\n", current_attrname); - continue; - } - - /* - * First see how long the value is for the extended attribute. - */ - xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, - current_attrname, NULL, 0); - switch (xattr_value_len) { - case -1: { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - } - default: - break; - } - - /* - * Each xattr valuepair starts with a magic so we can restore it easier. - */ - current_xattr = (xattr_t *)malloc(sizeof(xattr_t)); - memset(current_xattr, 0, sizeof(xattr_t)); - current_xattr->magic = XATTR_MAGIC; - expected_serialize_len += sizeof(current_xattr->magic); - - /* - * Allocate space for storing the name. - */ - current_xattr->name_length = strlen(current_attrtuple); - current_xattr->name = (char *)malloc(current_xattr->name_length); - memcpy(current_xattr->name, current_attrtuple, current_xattr->name_length); - - expected_serialize_len += sizeof(current_xattr->name_length) + - current_xattr->name_length; - - switch (xattr_value_len) { - case 0: - current_xattr->value = NULL; - current_xattr->value_length = 0; - expected_serialize_len += sizeof(current_xattr->value_length); - break; - default: - /* - * Allocate space for storing the value. - */ - current_xattr->value = (char *)malloc(xattr_value_len); - memset(current_xattr->value, 0, xattr_value_len); - - xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, - current_attrname, current_xattr->value, - xattr_value_len); - if (xattr_value_len < 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - break; - default: - Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - break; - } - - /* - * Default failure path out when retrieval of attr fails. - */ - free(current_xattr->value); - free(current_xattr->name); - free(current_xattr); - goto get_out; - } - - /* - * Store the actual length of the value. - */ - current_xattr->value_length = xattr_value_len; - expected_serialize_len += sizeof(current_xattr->value_length) + - current_xattr->value_length; - break; - } - - if (xattr_value_list == NULL) { - xattr_value_list = New(alist(10, not_owned_by_alist)); - } - - xattr_value_list->append(current_xattr); - xattr_count++; - - /* - * Protect ourself against things getting out of hand. - */ - if (expected_serialize_len >= MAX_XATTR_LENGTH) { - Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"), - jcr->last_fname, MAX_XATTR_LENGTH); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - /* - * Drop the local copy of the current_attrnamespace. - */ - actuallyfree(current_attrnamespace); - current_attrnamespace = NULL; - - /* - * We are done with this xattr list. - */ - free(xattr_list); - xattr_list = (char *)NULL; - } - - /* - * If we found any xattr send them to the SD. - */ - if (xattr_count > 0) { - /* - * Serialize the datastream. - */ - if (serialize_xattr_stream(jcr, - expected_serialize_len, - xattr_value_list) < expected_serialize_len) { - Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Send the datastream to the SD. - */ - retval = send_xattr_stream(jcr, os_default_xattr_streams[0]); - } else { - retval = bxattr_rtn_ok; - } - -get_out: - if (current_attrnamespace != NULL) { - actuallyfree(current_attrnamespace); - } - if (xattr_list != NULL) { - free(xattr_list); - } - if (xattr_value_list != NULL) { - xattr_drop_internal_table(xattr_value_list); - } - - return retval; -} - -static bxattr_rtn_code bsd_restore_xattr_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - xattr_t *current_xattr; - alist *xattr_value_list; - int current_attrnamespace, cnt; - char *attrnamespace, *attrname; - bxattr_rtn_code retval = bxattr_rtn_error; - - xattr_value_list = New(alist(10, not_owned_by_alist)); - - if (unserialize_xattr_stream(jcr, - content, - content_length, - xattr_value_list) != bxattr_rtn_ok) { - goto get_out; - } - - foreach_alist(current_xattr, xattr_value_list) { - /* - * Try splitting the xattr_name into a namespace and name part. - * The splitting character is a . - */ - attrnamespace = current_xattr->name; - if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) { - Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"), - current_xattr->name, jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - *attrname++ = '\0'; - - /* - * Make sure the attrnamespace makes sense. - */ - if (extattr_string_to_namespace(attrnamespace, ¤t_attrnamespace) != 0) { - Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"), - attrnamespace, jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Try restoring the extended attribute. - */ - cnt = extattr_set_link(jcr->last_fname, current_attrnamespace, - attrname, current_xattr->value, current_xattr->value_length); - if (cnt < 0 || cnt != (int)current_xattr->value_length) { - berrno be; - if (errno == ENOENT) { - goto get_out; - } else { - Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - } - - retval = bxattr_rtn_ok; - -get_out: - xattr_drop_internal_table(xattr_value_list); - return retval; -} - -/* - * Function pointers to the build and restore function to use for these xattrs. - */ -static bxattr_rtn_code (*os_backup_xattr_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - bsd_backup_xattr_streams; -static bxattr_rtn_code (*os_restore_xattr_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - bsd_restore_xattr_streams; - -#elif defined(HAVE_OSF1_OS) - -#if !defined(HAVE_GETPROPLIST) || \ - !defined(HAVE_GET_PROPLIST_ENTRY) || \ - !defined(HAVE_SIZEOF_PROPLIST_ENTRY) || \ - !defined(HAVE_ADD_PROPLIST_ENTRY) || \ - !defined(HAVE_SETPROPLIST) -#error "Missing full support for the Extended Attributes functions." -#endif - -#ifdef HAVE_SYS_PROPLIST_H -#include -#else -#error "Missing sys/proplist.h header file" -#endif - -/* - * Define the supported XATTR streams for this OS - */ -static int os_default_xattr_streams[1] = { - STREAM_XATTR_TRU64 -}; -static const char *xattr_acl_skiplist[1] = { - NULL -}; -static const char *xattr_skiplist[1] = { - NULL -}; - -static bxattr_rtn_code tru64_backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - int cnt; - char *bp, - *xattr_name, - *xattr_value; - bool skip_xattr; - int xattr_count = 0; - int32_t *flags, - *xattr_value_len; - int32_t xattr_list_len, - xattrbuf_size, - xattrbuf_min_size; - uint32_t expected_serialize_len = 0; - xattr_t *current_xattr; - alist *xattr_value_list = NULL; - struct proplistname_args prop_args; - bxattr_rtn_code retval = bxattr_rtn_error; - POOLMEM *xattrbuf = get_pool_memory(PM_MESSAGE); - - xattrbuf_size = sizeof_pool_memory(xattrbuf); - xattrbuf_min_size = 0; - xattr_list_len = getproplist(jcr->last_fname, 1, &prop_args, xattrbuf_size, - xattrbuf, &xattrbuf_min_size); - - /* - * See what xattr are available. - */ - switch (xattr_list_len) { - case -1: { - berrno be; - - switch (errno) { - case EOPNOTSUPP: - /* - * If the filesystem reports it doesn't support XATTRs we clear - * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores - * on all other files on the same filesystem. The - * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we - * change from one filesystem to an other. - */ - jcr->xattr_ctx->flags &= ~BXATTR_FLAG_SAVE_NATIVE; - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg2(jcr->errmsg, _("getproplist error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - } - case 0: - if (xattrbuf_min_size) { - /* - * The buffer isn't big enough to hold the xattr data, we now have - * a minimum buffersize so we resize the buffer and try again. - */ - xattrbuf = check_pool_memory_size(xattrbuf, xattrbuf_min_size + 1); - xattrbuf_size = xattrbuf_min_size + 1; - xattr_list_len = getproplist(jcr->last_fname, 1, &prop_args, xattrbuf_size, - xattrbuf, &xattrbuf_min_size); - switch (xattr_list_len) { - case -1: { - berrno be; - - switch (errno) { - default: - Mmsg2(jcr->errmsg, _("getproplist error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - } - case 0: - /* - * This should never happen as we sized the buffer according to the minimumsize - * returned by a previous getproplist call. If it does happen things are fishy and - * we are better of forgetting this xattr as it seems its list is changing at this - * exact moment so we can never make a good backup copy of it. - */ - retval = bxattr_rtn_ok; - goto get_out; - default: - break; - } - } else { - /* - * No xattr on file. - */ - retval = bxattr_rtn_ok; - goto get_out; - } - break; - default: - break; - } - - /* - * Walk the list of extended attributes names and retrieve the data. - * We already count the bytes needed for serializing the stream later on. - */ - bp = xattrbuf; - while (xattrbuf_size > 0) { - /* - * Call getproplist_entry to initialize name and value - * pointers to entries position within buffer. - */ - xattrbuf_size -= get_proplist_entry(&xattr_name, &flags, &xattr_value_len, &xattr_value, &bp); - - /* - * On some OSes you also get the acls in the extented attribute list. - * So we check if we are already backing up acls and if we do we - * don't store the extended attribute with the same info. - */ - if (ff_pkt->flags & FO_ACL) { - for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) { - if (bstrcmp(xattr_name, xattr_acl_skiplist[cnt])) { - skip_xattr = true; - break; - } - } - } - - /* - * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array. - */ - if (!skip_xattr) { - for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) { - if (bstrcmp(xattr_name, xattr_skiplist[cnt])) { - skip_xattr = true; - break; - } - } - } - - if (skip_xattr) { - Dmsg1(100, "Skipping xattr named %s\n", xattr_name); - continue; - } - - /* - * Each xattr valuepair starts with a magic so we can restore it easier. - */ - current_xattr = (xattr_t *)malloc(sizeof(xattr_t)); - memset(current_xattr, 0, sizeof(xattr_t)); - current_xattr->magic = XATTR_MAGIC; - expected_serialize_len += sizeof(current_xattr->magic); - - current_xattr->name_length = strlen(xattr_name); - current_xattr->name = bstrdup(xattr_name); - - expected_serialize_len += sizeof(current_xattr->name_length) + - current_xattr->name_length; - - current_xattr->value_length = *xattr_value_len; - current_xattr->value = (char *)malloc(current_xattr->value_length); - memcpy(current_xattr->value, xattr_value, current_xattr->value_length); - - expected_serialize_len += sizeof(current_xattr->value_length) + - current_xattr->value_length; - - if (xattr_value_list == NULL) { - xattr_value_list = New(alist(10, not_owned_by_alist)); - } - - xattr_value_list->append(current_xattr); - xattr_count++; - - /* - * Protect ourself against things getting out of hand. - */ - if (expected_serialize_len >= MAX_XATTR_LENGTH) { - Mmsg2(jcr->errmsg, - _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"), - jcr->last_fname, MAX_XATTR_LENGTH); - goto get_out; - } - } - - /* - * If we found any xattr send them to the SD. - */ - if (xattr_count > 0) { - /* - * Serialize the datastream. - */ - if (serialize_xattr_stream(jcr, - expected_serialize_len, - xattr_value_list) < expected_serialize_len) { - Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Send the datastream to the SD. - */ - retval = send_xattr_stream(jcr, os_default_xattr_streams[0]); - } else { - retval = bxattr_rtn_ok; - } - -get_out: - if (xattr_value_list != NULL) { - xattr_drop_internal_table(xattr_value_list); - } - free_pool_memory(xattrbuf); - - return retval; -} - -static bxattr_rtn_code tru64_restore_xattr_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - char *bp, *xattrbuf = NULL; - int32_t xattrbuf_size, cnt; - xattr_t *current_xattr; - alist *xattr_value_list; - bxattr_rtn_code retval = bxattr_rtn_error; - - xattr_value_list = New(alist(10, not_owned_by_alist)); - - if (unserialize_xattr_stream(jcr, - content, - content_length, - xattr_value_list) != bxattr_rtn_ok) { - goto get_out; - } - - /* - * See how big the propertylist must be. - */ - xattrbuf_size = 0; - foreach_alist(current_xattr, xattr_value_list) { - xattrbuf_size += sizeof_proplist_entry(current_xattr->name, current_xattr->value_length); - } - - xattrbuf = (char *)malloc(xattrbuf_size); - - /* - * Add all value pairs to the proplist. - */ - cnt = 0; - bp = xattrbuf; - foreach_alist(current_xattr, xattr_value_list) { - cnt = add_proplist_entry(current_xattr->name, 0, current_xattr->value_length, - current_xattr->value, &bp); - } - - /* - * Sanity check. - */ - if (cnt != xattrbuf_size) { - Mmsg1(jcr->errmsg, _("Unable create proper proplist to restore xattrs on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Restore the list of extended attributes on the file. - */ - cnt = setproplist(jcr->last_fname, 1, xattrbuf_size, xattrbuf); - switch (cnt) { - case -1: { - berrno be; - - switch (errno) { - case EOPNOTSUPP: - /* - * If the filesystem reports it doesn't support XATTRs we clear - * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores - * on all other files on the same filesystem. The - * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we - * change from one filesystem to an other. - */ - jcr->xattr_ctx->flags &= ~BXATTR_FLAG_RESTORE_NATIVE; - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg2(jcr->errmsg, _("setproplist error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - } - default: - break; - } - - retval = bxattr_rtn_ok; - -get_out: - if (xattrbuf) { - free(xattrbuf); - } - xattr_drop_internal_table(xattr_value_list); - - return retval; -} - -/* - * Function pointers to the build and restore function to use for these xattrs. - */ -static bxattr_rtn_code (*os_backup_xattr_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - tru64_backup_xattr_streams; -static bxattr_rtn_code (*os_restore_xattr_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - tru64_restore_xattr_streams; - -#elif defined(HAVE_SUN_OS) -/* - * Solaris extended attributes were introduced in Solaris 9 - * by PSARC 1999/209 - * - * Solaris extensible attributes were introduced in OpenSolaris - * by PSARC 2007/315 Solaris extensible attributes are also - * sometimes called extended system attributes. - * - * man fsattr(5) on Solaris gives a wealth of info. The most - * important bits are: - * - * Attributes are logically supported as files within the file - * system. The file system is therefore augmented with an - * orthogonal name space of file attributes. Any file (includ- - * ing attribute files) can have an arbitrarily deep attribute - * tree associated with it. Attribute values are accessed by - * file descriptors obtained through a special attribute inter- - * face. This logical view of "attributes as files" allows the - * leveraging of existing file system interface functionality - * to support the construction, deletion, and manipulation of - * attributes. - * - * The special files "." and ".." retain their accustomed - * semantics within the attribute hierarchy. The "." attribute - * file refers to the current directory and the ".." attribute - * file refers to the parent directory. The unnamed directory - * at the head of each attribute tree is considered the "child" - * of the file it is associated with and the ".." file refers - * to the associated file. For any non-directory file with - * attributes, the ".." entry in the unnamed directory refers - * to a file that is not a directory. - * - * Conceptually, the attribute model is fully general. Extended - * attributes can be any type of file (doors, links, direc- - * tories, and so forth) and can even have their own attributes - * (fully recursive). As a result, the attributes associated - * with a file could be an arbitrarily deep directory hierarchy - * where each attribute could have an equally complex attribute - * tree associated with it. Not all implementations are able - * to, or want to, support the full model. Implementation are - * therefore permitted to reject operations that are not sup- - * ported. For example, the implementation for the UFS file - * system allows only regular files as attributes (for example, - * no sub-directories) and rejects attempts to place attributes - * on attributes. - * - * The following list details the operations that are rejected - * in the current implementation: - * - * link Any attempt to create links between - * attribute and non-attribute space - * is rejected to prevent security- - * related or otherwise sensitive - * attributes from being exposed, and - * therefore manipulable, as regular - * files. - * - * rename Any attempt to rename between - * attribute and non-attribute space - * is rejected to prevent an already - * linked file from being renamed and - * thereby circumventing the link res- - * triction above. - * - * mkdir, symlink, mknod Any attempt to create a "non- - * regular" file in attribute space is - * rejected to reduce the functional- - * ity, and therefore exposure and - * risk, of the initial implementa- - * tion. - * - * The entire available name space has been allocated to "gen- - * eral use" to bring the implementation in line with the NFSv4 - * draft standard [NFSv4]. That standard defines "named attri- - * butes" (equivalent to Solaris Extended Attributes) with no - * naming restrictions. All Sun applications making use of - * opaque extended attributes will use the prefix "SUNW". - * - */ -#ifdef HAVE_SYS_ATTR_H -#include -#endif - -#ifdef HAVE_ATTR_H -#include -#endif - -#ifdef HAVE_SYS_NVPAIR_H -#include -#endif - -#ifdef HAVE_SYS_ACL_H -#include -#endif - -#if !defined(HAVE_OPENAT) || \ - !defined(HAVE_UNLINKAT) || \ - !defined(HAVE_FCHOWNAT) || \ - !defined(HAVE_FUTIMESAT) -#error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function" -#endif - -/* - * Define the supported XATTR streams for this OS - */ -#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) -static int os_default_xattr_streams[2] = { - STREAM_XATTR_SOLARIS, - STREAM_XATTR_SOLARIS_SYS -}; -#else -static int os_default_xattr_streams[1] = { - STREAM_XATTR_SOLARIS -}; -#endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */ - -/* - * This code creates a temporary cache with entries for each xattr which has - * a link count > 1 (which indicates it has one or more hard linked counterpart(s)) - */ -static inline xattr_link_cache_entry_t *find_xattr_link_cache_entry(JCR *jcr, ino_t inum) -{ - xattr_link_cache_entry_t *ptr; - - foreach_alist(ptr, jcr->xattr_ctx->link_cache) { - if (ptr && ptr->inum == inum) { - return ptr; - } - } - return NULL; -} - -static inline void add_xattr_link_cache_entry(JCR *jcr, ino_t inum, char *target) -{ - xattr_link_cache_entry_t *ptr; - - ptr = (xattr_link_cache_entry_t *)malloc(sizeof(xattr_link_cache_entry_t)); - memset(ptr, 0, sizeof(xattr_link_cache_entry_t)); - ptr->inum = inum; - ptr->target = bstrdup(target); - - if (!jcr->xattr_ctx->link_cache) { - jcr->xattr_ctx->link_cache = New(alist(10, not_owned_by_alist)); - } - jcr->xattr_ctx->link_cache->append(ptr); -} - -static inline void drop_xattr_link_cache(JCR *jcr) -{ - xattr_link_cache_entry_t *ptr; - - /* - * Walk the list of xattr link cache entries and free allocated memory on traversing. - */ - foreach_alist(ptr, jcr->xattr_ctx->link_cache) { - free(ptr->target); - free(ptr); - } - - delete jcr->xattr_ctx->link_cache; - jcr->xattr_ctx->link_cache = NULL; -} - -#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) -/* - * This function returns true if a non default extended system attribute - * list is associated with fd and returns false when an error has occured - * or when only extended system attributes other than archive, - * av_modified or crtime are set. - * - * The function returns true for the following cases: - * - * - any extended system attribute other than the default attributes - * ('archive', 'av_modified' and 'crtime') is set - * - nvlist has NULL name string - * - nvpair has data type of 'nvlist' - * - default data type. - */ -static bool solaris_has_non_transient_extensible_attributes(int fd) -{ - boolean_t value; - data_type_t type; - nvlist_t *response; - nvpair_t *pair; - f_attr_t fattr; - char *name; - bool retval = false; - - if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) { - return false; - } - - pair = NULL; - while ((pair = nvlist_next_nvpair(response, pair)) != NULL) { - name = nvpair_name(pair); - - if (name != NULL) { - fattr = name_to_attr(name); - } else { - retval = true; - goto get_out; - } - - type = nvpair_type(pair); - switch (type) { - case DATA_TYPE_BOOLEAN_VALUE: - if (nvpair_value_boolean_value(pair, &value) != 0) { - continue; - } - if (value && fattr != F_ARCHIVE && - fattr != F_AV_MODIFIED) { - retval = true; - goto get_out; - } - break; - case DATA_TYPE_UINT64_ARRAY: - if (fattr != F_CRTIME) { - retval = true; - goto get_out; - } - break; - case DATA_TYPE_NVLIST: - default: - retval = true; - goto get_out; - } - } - -get_out: - if (response != NULL) { - nvlist_free(response); - } - return retval; -} -#endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */ - -#if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL) -/* - * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.) - * There is no need to store those acls as we already store the stat bits too. - */ -static bool acl_is_trivial(int count, aclent_t *entries) -{ - int n; - aclent_t *ace; - - for (n = 0; n < count; n++) { - ace = &entries[n]; - if (!(ace->a_type == USER_OBJ || - ace->a_type == GROUP_OBJ || - ace->a_type == OTHER_OBJ || - ace->a_type == CLASS_OBJ)) - return false; - } - return true; -} -#endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */ - -static bxattr_rtn_code solaris_save_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text) -{ - bxattr_rtn_code retval = bxattr_rtn_error; -#ifdef HAVE_ACL -#ifdef HAVE_EXTENDED_ACL - int flags; - acl_t *aclp = NULL; - - /* - * See if this attribute has an ACL - */ - if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) || - pathconf(attrname, _PC_ACL_ENABLED) > 0) { - /* - * See if there is a non trivial acl on the file. - */ - if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) || - acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"), - attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - if (aclp != NULL) { -#if defined(ACL_SID_FMT) - /* - * New format flag added in newer Solaris versions. - */ - flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT; -#else - flags = ACL_APPEND_ID | ACL_COMPACT_FMT; -#endif /* ACL_SID_FMT */ - - *acl_text = acl_totext(aclp, flags); - acl_free(aclp); - } else { - *acl_text = NULL; - } - } else { - *acl_text = NULL; - } - retval = bxattr_rtn_ok; -#else /* HAVE_EXTENDED_ACL */ - int n; - aclent_t *acls = NULL; - - /* - * See if this attribute has an ACL - */ - if (fd != -1) { - n = facl(fd, GETACLCNT, 0, NULL); - } else { - n = acl(attrname, GETACLCNT, 0, NULL); - } - - if (n >= MIN_ACL_ENTRIES) { - acls = (aclent_t *)malloc(n * sizeof(aclent_t)); - if ((fd != -1 && facl(fd, GETACL, n, acls) != n) || - acl(attrname, GETACL, n, acls) != n) { - berrno be; - - switch (errno) { - case ENOENT: - free(acls); - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"), - attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - free(acls); - goto get_out; - } - } - - /* - * See if there is a non trivial acl on the file. - */ - if (!acl_is_trivial(n, acls)) { - if ((*acl_text = acltotext(acls, n)) == NULL) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"), - attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - free(acls); - goto get_out; - } - } else { - *acl_text = NULL; - } - - free(acls); - } else { - *acl_text = NULL; - } - retval = bxattr_rtn_ok; -#endif /* HAVE_EXTENDED_ACL */ - -#else /* HAVE_ACL */ - retval = bxattr_rtn_ok; -#endif /* HAVE_ACL */ - -get_out: - return retval; -} - -/* - * Forward declaration for recursive function call. - */ -static bxattr_rtn_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent); - -/* - * Save an extended or extensible attribute. - * This is stored as an opaque stream of bytes with the following encoding: - * - * \0\0\0 - * - * or for a hardlinked or symlinked attribute - * - * \0\0\0 - * - * xattr_name can be a subpath relative to the file the xattr is on. - * stat_buffer is the string representation of the stat struct. - * acl_string is an acl text when a non trivial acl is set on the xattr. - * actual_xattr_ctx is the content of the xattr file. - */ -static bxattr_rtn_code solaris_save_xattr(JCR *jcr, int fd, const char *xattr_namespace, - const char *attrname, bool toplevel_hidden_dir, int stream) -{ - int cnt; - int attrfd = -1; - struct stat st; - xattr_link_cache_entry_t *xlce; - char target_attrname[PATH_MAX]; - char link_source[PATH_MAX]; - char *acl_text = NULL; - char attribs[MAXSTRING]; - char buffer[XATTR_BUFSIZ]; - bxattr_rtn_code retval = bxattr_rtn_error; - - bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname); - - /* - * Get the stats of the extended or extensible attribute. - */ - if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg3(jcr->errmsg, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - /* - * Based on the filetype perform the correct action. We support most filetypes here, more - * then the actual implementation on Solaris supports so some code may never get executed - * due to limitations in the implementation. - */ - switch (st.st_mode & S_IFMT) { - case S_IFIFO: - case S_IFCHR: - case S_IFBLK: - /* - * Get any acl on the xattr. - */ - if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_rtn_ok) - goto get_out; - - /* - * The current implementation of xattr on Solaris doesn't support this, - * but if it ever does we are prepared. - * Encode the stat struct into an ASCII representation. - */ - encode_stat(attribs, &st, sizeof(st), 0, stream); - cnt = bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c", - target_attrname, 0, attribs, 0, - (acl_text) ? acl_text : "", 0); - break; - case S_IFDIR: - /* - * Get any acl on the xattr. - */ - if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_rtn_ok) - goto get_out; - - /* - * See if this is the toplevel_hidden_dir being saved. - */ - if (toplevel_hidden_dir) { - /* - * Save the data for later storage when we encounter a real xattr. - * We store the data in the jcr->xattr_ctx->content buffer - * and flush that just before sending out the first real xattr. - * Encode the stat struct into an ASCII representation and jump - * out of the function. - */ - encode_stat(attribs, &st, sizeof(st), 0, stream); - cnt = bsnprintf(buffer, sizeof(buffer), - "%s%c%s%c%s%c", - target_attrname, 0, attribs, 0, - (acl_text) ? acl_text : "", 0); - pm_memcpy(jcr->xattr_ctx->content, buffer, cnt); - jcr->xattr_ctx->content_length = cnt; - goto get_out; - } else { - /* - * The current implementation of xattr on Solaris doesn't support this, - * but if it ever does we are prepared. - * Encode the stat struct into an ASCII representation. - */ - encode_stat(attribs, &st, sizeof(st), 0, stream); - cnt = bsnprintf(buffer, sizeof(buffer), - "%s%c%s%c%s%c", - target_attrname, 0, attribs, 0, - (acl_text) ? acl_text : "", 0); - } - break; - case S_IFREG: - /* - * If this is a hardlinked file check the inode cache for a hit. - */ - if (st.st_nlink > 1) { - /* - * See if the cache already knows this inode number. - */ - if ((xlce = find_xattr_link_cache_entry(jcr, st.st_ino)) != NULL) { - /* - * Generate a xattr encoding with the reference to the target in there. - */ - encode_stat(attribs, &st, sizeof(st), st.st_ino, stream); - cnt = bsnprintf(buffer, sizeof(buffer), - "%s%c%s%c%s%c", - target_attrname, 0, attribs, 0, xlce->target, 0); - pm_memcpy(jcr->xattr_ctx->content, buffer, cnt); - jcr->xattr_ctx->content_length = cnt; - retval = send_xattr_stream(jcr, stream); - - /* - * For a hard linked file we are ready now, no need to recursively - * save the attributes. - */ - goto get_out; - } - - /* - * Store this hard linked file in the cache. - * Store the name relative to the top level xattr space. - */ - add_xattr_link_cache_entry(jcr, st.st_ino, target_attrname + 1); - } - - /* - * Get any acl on the xattr. - */ - if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_rtn_ok) { - goto get_out; - } - - /* - * Encode the stat struct into an ASCII representation. - */ - encode_stat(attribs, &st, sizeof(st), 0, stream); - cnt = bsnprintf(buffer, sizeof(buffer), - "%s%c%s%c%s%c", - target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0); - - /* - * Open the extended or extensible attribute file. - */ - if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - break; - case S_IFLNK: - /* - * The current implementation of xattr on Solaris doesn't support this, but if it - * ever does we are prepared. - * Encode the stat struct into an ASCII representation. - */ - if (readlink(attrname, link_source, sizeof(link_source)) < 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg3(jcr->errmsg, _("Unable to read symlin %s on \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - /* - * Generate a xattr encoding with the reference to the target in there. - */ - encode_stat(attribs, &st, sizeof(st), st.st_ino, stream); - cnt = bsnprintf(buffer, sizeof(buffer), - "%s%c%s%c%s%c", - target_attrname, 0, attribs, 0, link_source, 0); - pm_memcpy(jcr->xattr_ctx->content, buffer, cnt); - jcr->xattr_ctx->content_length = cnt; - retval = send_xattr_stream(jcr, stream); - - if (retval == bxattr_rtn_ok) { - jcr->xattr_ctx->nr_saved++; - } - - /* - * For a soft linked file we are ready now, no need to recursively save the attributes. - */ - goto get_out; - default: - goto get_out; - } - - /* - * See if this is the first real xattr being saved. - * If it is save the toplevel_hidden_dir attributes first. - * This is easy as its stored already in the - * jcr->xattr_ctx->content buffer. - */ - if (jcr->xattr_ctx->nr_saved == 0) { - retval = send_xattr_stream(jcr, STREAM_XATTR_SOLARIS); - if (retval != bxattr_rtn_ok) { - goto get_out; - } - jcr->xattr_ctx->nr_saved++; - } - - pm_memcpy(jcr->xattr_ctx->content, buffer, cnt); - jcr->xattr_ctx->content_length = cnt; - - /* - * Only dump the content of regular files. - */ - switch (st.st_mode & S_IFMT) { - case S_IFREG: - if (st.st_size > 0) { - /* - * Protect ourself against things getting out of hand. - */ - if (st.st_size >= MAX_XATTR_LENGTH) { - Mmsg2(jcr->errmsg, - _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"), - jcr->last_fname, MAX_XATTR_LENGTH); - goto get_out; - } - - while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) { - jcr->xattr_ctx->content = - check_pool_memory_size(jcr->xattr_ctx->content, - jcr->xattr_ctx->content_length + cnt); - memcpy(jcr->xattr_ctx->content + - jcr->xattr_ctx->content_length, buffer, cnt); - jcr->xattr_ctx->content_length += cnt; - } - - if (cnt < 0) { - Mmsg2(jcr->errmsg, _("Unable to read content of xattr %s on file \"%s\"\n"), - target_attrname, jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - break; - - default: - break; - } - - /* - * We build a new xattr stream send it to the SD. - */ - retval = send_xattr_stream(jcr, stream); - if (retval != bxattr_rtn_ok) { - goto get_out; - } - jcr->xattr_ctx->nr_saved++; - - /* - * Recursivly call solaris_save_extended_attributes for archiving the attributes - * available on this extended attribute. - */ - retval = solaris_save_xattrs(jcr, xattr_namespace, attrname); - - /* - * The recursive call could change our working dir so change back to the wanted workdir. - */ - if (fchdir(fd) < 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - -get_out: - if (acl_text != NULL) { - free(acl_text); - } - if (attrfd != -1) { - close(attrfd); - } - return retval; -} - -static bxattr_rtn_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent) -{ - const char *name; - int fd, filefd = -1, attrdirfd = -1; - DIR *dirp; - struct dirent *dp; - char current_xattr_namespace[PATH_MAX]; - bxattr_rtn_code retval = bxattr_rtn_error; - - /* - * Determine what argument to use. Use attr_parent when set - * (recursive call) or jcr->last_fname for first call. Also save - * the current depth of the xattr_space we are in. - */ - if (attr_parent) { - name = attr_parent; - if (xattr_namespace) { - bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/", - xattr_namespace, attr_parent); - } else { - bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace)); - } - } else { - name = jcr->last_fname; - bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace)); - } - - /* - * Open the file on which to save the xattrs read-only. - */ - if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) { - berrno be; - - switch (errno) { - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - /* - * Open the xattr naming space. - */ - if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) { - berrno be; - - switch (errno) { - case EINVAL: - /* - * Gentile way of the system saying this type of xattr layering is not supported. - * Which is not problem we just forget about this this xattr. - * But as this is not an error we return a positive return value. - */ - retval = bxattr_rtn_ok; - goto get_out; - case ENOENT: - retval = bxattr_rtn_ok; - goto get_out; - default: - Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"), - name, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - /* - * We need to change into the attribute directory to determine if each of the - * attributes should be saved. - */ - if (fchdir(attrdirfd) < 0) { - berrno be; - - Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Save the data of the toplevel xattr hidden_dir. We save this one before anything - * else because the readdir returns "." entry after the extensible attr entry. - * And as we want this entry before anything else we better just save its data. - */ - if (!attr_parent) - solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, ".", - true, STREAM_XATTR_SOLARIS); - - if ((fd = dup(attrdirfd)) == -1 || - (dirp = fdopendir(fd)) == (DIR *)NULL) { - berrno be; - - Mmsg2(jcr->errmsg, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - - goto get_out; - } - - /* - * Walk the namespace. - */ - while ((dp = readdir(dirp)) != NULL) { - /* - * Skip only the toplevel . dir. - */ - if (!attr_parent && bstrcmp(dp->d_name, ".")) - continue; - - /* - * Skip all .. directories - */ - if (bstrcmp(dp->d_name, "..")) - continue; - - Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n", - current_xattr_namespace, dp->d_name, jcr->last_fname); - -#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) - /* - * We are not interested in read-only extensible attributes. - */ - if (bstrcmp(dp->d_name, VIEW_READONLY)) { - Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n", - current_xattr_namespace, dp->d_name, jcr->last_fname); - - continue; - } - - /* - * We are only interested in read-write extensible attributes - * when they contain non-transient values. - */ - if (bstrcmp(dp->d_name, VIEW_READWRITE)) { - /* - * Determine if there are non-transient system attributes at the toplevel. - * We need to provide a fd to the open file. - */ - if (!solaris_has_non_transient_extensible_attributes(filefd)) { - Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n", - current_xattr_namespace, dp->d_name, jcr->last_fname); - continue; - } - - /* - * Save the xattr. - */ - solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name, - false, STREAM_XATTR_SOLARIS_SYS); - continue; - } -#endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */ - - /* - * Save the xattr. - */ - solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name, - false, STREAM_XATTR_SOLARIS); - } - - closedir(dirp); - retval = bxattr_rtn_ok; - -get_out: - if (attrdirfd != -1) - close(attrdirfd); - if (filefd != -1) - close(filefd); - return retval; -} - -#ifdef HAVE_ACL -static bxattr_rtn_code solaris_restore_xattr_acl(JCR *jcr, - int fd, - const char *attrname, - char *acl_text) -{ -#ifdef HAVE_EXTENDED_ACL - int error; - acl_t *aclp = NULL; - - if ((error = acl_fromtext(acl_text, &aclp)) != 0) { - Mmsg1(jcr->errmsg, - _("Unable to convert acl from text on file \"%s\"\n"), - jcr->last_fname); - return bxattr_rtn_error; - } - - if ((fd != -1 && facl_set(fd, aclp) != 0) || - acl_set(attrname, aclp) != 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"), - attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bxattr_rtn_error; - } - - if (aclp) { - acl_free(aclp); - } - return bxattr_rtn_ok; - -#else /* HAVE_EXTENDED_ACL */ - int n; - aclent_t *acls = NULL; - - acls = aclfromtext(acl_text, &n); - if (!acls) { - if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) || - acl(attrname, SETACL, n, acls) != 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"), - attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - return bxattr_rtn_error; - } - } - - if (acls) { - free(acls); - } - return bxattr_rtn_ok; - -#endif /* HAVE_EXTENDED_ACL */ - -} -#endif /* HAVE_ACL */ - -static bxattr_rtn_code solaris_restore_xattrs(JCR *jcr, - bool is_extensible, - char *content, - uint32_t content_length) - -{ - int fd, filefd = -1, attrdirfd = -1, attrfd = -1; - int used_bytes, cnt; - char *bp, *target_attrname, *attribs; - char *linked_target = NULL; - char *acl_text = NULL; - char *data = NULL; - int32_t inum; - struct stat st; - struct timeval times[2]; - bxattr_rtn_code retval = bxattr_rtn_error; - - /* - * Restore the xattr stream. First the part that is the same for all xattrs. - */ - used_bytes = 0; - - /* - * The name of the target xattr has a leading / we are not interested - * in that so skip it when decoding the string. We always start a the / - * of the xattr space anyway. - */ - target_attrname = content + 1; - if ((bp = strchr(target_attrname, '\0')) == (char *)NULL || - (used_bytes = (bp - content)) >= (int32_t)(content_length - 1)) { - goto restore_error; - } - attribs = ++bp; - - /* - * Open the file on which to restore the xattrs read-only. - */ - if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) { - berrno be; - - Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Open the xattr naming space and make it the current working dir. - */ - if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) { - berrno be; - - Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - if (fchdir(attrdirfd) < 0) { - berrno be; - - Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Try to open the correct xattr subdir based on the target_attrname given. - * e.g. check if its a subdir attrname. Each / in the string makes us go - * one level deeper. - */ - while ((bp = strchr(target_attrname, '/')) != (char *)NULL) { - *bp = '\0'; - - if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - close(filefd); - filefd = fd; - - /* - * Open the xattr naming space. - */ - if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - close(attrdirfd); - attrdirfd = fd; - - /* - * Make the xattr space our current workingdir. - */ - if (fchdir(attrdirfd) < 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - target_attrname = ++bp; - } - - /* - * Decode the attributes from the stream. - */ - decode_stat(attribs, &st, sizeof(st), &inum); - - /* - * Decode the next field (acl_text). - */ - if ((bp = strchr(attribs, '\0')) == (char *)NULL || - (used_bytes = (bp - content)) >= (int32_t)(content_length - 1)) { - goto restore_error; - } - acl_text = ++bp; - - /* - * Based on the filetype perform the correct action. We support most filetypes here, more - * then the actual implementation on Solaris supports so some code may never get executed - * due to limitations in the implementation. - */ - switch (st.st_mode & S_IFMT) { - case S_IFIFO: - /* - * The current implementation of xattr on Solaris doesn't support this, - * but if it ever does we are prepared. - */ - unlinkat(attrdirfd, target_attrname, 0); - if (mkfifo(target_attrname, st.st_mode) < 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - case S_IFCHR: - case S_IFBLK: - /* - * The current implementation of xattr on Solaris doesn't support this, - * but if it ever does we are prepared. - */ - unlinkat(attrdirfd, target_attrname, 0); - if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - case S_IFDIR: - /* - * If its not the hidden_dir create the entry. - * The current implementation of xattr on Solaris doesn't support this, - * but if it ever does we are prepared. - */ - if (!bstrcmp(target_attrname, ".")) { - unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR); - if (mkdir(target_attrname, st.st_mode) < 0) { - berrno be; - - /* *** FIXME *** why not Mmsg? */ - Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n", - target_attrname, jcr->last_fname, be.bstrerror()); - goto get_out; - } - } - break; - case S_IFREG: - /* - * See if this is a hard linked file. e.g. inum != 0 - */ - if (inum != 0) { - linked_target = bp; - - unlinkat(attrdirfd, target_attrname, 0); - if (link(linked_target, target_attrname) < 0) { - berrno be; - - Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"), - target_attrname, linked_target, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Successfully restored xattr. - */ - retval = bxattr_rtn_ok; - goto get_out; - } else { - if ((bp = strchr(acl_text, '\0')) == (char *)NULL || - (used_bytes = (bp - content)) >= (int32_t)content_length) { - goto restore_error; - } - - if (used_bytes < (int32_t)(content_length - 1)) - data = ++bp; - - /* - * Restore the actual xattr. - */ - if (!is_extensible) { - unlinkat(attrdirfd, target_attrname, 0); - } - - if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - /* - * Restore the actual data. - */ - if (st.st_size > 0) { - used_bytes = (data - content); - cnt = content_length - used_bytes; - - /* - * Do a sanity check, the st.st_size should be the same as the number of bytes - * we have available as data of the stream. - */ - if (cnt != st.st_size) { - Mmsg2(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"), - target_attrname, jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - while (cnt > 0) { - cnt = write(attrfd, data, cnt); - if (cnt < 0) { - berrno be; - - Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - used_bytes += cnt; - data += cnt; - cnt = content_length - used_bytes; - } - } - break; - case S_IFLNK: - /* - * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared. - */ - linked_target = bp; - - if (symlink(linked_target, target_attrname) < 0) { - berrno be; - - Mmsg4(jcr->errmsg, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"), - target_attrname, linked_target, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - /* - * Successfully restored xattr. - */ - retval = bxattr_rtn_ok; - goto get_out; - default: - goto get_out; - } - - /* - * Restore owner and acl for non extensible attributes. - */ - if (!is_extensible) { - if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) { - berrno be; - /* EINVAL means is not supported, no fail */ - if (errno == EINVAL || errno == ENOENT) { - retval = bxattr_rtn_ok; - } else { - Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - } - -#ifdef HAVE_ACL - if (acl_text && *acl_text) - if (solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text) != bxattr_rtn_ok) - goto get_out; -#endif /* HAVE_ACL */ - - /* - * For a non extensible attribute restore access and modification time on the xattr. - */ - if (!is_extensible) { - times[0].tv_sec = st.st_atime; - times[0].tv_usec = 0; - times[1].tv_sec = st.st_mtime; - times[1].tv_usec = 0; - - if (futimesat(attrdirfd, target_attrname, times) < 0) { - berrno be; - Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"), - target_attrname, jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - } - - /* - * Successfully restored xattr. - */ - retval = bxattr_rtn_ok; - goto get_out; - -restore_error: - Mmsg1(jcr->errmsg, _("Invalid xattr stream, failed to restore xattr stream on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - -get_out: - if (attrfd != -1) { - close(attrfd); - } - if (attrdirfd != -1) { - close(attrdirfd); - } - if (filefd != -1) { - close(filefd); - } - return retval; -} - -static bxattr_rtn_code solaris_backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - char cwd[PATH_MAX]; - bxattr_rtn_code retval = bxattr_rtn_ok; - - /* - * First see if extended attributes or extensible attributes are present. - * If not just pretend things went ok. - */ - if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) { - jcr->xattr_ctx->nr_saved = 0; - - /* - * As we change the cwd in the save function save the current cwd - * for restore after return from the solaris_save_xattrs function. - */ - getcwd(cwd, sizeof(cwd)); - retval = solaris_save_xattrs(jcr, NULL, NULL); - chdir(cwd); - if (jcr->xattr_ctx->link_cache) { - drop_xattr_link_cache(jcr); - } - } - return retval; -} - -static bxattr_rtn_code solaris_restore_xattr_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - char cwd[PATH_MAX]; - bool is_extensible = false; - bxattr_rtn_code retval = bxattr_rtn_error; - - /* - * First make sure we can restore xattr on the filesystem. - */ - switch (stream) { -#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) - case STREAM_XATTR_SOLARIS_SYS: - if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) { - Mmsg1(jcr->errmsg, _("Failed to restore extensible attributes on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - - is_extensible = true; - break; -#endif - case STREAM_XATTR_SOLARIS: - if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) { - Mmsg1(jcr->errmsg, _("Failed to restore extended attributes on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "%s", jcr->errmsg); - goto get_out; - } - break; - default: - goto get_out; - } - - /* - * As we change the cwd in the restore function save the current cwd - * for restore after return from the solaris_restore_xattrs function. - */ - getcwd(cwd, sizeof(cwd)); - retval = solaris_restore_xattrs(jcr, is_extensible, content, content_length); - chdir(cwd); - -get_out: - return retval; -} - - -/* - * Function pointers to the build and restore function to use for these xattrs. - */ -static bxattr_rtn_code (*os_backup_xattr_streams) - (JCR *jcr, FF_PKT *ff_pkt) = - solaris_backup_xattr_streams; -static bxattr_rtn_code (*os_restore_xattr_streams) - (JCR *jcr, int stream, char *content, uint32_t content_length) = - solaris_restore_xattr_streams; - -#endif /* defined(HAVE_SUN_OS) */ - -/* - * Entry points when compiled with support for XATTRs on a supported platform. - */ -bool backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - bxattr_rtn_code rtn = bxattr_rtn_ok; - - if (!(ff_pkt->flags & FO_XATTR && !ff_pkt->cmd_plugin)) { - return true; - } - - jcr->errmsg[0] = 0; - - /* - * See if we are changing from one device to an other. - * We save the current device we are scanning and compare - * it with the current st_dev in the last stat performed on - * the file we are currently storing. - */ - if (jcr->xattr_ctx->current_dev != ff_pkt->statp.st_dev) { - /* - * Reset the acl save flags. - */ - jcr->xattr_ctx->flags = 0; - jcr->xattr_ctx->flags |= BXATTR_FLAG_SAVE_NATIVE; - - /* - * Save that we started scanning a new filesystem. - */ - jcr->xattr_ctx->current_dev = ff_pkt->statp.st_dev; - } - - if ((jcr->xattr_ctx->flags & BXATTR_FLAG_SAVE_NATIVE) && os_restore_xattr_streams) { - rtn = os_backup_xattr_streams(jcr, ff_pkt); - } - switch (rtn) { - case bxattr_rtn_fatal: - return false; - case bxattr_rtn_ok: - return true; - case bxattr_rtn_error: - if (jcr->xattr_ctx->nr_errors < XATTR_MAX_ERROR_PRINT_PER_JOB) { - if (jcr->errmsg[0]) { - Jmsg(jcr, M_WARNING, 0, "Operating system XATTRs not configured.\n"); - } else { - Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); - } - jcr->xattr_ctx->nr_errors++; - } - return true; - } - /* Theoretically we cannot get here */ - return false; - -} - -bxattr_rtn_code restore_xattr_streams(JCR *jcr, - int stream, - char *content, - uint32_t content_length) -{ - int ret; - struct stat st; - unsigned int cnt; - bxattr_rtn_code retval = bxattr_rtn_error; - - /* - * See if we are changing from one device to an other. - * We save the current device we are restoring to and compare - * it with the current st_dev in the last stat performed on - * the file we are currently restoring. - */ - ret = lstat(jcr->last_fname, &st); - if (ret < 0) { - berrno be; - if (errno == ENOENT) { - retval = bxattr_rtn_ok; - } else { - Mmsg2(jcr->errmsg, _("Unable to stat file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg1(100, "%s", jcr->errmsg); - } - goto get_out; - } - if (jcr->xattr_ctx->current_dev != st.st_dev) { - /* - * Reset the acl save flags. - */ - jcr->xattr_ctx->flags = 0; - jcr->xattr_ctx->flags |= BXATTR_FLAG_RESTORE_NATIVE; - - /* - * Save that we started restoring to a new filesystem. - */ - jcr->xattr_ctx->current_dev = st.st_dev; - } - - /* - * See if we are still restoring native xattr to this filesystem. - */ - if ((jcr->xattr_ctx->flags & BXATTR_FLAG_RESTORE_NATIVE) && os_restore_xattr_streams) { - /* - * See if we can restore this stream, and ifso give it a try. - */ - for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) { - if (os_default_xattr_streams[cnt] == stream) { - retval = os_restore_xattr_streams(jcr, stream, content, content_length); - goto get_out; - } - } - } else { - /* - * Increment error count but don't log an error again for the same filesystem. - */ - jcr->xattr_ctx->nr_errors++; - retval = bxattr_rtn_ok; - goto get_out; - } - - /* - * Issue a warning and discard the message. But pretend the restore was ok. - */ - Jmsg2(jcr, M_WARNING, 0, - _("Cannot restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"), - jcr->last_fname, stream); - -get_out: - return retval; -} -#endif diff --git a/bacula/src/filed/xattr.h b/bacula/src/filed/xattr.h deleted file mode 100644 index 62742a82e2..0000000000 --- a/bacula/src/filed/xattr.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - Bacula(R) - The Network Backup Solution - - Copyright (C) 2000-2015 Kern Sibbald - Copyright (C) 2004-2014 Free Software Foundation Europe e.V. - - The original author of Bacula is Kern Sibbald, with contributions - from many others, a complete list can be found in the file AUTHORS. - - You may use this file and others of this release according to the - license defined in the LICENSE file, which includes the Affero General - Public License, v3.0 ("AGPLv3") and some additional permissions and - terms pursuant to its AGPLv3 Section 7. - - This notice must be preserved when any source code is - conveyed and/or propagated. - - Bacula(R) is a registered trademark of Kern Sibbald. -*/ - -#ifndef BACULA_XATTR_H_ -#define BACULA_XATTR_H_ - -#if defined(HAVE_LINUX_OS) -#define BXATTR_ENOTSUP EOPNOTSUPP -#elif defined(HAVE_DARWIN_OS) -#define BXATTR_ENOTSUP ENOTSUP -#elif defined(HAVE_HURD_OS) -#define BXATTR_ENOTSUP ENOTSUP -#endif - -/* - * Magic used in the magic field of the xattr struct. - * This way we can see we encounter a valid xattr struct. - */ -#define XATTR_MAGIC 0x5C5884 - -/* - * Internal representation of an extended attribute. - */ -struct xattr_t { - uint32_t magic; - uint32_t name_length; - char *name; - uint32_t value_length; - char *value; -}; - -/* - * Internal representation of an extended attribute hardlinked file. - */ -struct xattr_link_cache_entry_t { - uint32_t inum; - char *target; -}; - -#define BXATTR_FLAG_SAVE_NATIVE 0x01 -#define BXATTR_FLAG_RESTORE_NATIVE 0x02 - -/* - * Internal tracking data. - */ -struct xattr_ctx_t { - uint32_t flags; /* See BXATTR_FLAG_* */ - uint32_t current_dev; - uint32_t nr_errors; - uint32_t nr_saved; - POOLMEM *content; - uint32_t content_length; - alist *link_cache; -}; - -#define MAX_XATTR_LENGTH (1 * 1024 * 1024) /* 1 Mb */ - -#define XATTR_BUFSIZ 1024 - -#endif /* BACULA_XATTR_H_ */ diff --git a/bacula/src/findlib/bfile.c b/bacula/src/findlib/bfile.c index f0de4fac67..d86e666206 100644 --- a/bacula/src/findlib/bfile.c +++ b/bacula/src/findlib/bfile.c @@ -150,65 +150,65 @@ const char *stream_to_ascii(int stream) return _("Plugin Data"); case STREAM_RESTORE_OBJECT: return _("Restore Object"); - case STREAM_ACL_AIX_TEXT: + case STREAM_XACL_AIX_TEXT: return _("AIX ACL attribs"); - case STREAM_ACL_DARWIN_ACCESS: + case STREAM_XACL_DARWIN_ACCESS: return _("Darwin ACL attribs"); - case STREAM_ACL_FREEBSD_DEFAULT: + case STREAM_XACL_FREEBSD_DEFAULT: return _("FreeBSD Default ACL attribs"); - case STREAM_ACL_FREEBSD_ACCESS: + case STREAM_XACL_FREEBSD_ACCESS: return _("FreeBSD Access ACL attribs"); - case STREAM_ACL_HPUX_ACL_ENTRY: + case STREAM_XACL_HPUX_ACL_ENTRY: return _("HPUX ACL attribs"); - case STREAM_ACL_IRIX_DEFAULT: + case STREAM_XACL_IRIX_DEFAULT: return _("Irix Default ACL attribs"); - case STREAM_ACL_IRIX_ACCESS: + case STREAM_XACL_IRIX_ACCESS: return _("Irix Access ACL attribs"); - case STREAM_ACL_LINUX_DEFAULT: + case STREAM_XACL_LINUX_DEFAULT: return _("Linux Default ACL attribs"); - case STREAM_ACL_LINUX_ACCESS: + case STREAM_XACL_LINUX_ACCESS: return _("Linux Access ACL attribs"); - case STREAM_ACL_TRU64_DEFAULT: + case STREAM_XACL_TRU64_DEFAULT: return _("TRU64 Default ACL attribs"); - case STREAM_ACL_TRU64_ACCESS: + case STREAM_XACL_TRU64_ACCESS: return _("TRU64 Access ACL attribs"); - case STREAM_ACL_SOLARIS_POSIX: + case STREAM_XACL_SOLARIS_POSIX: return _("Solaris POSIX ACL attribs"); - case STREAM_ACL_SOLARIS_NFS4: + case STREAM_XACL_SOLARIS_NFS4: return _("Solaris NFSv4/ZFS ACL attribs"); - case STREAM_ACL_AFS_TEXT: + case STREAM_XACL_AFS_TEXT: return _("AFS ACL attribs"); - case STREAM_ACL_AIX_AIXC: + case STREAM_XACL_AIX_AIXC: return _("AIX POSIX ACL attribs"); - case STREAM_ACL_AIX_NFS4: + case STREAM_XACL_AIX_NFS4: return _("AIX NFSv4 ACL attribs"); - case STREAM_ACL_FREEBSD_NFS4: + case STREAM_XACL_FREEBSD_NFS4: return _("FreeBSD NFSv4/ZFS ACL attribs"); - case STREAM_ACL_HURD_DEFAULT: + case STREAM_XACL_HURD_DEFAULT: return _("GNU Hurd Default ACL attribs"); - case STREAM_ACL_HURD_ACCESS: + case STREAM_XACL_HURD_ACCESS: return _("GNU Hurd Access ACL attribs"); - case STREAM_XATTR_HURD: + case STREAM_XACL_HURD_XATTR: return _("GNU Hurd Extended attribs"); - case STREAM_XATTR_IRIX: + case STREAM_XACL_IRIX_XATTR: return _("IRIX Extended attribs"); - case STREAM_XATTR_TRU64: + case STREAM_XACL_TRU64_XATTR: return _("TRU64 Extended attribs"); - case STREAM_XATTR_AIX: + case STREAM_XACL_AIX_XATTR: return _("AIX Extended attribs"); - case STREAM_XATTR_OPENBSD: + case STREAM_XACL_OPENBSD_XATTR: return _("OpenBSD Extended attribs"); - case STREAM_XATTR_SOLARIS_SYS: + case STREAM_XACL_SOLARIS_SYS_XATTR: return _("Solaris Extensible attribs or System Extended attribs"); - case STREAM_XATTR_SOLARIS: + case STREAM_XACL_SOLARIS_XATTR: return _("Solaris Extended attribs"); - case STREAM_XATTR_DARWIN: + case STREAM_XACL_DARWIN_XATTR: return _("Darwin Extended attribs"); - case STREAM_XATTR_FREEBSD: + case STREAM_XACL_FREEBSD_XATTR: return _("FreeBSD Extended attribs"); - case STREAM_XATTR_LINUX: + case STREAM_XACL_LINUX_XATTR: return _("Linux Extended attribs"); - case STREAM_XATTR_NETBSD: + case STREAM_XACL_NETBSD_XATTR: return _("NetBSD Extended attribs"); default: sprintf(buf, "%d", stream); diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index d37a31f0fe..c2b85f7ea5 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -1,8 +1,7 @@ /* Bacula(R) - The Network Backup Solution - Copyright (C) 2000-2015 Kern Sibbald - Copyright (C) 2000-2014 Free Software Foundation Europe e.V. + Copyright (C) 2000-2016 Kern Sibbald The original author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -12,7 +11,7 @@ Public License, v3.0 ("AGPLv3") and some additional permissions and terms pursuant to its AGPLv3 Section 7. - This notice must be preserved when any source code is + This notice must be preserved when any source code is conveyed and/or propagated. Bacula(R) is a registered trademark of Kern Sibbald. @@ -147,8 +146,7 @@ struct bpContext; #ifdef FILE_DAEMON class htable; -struct acl_ctx_t; -struct xattr_ctx_t; +class XACL; class snapshot_manager; struct CRYPTO_CTX { @@ -414,8 +412,7 @@ public: POOLMEM *last_fname; /* last file saved/verified */ POOLMEM *job_metadata; /* VSS job metadata */ pthread_cond_t job_start_wait; /* Wait for SD to start Job */ - acl_ctx_t *acl_ctx; /* ACLs for backup/restore */ - xattr_ctx_t *xattr_ctx; /* Extended Attributes for backup/restore */ + XACL *xacl; /* ACLs and Extended Attributes for backup/restore */ int32_t last_type; /* type of last file saved/verified */ int incremental; /* set if incremental for SINCE */ time_t last_stat_time; /* Last time stats sent to Dir */ diff --git a/bacula/src/stored/bscan.c b/bacula/src/stored/bscan.c index ca3653a21b..e9c6678107 100644 --- a/bacula/src/stored/bscan.c +++ b/bacula/src/stored/bscan.c @@ -835,40 +835,40 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec) case STREAM_UNIX_ACCESS_ACL: /* Deprecated Standard ACL attributes on UNIX */ case STREAM_UNIX_DEFAULT_ACL: /* Deprecated Default ACL attributes on UNIX */ case STREAM_HFSPLUS_ATTRIBUTES: - case STREAM_ACL_AIX_TEXT: - case STREAM_ACL_DARWIN_ACCESS: - case STREAM_ACL_FREEBSD_DEFAULT: - case STREAM_ACL_FREEBSD_ACCESS: - case STREAM_ACL_HPUX_ACL_ENTRY: - case STREAM_ACL_IRIX_DEFAULT: - case STREAM_ACL_IRIX_ACCESS: - case STREAM_ACL_LINUX_DEFAULT: - case STREAM_ACL_LINUX_ACCESS: - case STREAM_ACL_TRU64_DEFAULT: - case STREAM_ACL_TRU64_DEFAULT_DIR: - case STREAM_ACL_TRU64_ACCESS: - case STREAM_ACL_SOLARIS_POSIX: - case STREAM_ACL_SOLARIS_NFS4: - case STREAM_ACL_AFS_TEXT: - case STREAM_ACL_AIX_AIXC: - case STREAM_ACL_AIX_NFS4: - case STREAM_ACL_FREEBSD_NFS4: - case STREAM_ACL_HURD_DEFAULT: - case STREAM_ACL_HURD_ACCESS: + case STREAM_XACL_AIX_TEXT: + case STREAM_XACL_DARWIN_ACCESS: + case STREAM_XACL_FREEBSD_DEFAULT: + case STREAM_XACL_FREEBSD_ACCESS: + case STREAM_XACL_HPUX_ACL_ENTRY: + case STREAM_XACL_IRIX_DEFAULT: + case STREAM_XACL_IRIX_ACCESS: + case STREAM_XACL_LINUX_DEFAULT: + case STREAM_XACL_LINUX_ACCESS: + case STREAM_XACL_TRU64_DEFAULT: + case STREAM_XACL_TRU64_DEFAULT_DIR: + case STREAM_XACL_TRU64_ACCESS: + case STREAM_XACL_SOLARIS_POSIX: + case STREAM_XACL_SOLARIS_NFS4: + case STREAM_XACL_AFS_TEXT: + case STREAM_XACL_AIX_AIXC: + case STREAM_XACL_AIX_NFS4: + case STREAM_XACL_FREEBSD_NFS4: + case STREAM_XACL_HURD_DEFAULT: + case STREAM_XACL_HURD_ACCESS: /* Ignore Unix ACL attributes */ break; - case STREAM_XATTR_HURD: - case STREAM_XATTR_IRIX: - case STREAM_XATTR_TRU64: - case STREAM_XATTR_AIX: - case STREAM_XATTR_OPENBSD: - case STREAM_XATTR_SOLARIS_SYS: - case STREAM_XATTR_SOLARIS: - case STREAM_XATTR_DARWIN: - case STREAM_XATTR_FREEBSD: - case STREAM_XATTR_LINUX: - case STREAM_XATTR_NETBSD: + case STREAM_XACL_HURD_XATTR: + case STREAM_XACL_IRIX_XATTR: + case STREAM_XACL_TRU64_XATTR: + case STREAM_XACL_AIX_XATTR: + case STREAM_XACL_OPENBSD_XATTR: + case STREAM_XACL_SOLARIS_SYS_XATTR: + case STREAM_XACL_SOLARIS_XATTR: + case STREAM_XACL_DARWIN_XATTR: + case STREAM_XACL_FREEBSD_XATTR: + case STREAM_XACL_LINUX_XATTR: + case STREAM_XACL_NETBSD_XATTR: /* Ignore Unix Extended attributes */ break; diff --git a/bacula/src/streams.h b/bacula/src/streams.h index d1db5b4120..e1f7dc7236 100644 --- a/bacula/src/streams.h +++ b/bacula/src/streams.h @@ -111,40 +111,43 @@ * from the stream dispatch function. Currently in this reserved space we allocate the * different acl streams from 1000 on and the different extended attributes streams from * 1999 down. So the two naming spaces grows towards each other. + * + * Rationalize names a bit to correspond to the new XACL classes + * */ -#define STREAM_ACL_AIX_TEXT 1000 /* AIX string of acl_get */ -#define STREAM_ACL_DARWIN_ACCESS 1001 /* Darwin (OSX) acl_t string of acl_to_text (POSIX acl) */ -#define STREAM_ACL_FREEBSD_DEFAULT 1002 /* FreeBSD acl_t string of acl_to_text (POSIX acl) for default acls */ -#define STREAM_ACL_FREEBSD_ACCESS 1003 /* FreeBSD acl_t string of acl_to_text (POSIX acl) for access acls */ -#define STREAM_ACL_HPUX_ACL_ENTRY 1004 /* HPUX acl_entry string of acltostr (POSIX acl) */ -#define STREAM_ACL_IRIX_DEFAULT 1005 /* IRIX acl_t string of acl_to_text (POSIX acl) for default acls */ -#define STREAM_ACL_IRIX_ACCESS 1006 /* IRIX acl_t string of acl_to_text (POSIX acl) for access acls */ -#define STREAM_ACL_LINUX_DEFAULT 1007 /* Linux acl_t string of acl_to_text (POSIX acl) for default acls */ -#define STREAM_ACL_LINUX_ACCESS 1008 /* Linux acl_t string of acl_to_text (POSIX acl) for access acls */ -#define STREAM_ACL_TRU64_DEFAULT 1009 /* Tru64 acl_t string of acl_to_text (POSIX acl) for default acls */ -#define STREAM_ACL_TRU64_DEFAULT_DIR 1010 /* Tru64 acl_t string of acl_to_text (POSIX acl) for default acls */ -#define STREAM_ACL_TRU64_ACCESS 1011 /* Tru64 acl_t string of acl_to_text (POSIX acl) for access acls */ -#define STREAM_ACL_SOLARIS_POSIX 1012 /* Solaris aclent_t string of acltotext or acl_totext (POSIX acl) */ -#define STREAM_ACL_SOLARIS_NFS4 1013 /* Solaris ace_t string of of acl_totext (NFSv4 or ZFS acl) */ -#define STREAM_ACL_AFS_TEXT 1014 /* AFS string of pioctl */ -#define STREAM_ACL_AIX_AIXC 1015 /* AIX string of aclx_printStr (POSIX acl) */ -#define STREAM_ACL_AIX_NFS4 1016 /* AIX string of aclx_printStr (NFSv4 acl) */ -#define STREAM_ACL_FREEBSD_NFS4 1017 /* FreeBSD acl_t string of acl_to_text (NFSv4 or ZFS acl) */ -#define STREAM_ACL_HURD_DEFAULT 1018 /* GNU HURD acl_t string of acl_to_text (POSIX acl) for default acls */ -#define STREAM_ACL_HURD_ACCESS 1019 /* GNU HURD acl_t string of acl_to_text (POSIX acl) for access acls */ -#define STREAM_XATTR_HURD 1989 /* GNU HURD extended attributes */ -#define STREAM_XATTR_IRIX 1990 /* IRIX extended attributes */ -#define STREAM_XATTR_TRU64 1991 /* TRU64 extended attributes */ -#define STREAM_XATTR_AIX 1992 /* AIX extended attributes */ -#define STREAM_XATTR_OPENBSD 1993 /* OpenBSD extended attributes */ -#define STREAM_XATTR_SOLARIS_SYS 1994 /* Solaris extensible attributes or - * otherwise named extended system attributes. - */ -#define STREAM_XATTR_SOLARIS 1995 /* Solaris extented attributes */ -#define STREAM_XATTR_DARWIN 1996 /* Darwin (OSX) extended attributes */ -#define STREAM_XATTR_FREEBSD 1997 /* FreeBSD extended attributes */ -#define STREAM_XATTR_LINUX 1998 /* Linux specific attributes */ -#define STREAM_XATTR_NETBSD 1999 /* NetBSD extended attributes */ +#define STREAM_XACL_AIX_TEXT 1000 /* AIX string of acl_get */ +#define STREAM_XACL_DARWIN_ACCESS 1001 /* Darwin (OSX) acl_t string of acl_to_text (POSIX acl) */ +#define STREAM_XACL_FREEBSD_DEFAULT 1002 /* FreeBSD acl_t string of acl_to_text (POSIX acl) for default acls */ +#define STREAM_XACL_FREEBSD_ACCESS 1003 /* FreeBSD acl_t string of acl_to_text (POSIX acl) for access acls */ +#define STREAM_XACL_HPUX_ACL_ENTRY 1004 /* HPUX acl_entry string of acltostr (POSIX acl) */ +#define STREAM_XACL_IRIX_DEFAULT 1005 /* IRIX acl_t string of acl_to_text (POSIX acl) for default acls */ +#define STREAM_XACL_IRIX_ACCESS 1006 /* IRIX acl_t string of acl_to_text (POSIX acl) for access acls */ +#define STREAM_XACL_LINUX_DEFAULT 1007 /* Linux acl_t string of acl_to_text (POSIX acl) for default acls */ +#define STREAM_XACL_LINUX_ACCESS 1008 /* Linux acl_t string of acl_to_text (POSIX acl) for access acls */ +#define STREAM_XACL_TRU64_DEFAULT 1009 /* Tru64 acl_t string of acl_to_text (POSIX acl) for default acls */ +#define STREAM_XACL_TRU64_DEFAULT_DIR 1010 /* Tru64 acl_t string of acl_to_text (POSIX acl) for default acls */ +#define STREAM_XACL_TRU64_ACCESS 1011 /* Tru64 acl_t string of acl_to_text (POSIX acl) for access acls */ +#define STREAM_XACL_SOLARIS_POSIX 1012 /* Solaris aclent_t string of acltotext or acl_totext (POSIX acl) */ +#define STREAM_XACL_SOLARIS_NFS4 1013 /* Solaris ace_t string of of acl_totext (NFSv4 or ZFS acl) */ +#define STREAM_XACL_AFS_TEXT 1014 /* AFS string of pioctl */ +#define STREAM_XACL_AIX_AIXC 1015 /* AIX string of aclx_printStr (POSIX acl) */ +#define STREAM_XACL_AIX_NFS4 1016 /* AIX string of aclx_printStr (NFSv4 acl) */ +#define STREAM_XACL_FREEBSD_NFS4 1017 /* FreeBSD acl_t string of acl_to_text (NFSv4 or ZFS acl) */ +#define STREAM_XACL_HURD_DEFAULT 1018 /* GNU HURD acl_t string of acl_to_text (POSIX acl) for default acls */ +#define STREAM_XACL_HURD_ACCESS 1019 /* GNU HURD acl_t string of acl_to_text (POSIX acl) for access acls */ +#define STREAM_XACL_HURD_XATTR 1989 /* GNU HURD extended attributes */ +#define STREAM_XACL_IRIX_XATTR 1990 /* IRIX extended attributes */ +#define STREAM_XACL_TRU64_XATTR 1991 /* TRU64 extended attributes */ +#define STREAM_XACL_AIX_XATTR 1992 /* AIX extended attributes */ +#define STREAM_XACL_OPENBSD_XATTR 1993 /* OpenBSD extended attributes */ +#define STREAM_XACL_SOLARIS_SYS_XATTR 1994 /* Solaris extensible attributes or + * otherwise named extended system attributes. + */ +#define STREAM_XACL_SOLARIS_XATTR 1995 /* Solaris extented attributes */ +#define STREAM_XACL_DARWIN_XATTR 1996 /* Darwin (OSX) extended attributes */ +#define STREAM_XACL_FREEBSD_XATTR 1997 /* FreeBSD extended attributes */ +#define STREAM_XACL_LINUX_XATTR 1998 /* Linux specific attributes */ +#define STREAM_XACL_NETBSD_XATTR 1999 /* NetBSD extended attributes */ /* WARNING!!! do not define more than 2047 of these old types */