From: Radosław Korzeniewski Date: Thu, 9 Nov 2017 11:26:11 +0000 (+0100) Subject: Update ACL/XATTR code and define new ACL/XATTR API for Plugins. X-Git-Tag: Release-9.0.6~58 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=b4671fd4b19e517bad9180fc43a098c706cad216;p=bacula%2Fbacula Update ACL/XATTR code and define new ACL/XATTR API for Plugins. The patch redesign ACL/XATTR code separating acl functionality from xattr. It allows for full conditional compilation on any supported OS. It closes issue: #2295. The patch adds a new ACl/XATTR API for Plugins. A new Plugin callback: bRC (*handleXACLdata)(bpContext *ctx, struct xacl_pkt *xacl); and support data: - BACL_BACKUP - Plugin ACL data backup, - BACL_RESTORE - Plugin ACL data restore, - BXATTR_BACKUP - Plugin XATTR data backup, - BXATTR_RESTORE - Plugin XATTR data restore struct xacl_pkt { int32_t pkt_size; /* Size of this packet */ int32_t func; /* Function code */ int32_t count; /* read/write count */ char *content; /* read/write buffer */ int32_t pkt_end; /* end packet sentinel */ }; Bacula will call handleXACLdata with xacl.func set to one of the defined enum's and for backup requires xacl.count and xacl.content to be set by a plugin and if xacl.content is allocated by plugin he is responsible for free and for restore will setup xacl.count and xacl.content to the data restored. The Bacula Plugin API will call handleXACLdata during backup with specific xacl.func multiple times for every file, until a plugin return xacl.count == 0 (similar to pluginIO where no more data to backup is signalled by io->status = 0). And as usual handleXACLdata has to return bRC_OK when operation was successful (acl/xattr to backup/restore is a success) or other (bRC_Error) when operation was unsuccessful. In this case no acl/xattr data will be saved. --- diff --git a/bacula/src/filed/Makefile.in b/bacula/src/filed/Makefile.in index 8b3e7f2d4a..f0d117200a 100644 --- a/bacula/src/filed/Makefile.in +++ b/bacula/src/filed/Makefile.in @@ -35,7 +35,8 @@ SVRSRCS = filed.c authenticate.c backup.c crypto.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 \ - xacl.c xacl_linux.c xacl_osx.c xacl_solaris.c xacl_freebsd.c + bacl.c bacl_linux.c bacl_osx.c bacl_solaris.c bacl_freebsd.c \ + bxattr.c bxattr_linux.c bxattr_osx.c bxattr_solaris.c bxattr_freebsd.c SVROBJS = $(SVRSRCS:.c=.o) JSONOBJS = bfdjson.o filed_conf.o @@ -62,7 +63,7 @@ all: Makefile bacula-fd @STATIC_FD@ bfdjson @echo "==== Make of filed is good ====" @echo " " -xacl.o: xacl.c +bacl.o: bacl.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/backup.c b/bacula/src/filed/backup.c index f76ff867ca..1cba287010 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -155,24 +155,31 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) } start_heartbeat_monitor(jcr); - jcr->xacl = (XACL*)new_xacl(); +#ifdef HAVE_ACL + jcr->bacl = (BACL*)new_bacl(); +#endif +#ifdef HAVE_XATTR + jcr->bxattr = (BXATTR*)new_bxattr(); +#endif /* Subroutine save_file() is called for each file */ if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, plugin_save)) { ok = false; /* error */ jcr->setJobStatus(JS_ErrorTerminated); } - - if (jcr->xacl) { - 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()); - } +#ifdef HAVE_ACL + if (jcr->bacl && jcr->bacl->get_acl_nr_errors() > 0) { + Jmsg(jcr, M_WARNING, 0, _("Had %ld acl errors while doing backup\n"), + jcr->bacl->get_acl_nr_errors()); } +#endif +#ifdef HAVE_XATTR + if (jcr->bxattr && jcr->bxattr->get_xattr_nr_errors() > 0) { + Jmsg(jcr, M_WARNING, 0, _("Had %ld xattr errors while doing backup\n"), + jcr->bxattr->get_xattr_nr_errors()); + } +#endif + /* Delete or keep snapshots */ close_snapshot_backup_session(jcr); close_vss_backup_session(jcr); @@ -183,10 +190,18 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) sd->signal(BNET_EOD); /* end of sending data */ - if (jcr->xacl) { - delete(jcr->xacl); - jcr->xacl = NULL; +#ifdef HAVE_ACL + if (jcr->bacl) { + delete(jcr->bacl); + jcr->bacl = NULL; + } +#endif +#ifdef HAVE_XATTR + if (jcr->bxattr) { + delete(jcr->bxattr); + jcr->bxattr = NULL; } +#endif if (jcr->big_buf) { bfree_and_null(jcr->big_buf); } @@ -505,16 +520,18 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) /* * Save ACLs and Extended Attributes when requested and available - * for anything not being a symlink and not being a plugin (why not?). + * for anything not being a symlink. */ - 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; - } +#ifdef HAVE_ACL + if (jcr->bacl && jcr->bacl->backup_acl(jcr, ff_pkt) == bRC_BACL_error) { + goto bail_out; } +#endif +#ifdef HAVE_XATTR + if (jcr->bxattr && jcr->bxattr->backup_xattr(jcr, ff_pkt) == bRC_BXATTR_error) { + goto bail_out; + } +#endif if (!crypto_terminate_digests(bctx)) { goto bail_out; diff --git a/bacula/src/filed/bacl.c b/bacula/src/filed/bacl.c new file mode 100644 index 0000000000..53ed0218f8 --- /dev/null +++ b/bacula/src/filed/bacl.c @@ -0,0 +1,776 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 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 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 in Bacula Enterprise. + * The runtime consist of two parts: + * 1. OS independent class: BACL + * 2. OS dependent subclass: BACL_* + * + * 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 dependent 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. + * Above behavior is a backward compatibility with previous Bacula implementation + * we need to maintain. + * + * During OS specific implementation of BACL you need to implement a following methods: + * + * [bacl] - indicates bacl function/method to call + * [os] - indicates OS specific function, which could be different on specific OS + * (we use a Linux API calls as an example) + * + * ::os_get_acl(JCR *jcr, BACL_type bacltype) + * + * 1. get binary form of the acl - acl_get_file[os] + * 2. check if acl is trivial if required - call acl_issimple[bacl] + * 3. translate binary form into text representation - acl_to_text[os] + * 4. save acl text into content - set_content[bacl] + * 5. if acl not supported on filesystem - call clear_flag(BACL_FLAG_NATIVE)[bacl] + * + * ::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt) + * + * 1. call os_get_acl[bacl] for all supported ACL_TYPES + * 2. call send_acl_stream[bacl] for all supported ACL_STREAMS + * + * ::os_set_acl(JCR *jcr, BACL_type bacltype, 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(BACL_FLAG_NATIVE) + * + * ::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length) + * + * 1. call os_set_acl for all supported ACL_TYPES + */ + +#include "bacula.h" +#include "filed.h" +#include "fd_plugins.h" + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +/* + * This is a constructor of the base BACL class which is OS independent + * + * - for initialization it uses ::init() + * + */ +BACL::BACL (){ + init(); +}; + +/* + * This is a destructor of the BACL class + */ +BACL::~BACL (){ + free_pool_memory(content); +}; + +/* + * Initialization routine + * - initializes all variables to required status + * - allocates required memory + */ +void BACL::init(){ +#if defined(HAVE_ACL) + acl_ena = TRUE; +#else + acl_ena = FALSE; +#endif + + /* generic variables */ + flags = BACL_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; + acl_streams = NULL; + default_acl_streams = NULL; +}; + +/* + * Enables ACL handling in runtime, could be disabled with disable_acl + * when ACL is not configured then cannot change status + */ +void BACL::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 BACL::disable_acl(){ + acl_ena = FALSE; +}; + +/* + * Copies a text into a content variable and sets a content_len respectively + * + * in: + * text - a standard null terminated string + * out: + * pointer to content variable to use externally + */ +POOLMEM * BACL::set_content(char *text){ + content_len = pm_strcpy(&content, text); + if (content_len > 0){ + /* count the nul terminated char */ + content_len++; + } + // Dmsg2(400, "BACL::set_content: %p %i\n", text, content_len); + 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 * BACL::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_BACL_ok - change of device checked and finish successful + * bRC_BACL_error - encountered error + * bRC_BACL_skip - cannot verify device - no file found + * bRC_BACL_inval - invalid input data + */ +bRC_BACL BACL::check_dev (JCR *jcr){ + + int lst; + struct stat st; + + /* sanity check of input variables */ + if (jcr == NULL || jcr->last_fname == NULL){ + return bRC_BACL_inval; + } + + lst = lstat(jcr->last_fname, &st); + switch (lst){ + case -1: { + berrno be; + switch (errno){ + case ENOENT: + return bRC_BACL_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_BACL_error; + } + break; + } + case 0: + break; + } + + check_dev(jcr, st.st_dev); + + return bRC_BACL_ok; +}; + +/* + * Check if we changed the device, if so setup a flags + * + * in: + * jcr - Job Control Record + * out: + * internal flags status set + */ +void BACL::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 = BACL_FLAG_NONE; +#if defined(HAVE_AFS_ACL) + /* handle special fs: AFS */ + if (fstype_equals(jcr->last_fname, "afs")){ + set_flag(BACL_FLAG_AFS); + } else { + set_flag(BACL_FLAG_NATIVE); + } +#else + set_flag(BACL_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_BACL_inval - when supplied variables are incorrect + * bRC_BACL_fatal - when we can't send data to the SD + * bRC_BACL_ok - send finish without errors + */ +bRC_BACL BACL::send_acl_stream(JCR *jcr, int stream){ + + BSOCK * sd; + POOLMEM * msgsave; +#ifdef FD_NO_SEND_TEST + return bRC_BACL_ok; +#endif + + /* sanity check of input variables */ + if (jcr == NULL || jcr->store_bsock == NULL){ + return bRC_BACL_inval; + } + if (content_len <= 0){ + return bRC_BACL_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_BACL_fatal; + } + + /* send the buffer to the storage daemon */ + Dmsg1(400, "Backing up ACL: %i\n", content_len); +#if 0 + POOL_MEM tmp(PM_FNAME); + pm_memcpy(tmp, content, content_len); + Dmsg2(400, "Backing up ACL: (%i) <%s>\n", strlen(tmp.addr()), tmp.c_str()); +#endif + 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_BACL_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_BACL_fatal; + } + + Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname); + return bRC_BACL_ok; +}; + +/* + * The main public backup method for ACL + * + * in: + * jcr - Job Control Record + * ff_pkt - file backup record + * out: + * bRC_BACL_fatal - when ACL backup is not compiled in Bacula + * bRC_BACL_ok - backup finish without problems + * bRC_BACL_error - when you can't backup acl data because some error + */ +bRC_BACL BACL::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_BACL_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_BACL rc; + + jcr->errmsg[0] = 0; + /* check if we have a plugin generated backup */ + if (ff_pkt->cmd_plugin){ + rc = backup_plugin_acl(jcr, ff_pkt); + } else { + /* Check for aclsupport flag and no acl request for link */ + if (!(ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK)){ + return bRC_BACL_ok; + } + + check_dev(jcr, ff_pkt->statp.st_dev); + +#if defined(HAVE_AFS_ACL) + if (flags & BACL_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 & BACL_FLAG_NATIVE){ + Dmsg0(400, "make Native ACL call\n"); + rc = os_backup_acl(jcr, ff_pkt); + } else { + /* skip acl backup */ + return bRC_BACL_ok; + } +#endif + } +#if defined(HAVE_AFS_ACL) + bail_out: +#endif + if (rc == bRC_BACL_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_BACL_ok; + } + return rc; + } + return bRC_BACL_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 pointer to the data stream to restore + * length - a data stream length + * out: + * bRC_BACL_fatal - when ACL restore is not compiled in Bacula + * bRC_BACL_ok - restore finish without problems + * bRC_BACL_error - when you can't restore a stream because some error + */ +bRC_BACL BACL::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 restore requested but not configured in Bacula.\n"); + return bRC_BACL_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_BACL rc; + + /* check_dev supported on real fs only */ + if (stream != STREAM_XACL_PLUGIN_ACL){ + rc = check_dev(jcr); + + switch (rc){ + case bRC_BACL_skip: + return bRC_BACL_ok; + case bRC_BACL_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_BACL_AFS_TEXT: + if (flags & BACL_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_BACL_ok; + } +#endif +#if defined(HAVE_ACL) + case STREAM_UNIX_ACCESS_ACL: + case STREAM_UNIX_DEFAULT_ACL: + if (flags & BACL_FLAG_NATIVE){ + return os_restore_acl(jcr, stream, content, content_len); + } else { + inc_acl_errors(); + return bRC_BACL_ok; + } + break; + case STREAM_XACL_PLUGIN_ACL: + return restore_plugin_acl(jcr); + default: + if (flags & BACL_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_BACL_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_BACL_error; + } + return bRC_BACL_ok; +#endif +}; + +/* + * Performs a generic ACL backup using OS specific methods for + * getting acl data from file + * + * in: + * jcr - Job Control Record + * ff_pkt - file to backup control package + * out: + * bRC_BACL_ok - backup of acl's was successful + * bRC_BACL_fatal - was an error during acl backup + */ +bRC_BACL BACL::generic_backup_acl (JCR *jcr, FF_PKT *ff_pkt) +{ + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_BACL_inval; + } + + if (os_get_acl(jcr, BACL_TYPE_ACCESS) == bRC_BACL_fatal){ + /* XXX: check if os_get_acl return fatal and decide what to do when error is returned */ + return bRC_BACL_fatal; + } + + if (content_len > 0){ + if (send_acl_stream(jcr, acl_streams[0]) == bRC_BACL_fatal){ + return bRC_BACL_fatal; + } + } + + if (ff_pkt->type == FT_DIREND){ + if (os_get_acl(jcr, BACL_TYPE_DEFAULT) == bRC_BACL_fatal){ + return bRC_BACL_fatal; + } + if (content_len > 0){ + if (send_acl_stream(jcr, default_acl_streams[0]) == bRC_BACL_fatal){ + return bRC_BACL_fatal; + } + } + } + return bRC_BACL_ok; +}; + +/* + * Performs a generic ACL restore using OS specific methods for + * setting acl data on file. + * + * in: + * jcr - Job Control Record + * stream - a stream number to restore + * out: + * bRC_BACL_ok - restore of acl's was successful + * bRC_BACL_error - was an error during acl restore + * bRC_BACL_fatal - was a fatal error during acl restore or input data + * is invalid + */ +bRC_BACL BACL::generic_restore_acl (JCR *jcr, int stream){ + + unsigned int count; + + /* sanity check of input variables */ + if (jcr == NULL){ + return bRC_BACL_inval; + } + + switch (stream){ + case STREAM_UNIX_ACCESS_ACL: + return os_set_acl(jcr, BACL_TYPE_ACCESS, content, content_len); + case STREAM_UNIX_DEFAULT_ACL: + return os_set_acl(jcr, BACL_TYPE_DEFAULT, content, content_len); + default: + for (count = 0; acl_streams[count] > 0; count++){ + if (acl_streams[count] == stream){ + return os_set_acl(jcr, BACL_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, BACL_TYPE_DEFAULT, content, content_len); + } + } + break; + } + return bRC_BACL_error; +}; + +/* + * Perform a generic ACL backup using a plugin. It calls the plugin API to + * get required acl data from plugin. + * + * in: + * jcr - Job Control Record + * ff_pkt - file to backup control package + * out: + * bRC_BACL_ok - backup of acls was successful + * bRC_BACL_fatal - was an error during acl backup + */ +bRC_BACL BACL::backup_plugin_acl (JCR *jcr, FF_PKT *ff_pkt) +{ + int status; + char *data; + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_BACL_inval; + } + + while ((status = plugin_backup_acl(jcr, ff_pkt, &data)) > 0){ + /* data is a plugin buffer which contains data to backup + * and status is a length of the buffer when > 0 */ + set_content(data, status); + if (send_acl_stream(jcr, STREAM_XACL_PLUGIN_ACL) == bRC_BACL_fatal){ + return bRC_BACL_fatal; + } + } + if (status < 0){ + /* error */ + return bRC_BACL_error; + } + + return bRC_BACL_ok; +}; + +/* + * Perform a generic ACL restore using a plugin. It calls the plugin API to + * send acl data to plugin. + * + * in: + * jcr - Job Control Record + * stream - a stream number to restore + * out: + * bRC_BACL_ok - restore of acls was successful + * bRC_BACL_error - was an error during acls restore + * bRC_BACL_fatal - was a fatal error during acl restore or input data + * is invalid + */ +bRC_BACL BACL::restore_plugin_acl (JCR *jcr) +{ + /* sanity check of input variables */ + if (jcr == NULL){ + return bRC_BACL_inval; + } + + if (!plugin_restore_acl(jcr, content, content_len)){ + /* error */ + return bRC_BACL_error; + } + + return bRC_BACL_ok; +} + +/* + * 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 specific OS + * pacl_def - default (directory) acl streams supported for specific OS + */ +void BACL::set_acl_streams (const int *pacl, const int *pacl_def){ + + acl_streams = pacl; + default_acl_streams = pacl_def; +}; + +#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_BACL_inval - input variables are invalid (NULL) + * bRC_BACL_ok - backup finish without problems + * bRC_BACL_error - when you can't backup acl data because some error + */ +bRC_BACL BACL::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_BACL_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_BACL_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_BACL_error; + } + set_content(data); + return send_acl_stream(jcr, STREAM_BACL_AFS_TEXT); +}; + +/* + * Restore ACL data of AFS + * in: + * jcr - Job Control Record + * stream - a backup stream type number to restore_acl + * out: + * bRC_BACL_inval - input variables are invalid (NULL) + * bRC_BACL_ok - backup finish without problems + * bRC_BACL_error - when you can't backup acl data because some error + */ +bRC_BACL BACL::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_BACL_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_BACL_error; + } + return bRC_BACL_ok; +}; +#endif /* HAVE_AFS_ACL */ + +#include "bacl_osx.h" +#include "bacl_linux.h" +#include "bacl_freebsd.h" +#include "bacl_solaris.h" +// #include "bacl_aix.h" + +/* + * Creating the correct instance of the BACL for a supported OS + */ +void *new_bacl() +{ +#if defined(HAVE_DARWIN_OS) + return new BACL_OSX(); +#elif defined(HAVE_LINUX_OS) + return new BACL_Linux(); +#elif defined(HAVE_FREEBSD_OS) + return new BACL_FreeBSD(); +#elif defined(HAVE_HURD_OS) + return new BACL_Hurd(); +#elif defined(HAVE_AIX_OS) + return new BACL_AIX(); +#elif defined(HAVE_IRIX_OS) + return new BACL_IRIX(); +#elif defined(HAVE_OSF1_OS) + return new BACL_OSF1(); +#elif defined(HAVE_SUN_OS) + return new BACL_Solaris(); +#else + return NULL; +#endif +}; + +#endif /* HAVE_ACL */ diff --git a/bacula/src/filed/bacl.h b/bacula/src/filed/bacl.h new file mode 100644 index 0000000000..5ae448ac7f --- /dev/null +++ b/bacula/src/filed/bacl.h @@ -0,0 +1,195 @@ +/* + 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 code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BACL_H_ +#define __BACL_H_ + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +/* + * 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_BACL { + bRC_BACL_inval = -3, // input data invalid + bRC_BACL_fatal = -2, // a fatal error + bRC_BACL_error = -1, // standard error + bRC_BACL_ok = 0, // success + bRC_BACL_skip = 1, // processing should skip current runtime + bRC_BACL_cont = 2 // processing should skip current element + // and continue with next one +}; + +/* + * 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_PLUGIN = 6 +} BACL_type; + +/* + * Flags which control what ACL engine to use for backup/restore + */ +#define BACL_FLAG_NONE 0 +#define BACL_FLAG_NATIVE 0x01 +#define BACL_FLAG_AFS 0x02 +#define BACL_FLAG_PLUGIN 0x04 + +/* + * Ensure we have none + */ +#ifndef ACL_TYPE_NONE +#define ACL_TYPE_NONE 0x0 +#endif + +/* + * Basic ACL class which is a foundation for any other OS specific 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 generic functions and OS independent API, i.e. for AFS ACL or Plugins ACL + * (future functionality). + */ +class BACL { +private: + bool acl_ena; + uint32_t flags; + uint32_t current_dev; + POOLMEM *content; + uint32_t content_len; + uint32_t acl_nr_errors; + const int *acl_streams; + const int *default_acl_streams; + const char **xattr_skiplist; + const char **xattr_acl_skiplist; + + void init(); + + /** + * Perform OS specific ACL backup. + * in: + * jcr - Job Control Record + * ff_pkt - file to backup information rector + * out: + * bRC_BACL_ok - backup performed without problems + * any other - some error occurred + */ + virtual bRC_BACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){return bRC_BACL_fatal;}; + + /** + * Perform OS specific 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_BACL_ok - backup performed without problems + * any other - some error occurred + */ + virtual bRC_BACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){return bRC_BACL_fatal;}; + + /** + * Low level OS specific runtime to get ACL data from file. The ACL data is set in internal content buffer. + * + * in: + * jcr - Job Control Record + * bacltype - the acl type to restore + * out: + * bRC_BACL_ok - + * bRC_BACL_error/fatal - an error or fatal error occurred + */ + virtual bRC_BACL os_get_acl (JCR *jcr, BACL_type bacltype){return bRC_BACL_fatal;}; + + /** + * Low level OS specific runtime to set ACL data on file. + * + * in: + * jcr - Job Control Record + * bacltype - the acl type to restore + * content - a buffer with data to restore + * length - a data restore length + * out: + * bRC_BACL_ok - + * bRC_BACL_error/fatal - an error or fatal error occurred + */ + virtual bRC_BACL os_set_acl (JCR *jcr, BACL_type bacltype, char *content, uint32_t length){return bRC_BACL_fatal;}; + + void inc_acl_errors(){ acl_nr_errors++;}; + bRC_BACL check_dev (JCR *jcr); + void check_dev (JCR *jcr, uint32_t dev); + +public: + BACL (); + virtual ~BACL(); + + /* enable/disable functionality */ + void enable_acl(); + void disable_acl(); + + /* + * public methods used outside the class or derivatives + */ + bRC_BACL backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_BACL restore_acl (JCR *jcr, int stream, char *content, uint32_t content_length); + + /* utility functions */ + inline uint32_t get_acl_nr_errors(){ return acl_nr_errors;}; + void set_acl_streams (const int *pacl, const int *pacl_def); + 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;}; + + /* sending data to the storage */ + bRC_BACL send_acl_stream (JCR *jcr, int stream); + + /* generic functions */ + bRC_BACL generic_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_BACL generic_restore_acl (JCR *jcr, int stream); + bRC_BACL afs_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_BACL afs_restore_acl (JCR *jcr, int stream); + bRC_BACL backup_plugin_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_BACL restore_plugin_acl (JCR *jcr); +}; + +void *new_bacl(); + +#endif /* HAVE_ACL */ + +#endif /* __BACL_H_ */ diff --git a/bacula/src/filed/bacl_freebsd.c b/bacula/src/filed/bacl_freebsd.c new file mode 100644 index 0000000000..247248caac --- /dev/null +++ b/bacula/src/filed/bacl_freebsd.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 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 "bacl_freebsd.h" + +#if defined(HAVE_FREEBSD_OS) + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +/* + * 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 +}; + +/* + * OS specific constructor + */ +BACL_FreeBSD::BACL_FreeBSD(){ + + set_acl_streams(os_acl_streams, os_default_acl_streams); +}; + +/* + * Translates Bacula internal acl representation into acl type + * + * in: + * bacltype - internal Bacula acl type (BACL_type) + * out: + * acl_type_t - os dependent acl type + * when failed - ACL_TYPE_NONE is returned + */ +acl_type_t BACL_FreeBSD::get_acltype(BACL_type bacltype){ + + acl_type_t acltype; + + switch (bacltype){ +#ifdef HAVE_ACL_TYPE_NFS4 + case BACL_TYPE_NFS4: + acltype = ACL_TYPE_NFS4; + break; +#endif + case BACL_TYPE_ACCESS: + acltype = ACL_TYPE_ACCESS; + break; + case BACL_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 BACL_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 BACL_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_BACL_ok - check successful, lets setup bacltype variable + * bRC_BACL_error - in case of error + * bRC_BACL_skip - you should skip all other routine + * bRC_BACL_cont - you need to continue your routine + */ +bRC_BACL BACL_FreeBSD::check_bacltype (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_BACL_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_BACL_error; + } + } + case 0: + /* continue the routine */ + return bRC_BACL_cont; + default: + break; + } + return bRC_BACL_ok; +}; + +/* + * Perform OS specific ACL backup + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_FreeBSD::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_BACL rc; + BACL_type bacltype = BACL_TYPE_NONE; + +#if defined(_PC_ACL_NFS4) + /* + * Check if filesystem supports NFS4 acls. + */ + rc = check_bacltype(jcr, _PC_ACL_NFS4); + switch (rc){ + case bRC_BACL_ok: + bacltype = BACL_TYPE_NFS4; + break; + case bRC_BACL_skip: + return bRC_BACL_ok; + case bRC_BACL_cont: + break; + default: + /* errors */ + return rc; + } +#endif + if (bacltype == BACL_TYPE_NONE){ + /* + * Check if filesystem supports POSIX acls. + */ + rc = check_bacltype(jcr, _PC_ACL_EXTENDED); + switch (rc){ + case bRC_BACL_ok: + bacltype = BACL_TYPE_ACCESS; + break; + case bRC_BACL_skip: + return bRC_BACL_ok; + case bRC_BACL_cont: + break; + default: + /* errors */ + return rc; + } + } + + /* no ACL's available for file, so skip this filesystem */ + if (bacltype == BACL_TYPE_NONE){ + clear_flag(BACL_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_BACL_ok; + } + + switch (bacltype){ + case BACL_TYPE_NFS4: + /* + * Read NFS4 ACLs + */ + if (os_get_acl(jcr, BACL_TYPE_NFS4) == bRC_BACL_fatal) + return bRC_BACL_fatal; + + if (get_content_len() > 0){ + if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_NFS4) == bRC_BACL_fatal) + return bRC_BACL_fatal; + } + break; + case BACL_TYPE_ACCESS: + /* + * Read access ACLs + */ + if (os_get_acl(jcr, BACL_TYPE_ACCESS) == bRC_BACL_fatal) + return bRC_BACL_fatal; + + if (get_content_len() > 0){ + if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_ACCESS) == bRC_BACL_fatal) + return bRC_BACL_fatal; + } + + /* + * Directories can have default ACLs too + */ + if (ff_pkt->type == FT_DIREND){ + if (os_get_acl(jcr, BACL_TYPE_DEFAULT) == bRC_BACL_fatal) + return bRC_BACL_fatal; + if (get_content_len() > 0){ + if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_DEFAULT) == bRC_BACL_fatal) + return bRC_BACL_fatal; + } + } + break; + default: + break; + } + + return bRC_BACL_ok; +}; + +/* + * Perform OS specific ACL restore + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_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_BACL_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_BACL_error; + } + } + case 0: + clear_flag(BACL_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_BACL_error; + default: + break; + } + + switch (stream){ + case STREAM_UNIX_ACCESS_ACL: + case STREAM_XACL_FREEBSD_ACCESS: + return os_set_acl(jcr, BACL_TYPE_ACCESS, content, length); + case STREAM_UNIX_DEFAULT_ACL: + case STREAM_XACL_FREEBSD_DEFAULT: + return os_set_acl(jcr, BACL_TYPE_DEFAULT, content, length); + case STREAM_XACL_FREEBSD_NFS4: + return os_set_acl(jcr, BACL_TYPE_NFS4, content, length); + default: + break; + } + return bRC_BACL_error; +}; + +/* + * Low level OS specific runtime to get ACL data from file. + * The ACL data is set in internal content buffer + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_FreeBSD::os_get_acl(JCR *jcr, BACL_type bacltype){ + + acl_t acl; + acl_type_t acltype; + char *acltext; + bRC_BACL rc = bRC_BACL_ok; + + acltype = get_acltype(bacltype); + 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 simple ACL which correspond to standard permissions only */ + if (bacltype == BACL_TYPE_ACCESS && acl_issimple(acl)){ + goto bail_out; + } + +#if defined(_PC_ACL_NFS4) + if (bacltype == BACL_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_BACL_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_BACL_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(BACL_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_BACL_error; + break; + } + } + +bail_out: + if (acl){ + acl_free(acl); + } + /* + * it is a bit of hardcore to clear a pool memory 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 substitution of empty string. + */ + set_content(NULL); + return rc; +}; + +/* + * Low level OS specific runtime to set ACL data on file + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_FreeBSD::os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length){ + + acl_t acl; + acl_type_t acltype; + + acltype = get_acltype(bacltype); + 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_BACL_ok; + } + + berrno be; + switch (errno){ + case ENOENT: + return bRC_BACL_ok; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support acl's we clear the + * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(BACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname); + return bRC_BACL_error; + default: + Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + return bRC_BACL_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_BACL_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_BACL_ok; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support ACLs we clear the + * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(BACL_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_BACL_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_BACL_error; + } + } + acl_free(acl); + return bRC_BACL_ok; +}; + +#endif /* HAVE_ACL */ + +#endif /* HAVE_FREEBSD_OS */ diff --git a/bacula/src/filed/bacl_freebsd.h b/bacula/src/filed/bacl_freebsd.h new file mode 100644 index 0000000000..1800dd1577 --- /dev/null +++ b/bacula/src/filed/bacl_freebsd.h @@ -0,0 +1,70 @@ +/* + 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 code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BACL_FreeBSD_H_ +#define __BACL_FreeBSD_H_ + +#if defined(HAVE_FREEBSD_OS) +#include + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +#ifdef HAVE_SYS_ACL_H +#include +#else +#error "configure failed to detect availability of sys/acl.h" +#endif + +#ifdef HAVE_LIBUTIL_H +#include +#endif + +/* + * + * + */ +class BACL_FreeBSD : public BACL { +private: + bRC_BACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_BACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length); + bRC_BACL os_get_acl(JCR *jcr, BACL_type bacltype); + bRC_BACL os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length); + /* requires acl.h available */ + bool acl_issimple(acl_t acl); + acl_type_t get_acltype(BACL_type bacltype); + int acl_nrentries(acl_t acl); + bRC_BACL check_bacltype (JCR *jcr, int name); +public: + BACL_FreeBSD (); +}; + +#endif /* HAVE_ACL */ + +#endif /* HAVE_FREEBSD_OS */ + +#endif /* __BACL_FreeBSD_H_ */ diff --git a/bacula/src/filed/bacl_linux.c b/bacula/src/filed/bacl_linux.c new file mode 100644 index 0000000000..33bf27bc4f --- /dev/null +++ b/bacula/src/filed/bacl_linux.c @@ -0,0 +1,344 @@ +/* + 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 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 "bacl_linux.h" + +#if defined(HAVE_LINUX_OS) + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +/* + * 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 +}; + +/* + * OS specific constructor + */ +BACL_Linux::BACL_Linux(){ + set_acl_streams(os_acl_streams, os_default_acl_streams); +}; + +/* + * Translates Bacula internal acl representation into + * acl type + * + * in: + * bacltype - internal Bacula acl type (BACL_type) + * out: + * acl_type_t - os dependent acl type + * when failed - ACL_TYPE_NONE is returned + */ +acl_type_t BACL_Linux::get_acltype(BACL_type bacltype){ + + acl_type_t acltype; + + switch (bacltype){ + case BACL_TYPE_ACCESS: + acltype = ACL_TYPE_ACCESS; + break; + case BACL_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 BACL_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 BACL_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 specific ACL backup + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_Linux::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + return generic_backup_acl(jcr, ff_pkt); +}; + +/* + * Perform OS specific ACL restore + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_Linux::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){ + return generic_restore_acl(jcr, stream); +}; + +/* + * Low level OS specific runtime to get ACL data from file. The ACL data is set in internal content buffer. + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_Linux::os_get_acl(JCR *jcr, BACL_type bacltype){ + + acl_t acl; + acl_type_t acltype; + char *acltext; + bRC_BACL rc = bRC_BACL_ok; + + /* check input data */ + if (jcr == NULL){ + return bRC_BACL_inval; + } + + acltype = get_acltype(bacltype); + 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 simple ACL which correspond to standard permissions only */ + if (bacltype == BACL_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_BACL_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_BACL_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(BACL_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_BACL_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 specific runtime to set ACL data on file + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_Linux::os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length){ + + acl_t acl; + acl_type_t acltype; + + /* check input data */ + if (jcr == NULL || content == NULL){ + return bRC_BACL_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_BACL_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_BACL_error; + } + + /* handle different acl types for Linux */ + acltype = get_acltype(bacltype); + 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_BACL_ok; + } + + berrno be; + switch (errno){ + case ENOENT: + return bRC_BACL_ok; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support acl's we clear the + * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(BACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname); + return bRC_BACL_error; + default: + Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); + return bRC_BACL_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_BACL_ok; + case ENOTSUP: + /* + * If the filesystem reports it doesn't support ACLs we clear the + * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(BACL_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_BACL_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_BACL_error; + } + } + acl_free(acl); + return bRC_BACL_ok; +}; + +#endif /* HAVE_ACL */ + +#endif /* HAVE_LINUX_OS */ diff --git a/bacula/src/filed/bacl_linux.h b/bacula/src/filed/bacl_linux.h new file mode 100644 index 0000000000..4ed131d6f4 --- /dev/null +++ b/bacula/src/filed/bacl_linux.h @@ -0,0 +1,64 @@ +/* + 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 code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BACL_LINUX_H_ +#define __BACL_LINUX_H_ + +#if defined(HAVE_LINUX_OS) +#include + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +#ifdef HAVE_SYS_ACL_H +#include +#else +#error "configure failed to detect availability of sys/acl.h" +#endif + +/* + * + * + */ +class BACL_Linux : public BACL { +private: + bRC_BACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_BACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length); + bRC_BACL os_get_acl(JCR *jcr, BACL_type bacltype); + bRC_BACL os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length); + acl_type_t get_acltype(BACL_type bacltype); + int acl_nrentries(acl_t acl); + bool acl_issimple(acl_t acl); +public: + BACL_Linux (); +}; + +#endif /* HAVE_ACL */ + +#endif /* HAVE_LINUX_OS */ + +#endif /* __BACL_LINUX_H_ */ diff --git a/bacula/src/filed/bacl_osx.c b/bacula/src/filed/bacl_osx.c new file mode 100644 index 0000000000..82a3d749fd --- /dev/null +++ b/bacula/src/filed/bacl_osx.c @@ -0,0 +1,294 @@ +/* + 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 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 "bacl_osx.h" + +#if defined(HAVE_DARWIN_OS) + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +/* + * 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 +}; + +/* + * OS specific constructor + */ +BACL_OSX::BACL_OSX(){ + + set_acl_streams(os_acl_streams, os_default_acl_streams); +}; + +/* + * Translates Bacula internal acl representation into + * acl type + * + * in: + * bacltype - internal Bacula acl type (BACL_type) + * out: + * acl_type_t - os dependent acl type + * when failed - ACL_TYPE_NONE is returned + */ +acl_type_t BACL_OSX::get_acltype(BACL_type bacltype){ + + acl_type_t acltype; + + switch (bacltype){ + case BACL_TYPE_ACCESS: + acltype = ACL_TYPE_ACCESS; + break; + #ifdef HAVE_ACL_TYPE_EXTENDED + case BACL_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 BACL_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 specific ACL backup + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_OSX::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + + /* check input data */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_BACL_inval; + } + +#if defined(HAVE_ACL_TYPE_EXTENDED) + /* + * Use BACL_TYPE_EXTENDED only when available + */ + Dmsg0(400, "MacOSX Extended ACL computed\n"); + if (os_get_acl(jcr, BACL_TYPE_EXTENDED) == bRC_BACL_fatal){ + return bRC_BACL_fatal; + } +#else + Dmsg0(400, "MacOSX standard ACL computed\n"); + if (os_get_acl(jcr, BACL_TYPE_ACCESS) == bRC_BACL_fatal){ + return bRC_BACL_fatal; + } +#endif + + return send_acl_stream(jcr, STREAM_XACL_DARWIN_ACCESS); +}; + +/* + * Perform OS specific ACL restore + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_OSX::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){ + +#if defined(HAVE_ACL_TYPE_EXTENDED) + return os_set_acl(jcr, BACL_TYPE_EXTENDED, content, length); +#else + return os_set_acl(jcr, BACL_TYPE_ACCESS, content, length); +#endif +}; + +/* + * Low level OS specific runtime to get ACL data from file. The ACL data is set in internal content buffer + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_OSX::os_get_acl(JCR *jcr, BACL_type bacltype){ + + acl_t acl; + acl_type_t acltype; + char *acltext; + bRC_BACL rc = bRC_BACL_ok; + + /* check input data */ + if (jcr == NULL){ + return bRC_BACL_inval; + } + + acltype = get_acltype(bacltype); + 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_BACL_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_BACL_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(BACL_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_BACL_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 specific runtime to set ACL data on file + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_OSX::os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length){ + + acl_t acl; + acl_type_t acltype; + + /* check input data */ + if (jcr == NULL || content == NULL){ + return bRC_BACL_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_BACL_error; + } + + acltype = get_acltype(bacltype); + + /* + * 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_BACL_ok; + case EOPNOTSUPP: + /* + * If the filesystem reports it doesn't support ACLs we clear the + * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files + * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(BACL_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_BACL_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_BACL_error; + } + } + acl_free(acl); + return bRC_BACL_ok; +}; + +#endif /* HAVE_ACL */ + +#endif /* HAVE_DARWIN_OS */ diff --git a/bacula/src/filed/bacl_osx.h b/bacula/src/filed/bacl_osx.h new file mode 100644 index 0000000000..a148dd08dd --- /dev/null +++ b/bacula/src/filed/bacl_osx.h @@ -0,0 +1,64 @@ +/* + 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 code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BACL_OSX_H_ +#define __BACL_OSX_H_ + +#if defined(HAVE_DARWIN_OS) +#include + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +#ifdef HAVE_SYS_ACL_H +#include +#else +#error "configure failed to detect availability of sys/acl.h" +#endif + +/* + * + * + */ +class BACL_OSX : public BACL { +private: + bRC_BACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_BACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length); + bRC_BACL os_get_acl(JCR *jcr, BACL_type bacltype); + bRC_BACL os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length); + /* requires acl.h available */ + acl_type_t get_acltype(BACL_type bacltype); + int acl_nrentries(acl_t acl); +public: + BACL_OSX (); +}; + +#endif /* HAVE_ACL */ + +#endif /* HAVE_DARWIN_OS */ + +#endif /* __BACL_OSX_H_ */ diff --git a/bacula/src/filed/bacl_solaris.c b/bacula/src/filed/bacl_solaris.c new file mode 100644 index 0000000000..a751ce4fe8 --- /dev/null +++ b/bacula/src/filed/bacl_solaris.c @@ -0,0 +1,323 @@ +/* + 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 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 "bacl_solaris.h" + +#if defined(HAVE_SUN_OS) + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +/* + * 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 +}; + +/* + * OS specific constructor + */ +BACL_Solaris::BACL_Solaris(){ + + set_acl_streams(os_acl_streams, os_default_acl_streams); + cache = NULL; +}; + +/* + * OS specific destructor + */ +BACL_Solaris::~BACL_Solaris(){}; + +/* + * 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_BACL_ok - check successful, lets setup bacltype variable + * bRC_BACL_error - in case of error + * bRC_BACL_skip - you should skip all other routine + */ +bRC_BACL BACL_Solaris::check_bacltype (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_BACL_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_BACL_error; + } + } + case 0: + /* No support for ACLs */ + clear_flag(BACL_FLAG_NATIVE); + set_content(NULL); + return bRC_BACL_skip; + default: + break; + } + return bRC_BACL_ok; +}; + +/* + * Perform OS specific ACL backup + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_Solaris::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_BACL rc; + int stream; + + /* + * See if filesystem supports acls. + */ + rc = check_bacltype(jcr, _PC_ACL_ENABLED); + switch (rc){ + case bRC_BACL_ok: + break; + case bRC_BACL_skip: + return bRC_BACL_ok; + default: + /* errors */ + return rc; + } + + rc = os_get_acl(jcr, &stream); + switch (rc){ + case bRC_BACL_ok: + if (get_content_len() > 0){ + if (send_acl_stream(jcr, stream) == bRC_BACL_fatal){ + return bRC_BACL_fatal; + } + } + break; + default: + return rc; + } + + return bRC_BACL_ok; +}; + +/* + * Perform OS specific ACL restore + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_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_BACL_error; + } + + switch (aclrc){ + case -1: { + berrno be; + + switch (errno){ + case ENOENT: + return bRC_BACL_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_BACL_error; + } + } + case 0: + clear_flag(BACL_FLAG_NATIVE); + Mmsg(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"), jcr->last_fname); + return bRC_BACL_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_BACL_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_BACL_error; + } + break; + default: + break; + } + + return os_set_acl(jcr, stream, content, length); +}; + +/* + * Low level OS specific runtime to get ACL data from file. The ACL data is set in internal content buffer + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_Solaris::os_get_acl(JCR *jcr, int *stream){ + + int flags; + acl_t *aclp; + char *acl_text; + bRC_BACL rc = bRC_BACL_fatal; + + if (!stream){ + return bRC_BACL_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_BACL_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_BACL_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_BACL_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_BACL_error; + break; + } + + acl_free(aclp); + } + return rc; +}; + +/* + * Low level OS specific runtime to set ACL data on file + * + * in/out - check API at bacl.h + */ +bRC_BACL BACL_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_BACL_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_BACL_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_BACL_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_BACL_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_BACL_error; + } + } + + acl_free(aclp); + return bRC_BACL_ok; +}; + +#endif /* HAVE_ACL */ + +#endif /* HAVE_SUN_OS */ diff --git a/bacula/src/filed/bacl_solaris.h b/bacula/src/filed/bacl_solaris.h new file mode 100644 index 0000000000..8a30b9ca74 --- /dev/null +++ b/bacula/src/filed/bacl_solaris.h @@ -0,0 +1,84 @@ +/* + 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 code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BACL_Solaris_H_ +#define __BACL_Solaris_H_ + +#if defined(HAVE_SUN_OS) + +/* check if ACL support is enabled */ +#if defined(HAVE_ACL) + +#ifdef HAVE_SYS_ACL_H +#include +#else +#error "configure failed to detect availability of sys/acl.h" +#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 + +/* + * + * + */ +class BACL_Solaris : public BACL { +private: + alist * cache; + bRC_BACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt); + bRC_BACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length); + bRC_BACL os_get_acl(JCR *jcr, int *stream); + bRC_BACL os_set_acl(JCR *jcr, int stream, char *content, uint32_t length); + /* requires acl.h available */ + bRC_BACL check_bacltype (JCR *jcr, int name); +public: + BACL_Solaris (); + ~BACL_Solaris (); +}; + +#endif /* HAVE_ACL */ + +#endif /* HAVE_SUN_OS */ + +#endif /* __BACL_Solaris_H_ */ diff --git a/bacula/src/filed/bxattr.c b/bacula/src/filed/bxattr.c new file mode 100644 index 0000000000..a4486b40f9 --- /dev/null +++ b/bacula/src/filed/bxattr.c @@ -0,0 +1,953 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 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 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 XATTR in Bacula Enterprise. + * The runtime consist of two parts: + * 1. OS independent class: BXATTR + * 2. OS dependent subclass: BXATTR_* + * + * OS dependent subclasses are available for the following OS: + * - Darwin (OSX) + * - FreeBSD + * - Linux + * - Solaris + * + * OS depended subclasses in progress: + * - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface) + * - HPUX + * - IRIX + * - Tru64 + * + * 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. + * The behavior above is backward compatibility with previous Bacula implementation + * we need to maintain. + * + * During OS specific implementation of BXATTR you need to implement a following methods: + * + * [bxattr] - indicates bxattr function/method to call + * [os] - indicates OS specific function, which could be different on specific OS + * (we use a Linux api calls as an example) + * + * ::os_get_xattr_names (JCR *jcr, int namespace, POOLMEM ** pxlist, uint32_t * xlen) + * + * 1. get a size of the extended attributes list for the file - llistxattr[os] + * in most os'es it is required to have a sufficient space for attributes list + * and we wont allocate too much and too low space + * 2. allocate the buffer of required space + * 3. get an extended attributes 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 attribute value for the file - lgetxattr[os] + * in most os'es it is required to have a sufficient space for attribute value + * and we wont allocate too much and too low space + * 2. allocate the buffer of required space + * 3. get an extended attribute 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[bxattr] + * 1B. for every attribute name get a value - os_get_xattr_value[bxattr] + * You should skip some OS specific attributes like ACL attributes or NFS4; you can use + * check_xattr_skiplists[bxattr] for this + * 1C. build a list [type alist] of name/value pairs stored in BXATTR_xattr struct + * 2. if the xattr list is not empty then serialize the list using serialize_xattr_stream[bxattr] + * 3. call send_xattr_stream[bxattr] + * + * ::os_set_xattr (JCR *jcr, BXATTR_xattr *xattr) + * + * 1. set xattr on file using name/value in xattr - lsetxattr[os] + * 2. if xattr not supported on filesystem - call clear_flag(BXATTR_FLAG_NATIVE)[bxattr] + * + * ::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[bxattr] to set this attribute on file + */ + +#include "bacula.h" +#include "filed.h" +#include "fd_plugins.h" + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +/* + * This is a constructor of the base BXATTR class which is OS independent + * + * - for initialization it uses ::init() + * + */ +BXATTR::BXATTR (){ + init(); +}; + +/* + * This is a destructor of the BXATTR class + */ +BXATTR::~BXATTR (){ + free_pool_memory(content); +}; + +/* + * Initialization routine + * - initializes all variables to required status + * - allocates required memory + */ +void BXATTR::init(){ + +#if defined(HAVE_XATTR) + xattr_ena = TRUE; +#else + xattr_ena = FALSE; +#endif + + /* generic variables */ + flags = BXATTR_FLAG_NONE; + current_dev = 0; + content = get_pool_memory(PM_BSOCK); /* it is better to have a 4k buffer */ + content_len = 0; + xattr_nr_errors = 0; + xattr_streams = NULL; + xattr_skiplist = NULL; + xattr_acl_skiplist = NULL; +}; + +/* + * Enables XATTR handling in runtime, could be disabled with disable_xattr + * when XATTR is not configured then cannot change status + */ +void BXATTR::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 BXATTR::disable_xattr(){ + xattr_ena = FALSE; +}; + +/* + * Copies a text into a content variable and sets a content_len respectively + * + * in: + * text - a standard null terminated string + * out: + * pointer to content variable to use externally + */ +POOLMEM * BXATTR::set_content(char *text){ + content_len = pm_strcpy(&content, text); + if (content_len > 0){ + /* count the nul terminated char */ + content_len++; + } + // Dmsg2(400, "BXATTR::set_content: %p %i\n", text, content_len); + 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 * BXATTR::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_BXATTR_ok - change of device checked and finish successful + * bRC_BXATTR_error - encountered error + * bRC_BXATTR_skip - cannot verify device - no file found + * bRC_BXATTR_inval - invalid input data + */ +bRC_BXATTR BXATTR::check_dev (JCR *jcr){ + + int lst; + struct stat st; + + /* sanity check of input variables */ + if (jcr == NULL || jcr->last_fname == NULL){ + return bRC_BXATTR_inval; + } + + lst = lstat(jcr->last_fname, &st); + switch (lst){ + case -1: { + berrno be; + switch (errno){ + case ENOENT: + return bRC_BXATTR_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_BXATTR_error; + } + break; + } + case 0: + break; + } + + check_dev(jcr, st.st_dev); + + return bRC_BXATTR_ok; +}; + +/* + * Check if we changed the device, if so setup a flags + * + * in: + * jcr - Job Control Record + * out: + * internal flags status set + */ +void BXATTR::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 = BXATTR_FLAG_NONE; + set_flag(BXATTR_FLAG_NATIVE); + 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_BXATTR_inval - when supplied variables are incorrect + * bRC_BXATTR_fatal - when we can't send data to the SD + * bRC_BXATTR_ok - send finish without errors + */ +bRC_BXATTR BXATTR::send_xattr_stream(JCR *jcr, int stream){ + + BSOCK * sd; + POOLMEM * msgsave; +#ifdef FD_NO_SEND_TEST + return bRC_BXATTR_ok; +#endif + + /* sanity check of input variables */ + if (jcr == NULL || jcr->store_bsock == NULL){ + return bRC_BXATTR_inval; + } + if (content_len <= 0){ + return bRC_BXATTR_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_BXATTR_fatal; + } + + /* send the buffer to the storage daemon */ + Dmsg1(400, "Backing up XATTR: %i\n", content_len); +#if 0 + POOL_MEM tmp(PM_FNAME); + pm_memcpy(tmp, content, content_len); + Dmsg2(400, "Backing up XATTR: (%i) <%s>\n", strlen(tmp.addr()), tmp.c_str()); +#endif + 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_BXATTR_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_BXATTR_fatal; + } + Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname); + return bRC_BXATTR_ok; +}; + +/* + * The main public backup method for XATTR + * + * in: + * jcr - Job Control Record + * ff_pkt - file backup record + * out: + * bRC_BXATTR_fatal - when XATTR backup is not compiled in Bacula + * bRC_BXATTR_ok - backup finish without problems + * bRC_BXATTR_error - when you can't backup xattr data because some error + */ +bRC_BXATTR BXATTR::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_BXATTR_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_BXATTR rc; + + jcr->errmsg[0] = 0; + /* check if we have a plugin generated backup */ + if (ff_pkt->cmd_plugin){ + rc = backup_plugin_xattr(jcr, ff_pkt); + } else { + /* Check for xattrsupport flag */ + if (!(ff_pkt->flags & FO_XATTR && !ff_pkt->cmd_plugin)){ + return bRC_BXATTR_ok; + } + + check_dev(jcr, ff_pkt->statp.st_dev); + + if (flags & BXATTR_FLAG_NATIVE){ + Dmsg0(400, "make Native XATTR call\n"); + rc = os_backup_xattr(jcr, ff_pkt); + } else { + /* skip xattr backup */ + return bRC_BXATTR_ok; + } + + } + + if (rc == bRC_BXATTR_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_BXATTR_ok; + } + return rc; + } + return bRC_BXATTR_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_BXATTR_fatal - when XATTR restore is not compiled in Bacula + * bRC_BXATTR_ok - restore finish without problems + * bRC_BXATTR_error - when you can't restore a stream because some error + */ +bRC_BXATTR BXATTR::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_BXATTR_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_BXATTR rc; + + /* check_dev supported on real fs only */ + if (stream != STREAM_XACL_PLUGIN_XATTR){ + rc = check_dev(jcr); + + switch (rc){ + case bRC_BXATTR_skip: + return bRC_BXATTR_ok; + case bRC_BXATTR_ok: + break; + default: + return rc; + } + } + + /* copy a data into a content buffer */ + set_content(data, length); + + switch (stream){ + case STREAM_XACL_PLUGIN_XATTR: + return restore_plugin_xattr(jcr); + default: + if (flags & BXATTR_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_BXATTR_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_BXATTR_error; + } + return bRC_BXATTR_ok; +#endif +}; + +/* + * Checks if supplied xattr attribute name is indicated on OS specific 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 specific skip lists and should be skipped during backup + * FALSE - the attribute should be saved on backup stream + */ +bool BXATTR::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 specific 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_BXATTR_ok - xattr backup ok or no xattr to backup found + * bRC_BXATTR_error/fatal - an error or fatal error occurred + * bRC_BXATTR_inval - input variables was invalid + */ +bRC_BXATTR BXATTR::generic_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_BXATTR 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; + BXATTR_xattr *xattr; + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_BXATTR_inval; + } + + /* xlist is allocated as POOLMEM by os_get_xattr_names */ + rc = os_get_xattr_names(jcr, &xlist, &xlen); + switch (rc){ + case bRC_BXATTR_ok: + /* it's ok, so go further */ + break; + case bRC_BXATTR_skip: + case bRC_BXATTR_cont: + /* no xattr available, so skip rest of it */ + return bRC_BXATTR_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_BXATTR_ok: + /* it's ok, so go further */ + break; + case bRC_BXATTR_skip: + /* no xattr available, so skip rest of it */ + free_pool_memory(xlist); + return bRC_BXATTR_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 = (BXATTR_xattr*)malloc(sizeof(BXATTR_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_BXATTR_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_BXATTR_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 specific methods for + * setting XATTR data on file. + * + * in: + * jcr - Job Control Record + * stream - a stream number to restore + * out: + * bRC_BXATTR_ok - restore of acl's was successful + * bRC_BXATTR_error - was an error during xattr restore + * bRC_BXATTR_fatal - was a fatal error during xattr restore + * bRC_BXATTR_inval - input variables was invalid + */ +bRC_BXATTR BXATTR::generic_restore_xattr (JCR *jcr, int stream){ + + bRC_BXATTR rc = bRC_BXATTR_ok; + alist *xattr_list; + BXATTR_xattr *xattr; + + /* sanity check of input variables */ + if (jcr == NULL){ + return bRC_BXATTR_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_BXATTR_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; +}; + +/* + * Perform a generic XATTR backup using a plugin. It calls the plugin API to + * get required xattr data from plugin. + * + * in: + * jcr - Job Control Record + * ff_pkt - file to backup control package + * out: + * bRC_BXATTR_ok - backup of xattrs was successful + * bRC_BXATTR_fatal - was an error during xattr backup + */ +bRC_BXATTR BXATTR::backup_plugin_xattr (JCR *jcr, FF_PKT *ff_pkt) +{ + int status; + char *data; + + /* sanity check of input variables */ + if (jcr == NULL || ff_pkt == NULL){ + return bRC_BXATTR_inval; + } + + while ((status = plugin_backup_xattr(jcr, ff_pkt, &data)) > 0){ + /* data is a plugin buffer which contains data to backup + * and status is a length of the buffer when > 0 */ + set_content(data, status); + if (send_xattr_stream(jcr, STREAM_XACL_PLUGIN_XATTR) == bRC_BXATTR_fatal){ + return bRC_BXATTR_fatal; + } + } + if (status < 0){ + /* error */ + return bRC_BXATTR_error; + } + + return bRC_BXATTR_ok; +}; + +/* + * Perform a generic XATTR restore using a plugin. It calls the plugin API to + * send acl data to plugin. + * + * in: + * jcr - Job Control Record + * stream - a stream number to restore + * out: + * bRC_BXATTR_ok - restore of xattrs was successful + * bRC_BXATTR_error - was an error during xattrs restore + * bRC_BXATTR_fatal - was a fatal error during xattrs restore or input data + * is invalid + */ +bRC_BXATTR BXATTR::restore_plugin_xattr (JCR *jcr) +{ + /* sanity check of input variables */ + if (jcr == NULL){ + return bRC_BXATTR_inval; + } + + if (!plugin_restore_xattr(jcr, content, content_len)){ + /* error */ + return bRC_BXATTR_error; + } + + return bRC_BXATTR_ok; +} + +/* + * Initialize a variable xattr_streams for a specified OS. + * The rutine should be called from object instance constructor + * + * in: + * pxattr - xattr streams supported for specific OS + */ +void BXATTR::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 specific OS + * pxattr_acl - xattr acl names skip list for specific OS + */ +void BXATTR::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_BXATTR_ok - when serialization was perfect + * bRC_BXATTR_inval - when we have invalid variables + * bRC_BXATTR_error - illegal attribute name + */ +bRC_BXATTR BXATTR::serialize_xattr_stream(JCR *jcr, uint32_t len, alist *list){ + + ser_declare; + BXATTR_xattr *xattr; + + /* sanity check of input variables */ + if (jcr == NULL || list == NULL){ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_ok - when unserialize was perfect + * bRC_BXATTR_inval - when we have invalid variables + * list - key/value pairs populated xattr list + */ +bRC_BXATTR BXATTR::unserialize_xattr_stream(JCR *jcr, char *content, uint32_t length, alist *list){ + + unser_declare; + uint32_t magic; + BXATTR_xattr *xattr; + + /* sanity check of input variables */ + if (jcr == NULL || content == NULL || list == NULL){ + return bRC_BXATTR_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_BXATTR_error; + } + /* first attribute name length */ + xattr = (BXATTR_xattr *)malloc(sizeof(BXATTR_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_BXATTR_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_BXATTR_ok; +}; + +#include "bxattr_osx.h" +#include "bxattr_linux.h" +#include "bxattr_freebsd.h" +#include "bxattr_solaris.h" +// #include "bxattr_aix.h" + +/* + * Creating the current instance of the BXATTR for a supported OS + */ +void *new_bxattr() +{ +#if defined(HAVE_DARWIN_OS) + return new BXATTR_OSX(); +#elif defined(HAVE_LINUX_OS) + return new BXATTR_Linux(); +#elif defined(HAVE_FREEBSD_OS) + return new BXATTR_FreeBSD(); +#elif defined(HAVE_HURD_OS) + return new BXATTR_Hurd(); +#elif defined(HAVE_AIX_OS) + return new BXATTR_AIX(); +#elif defined(HAVE_IRIX_OS) + return new BXATTR_IRIX(); +#elif defined(HAVE_OSF1_OS) + return new BXATTR_OSF1(); +#elif defined(HAVE_SUN_OS) + return new BXATTR_Solaris(); +#else + return NULL; +#endif +}; + +#endif /* HAVE_XATTR */ diff --git a/bacula/src/filed/bxattr.h b/bacula/src/filed/bxattr.h new file mode 100644 index 0000000000..26e11d2b71 --- /dev/null +++ b/bacula/src/filed/bxattr.h @@ -0,0 +1,231 @@ +/* + 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 XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BXATTR_H_ +#define __BXATTR_H_ + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +/* + * 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_BXATTR { + bRC_BXATTR_inval = -3, // input data invalid + bRC_BXATTR_fatal = -2, // a fatal error + bRC_BXATTR_error = -1, // standard error + bRC_BXATTR_ok = 0, // success + bRC_BXATTR_skip = 1, // processing should skip current runtime + bRC_BXATTR_cont = 2 // processing should skip current element + // and continue with next one +}; + +/* + * Flags which control what XATTR engine + * to use for backup/restore + */ +#define BXATTR_FLAG_NONE 0 +#define BXATTR_FLAG_NATIVE 0x01 +#define BXATTR_FLAG_AFS 0x02 +#define BXATTR_FLAG_PLUGIN 0x04 + +/* + * Extended attribute (xattr) list element. + * + * Every xattr consist of a Key=>Value pair where + * both could be a binary data. + */ +struct BXATTR_xattr { + uint32_t name_len; + char *name; + uint32_t value_len; + char *value; +}; + +/* + * Basic XATTR class which is a foundation for any other OS specific 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 XATTR generic functions and OS + * independent API, i.e. for AFS XATTR or Plugins XATTR (future functionality). + */ +class BXATTR { +private: + bool xattr_ena; + uint32_t flags; + uint32_t current_dev; + POOLMEM *content; + uint32_t content_len; + uint32_t xattr_nr_errors; + const int *xattr_streams; + const char **xattr_skiplist; + const char **xattr_acl_skiplist; + + void init(); + + /** + * Perform OS specific XATTR backup. + * + * in: + * jcr - Job Control Record + * ff_pkt - file to backup control package + * out: + * bRC_BXATTR_ok - xattr backup ok or no xattr to backup found + * bRC_BXATTR_error/fatal - an error or fatal error occurred + */ + virtual bRC_BXATTR os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){return bRC_BXATTR_fatal;}; + + /** + * Perform OS specific 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_BXATTR_ok - xattr backup ok or no xattr to backup found + * bRC_BXATTR_error/fatal - an error or fatal error occurred + */ + virtual bRC_BXATTR os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){return bRC_BXATTR_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_BXATTR_ok - we've got a xattr data to backup + * bRC_BXATTR_skip - no xattr data available, no fatal error, skip rest of the runtime + * bRC_BXATTR_fatal - when required buffers are unallocated + * bRC_BXATTR_error - in case of any error + */ + virtual bRC_BXATTR os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){return bRC_BXATTR_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_BXATTR_ok - we've got a xattr data which could be empty when xlen=0 + * bRC_BXATTR_skip - no xattr data available, no fatal error, skip rest of the runtime + * bRC_BXATTR_error - error getting an attribute + * bRC_BXATTR_fatal - required buffers are unallocated + */ + virtual bRC_BXATTR os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){return bRC_BXATTR_fatal;}; + + /** + * Low level OS specific runtime to set extended attribute on file + * + * in: + * jcr - Job Control Record + * xattr - the struct with attribute/value to set + * + * out: + * bRC_BXATTR_ok - setting the attribute was ok + * bRC_BXATTR_error - error during extattribute set + * bRC_BXATTR_fatal - required buffers are unallocated + */ + virtual bRC_BXATTR os_set_xattr (JCR *jcr, BXATTR_xattr *xattr){return bRC_BXATTR_fatal;}; + + void inc_xattr_errors(){ xattr_nr_errors++;}; + bRC_BXATTR check_dev (JCR *jcr); + void check_dev (JCR *jcr, uint32_t dev); + +public: + BXATTR (); + virtual ~BXATTR(); + + /* enable/disable functionality */ + void enable_xattr(); + void disable_xattr(); + + /* + * public methods used outside the class or derivatives + */ + bRC_BXATTR backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_BXATTR restore_xattr (JCR *jcr, int stream, char *content, uint32_t content_length); + + /* utility functions */ + inline uint32_t get_xattr_nr_errors(){ return xattr_nr_errors;}; + 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_BXATTR send_xattr_stream (JCR *jcr, int stream); + + /* serialize / unserialize stream */ + bRC_BXATTR unserialize_xattr_stream(JCR *jcr, char *content, uint32_t length, alist *list); + bRC_BXATTR serialize_xattr_stream(JCR *jcr, uint32_t len, alist *list); + + /* generic functions */ + bRC_BXATTR generic_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_BXATTR generic_restore_xattr (JCR *jcr, int stream); + bRC_BXATTR backup_plugin_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_BXATTR restore_plugin_xattr (JCR *jcr); +}; + +void *new_bxattr(); + +#endif /* HAVE_XATTR */ + +#endif /* __BXATTR_H_ */ diff --git a/bacula/src/filed/bxattr_freebsd.c b/bacula/src/filed/bxattr_freebsd.c new file mode 100644 index 0000000000..44d11eb8e7 --- /dev/null +++ b/bacula/src/filed/bxattr_freebsd.c @@ -0,0 +1,465 @@ +/* + 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 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 "bxattr_freebsd.h" + +#if defined(HAVE_FREEBSD_OS) + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +/* + * 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 specific constructor + */ +BXATTR_FreeBSD::BXATTR_FreeBSD() +{ + set_xattr_streams(os_xattr_streams); + set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist); +}; + +/* + * Perform OS specific extended attribute backup + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_FreeBSD::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_BXATTR 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; + BXATTR_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_BXATTR_ok: + /* it's ok, so go further */ + break; + case bRC_BXATTR_skip: + case bRC_BXATTR_cont: + /* no xattr available, so skip rest of it */ + return bRC_BXATTR_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_BXATTR_ok: + /* it's ok, so go further */ + break; + case bRC_BXATTR_skip: + /* no xattr available, so skip rest of it */ + rc = bRC_BXATTR_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 = (BXATTR_xattr*)malloc(sizeof(BXATTR_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_BXATTR_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_BXATTR_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 specific XATTR restore. Runtime is called only when stream is supported by OS. + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_FreeBSD::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){ + return generic_restore_xattr(jcr, stream); +}; + +/* + * 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 bxattr.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_BXATTR BXATTR_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_BXATTR_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_BXATTR_skip; + case EOPNOTSUPP: + /* no xattr supported on filesystem, clear a flag and skip it */ + clear_flag(BXATTR_FLAG_NATIVE); + set_content(NULL); + return bRC_BXATTR_skip; + case EPERM: + if (ns == EXTATTR_NAMESPACE_SYSTEM){ + return bRC_BXATTR_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_BXATTR_error; + } + break; + } + case 0: + /* xattr available but empty, skip it */ + return bRC_BXATTR_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_BXATTR_skip; + case EPERM: + if (ns == EXTATTR_NAMESPACE_SYSTEM){ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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 bxattr.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_BXATTR BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_ok; +}; + +/* + * Low level OS specific runtime to set extended attribute on file + * + * in/out - check API at bxattr.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_BXATTR BXATTR_FreeBSD::os_set_xattr (JCR *jcr, BXATTR_xattr *xattr){ + + char * name; + char * nspace; + int ns; + int rc; + + /* check input data */ + if (jcr == NULL || xattr == NULL){ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_error; + } + } + return bRC_BXATTR_ok; +}; + +#endif /* HAVE_XATTR */ + +#endif /* HAVE_FREEBSD_OS */ diff --git a/bacula/src/filed/bxattr_freebsd.h b/bacula/src/filed/bxattr_freebsd.h new file mode 100644 index 0000000000..b6a1576a26 --- /dev/null +++ b/bacula/src/filed/bxattr_freebsd.h @@ -0,0 +1,85 @@ +/* + 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 XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BXATTR_FreeBSD_H_ +#define __BXATTR_FreeBSD_H_ + +#if defined(HAVE_FREEBSD_OS) +#include + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +#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 BXATTR_FreeBSD : public BXATTR { +private: + bRC_BXATTR os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_BXATTR os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length); + bRC_BXATTR os_get_xattr_names (JCR *jcr, const int ns, POOLMEM **list, uint32_t *length); + bRC_BXATTR os_get_xattr_value (JCR *jcr, const int ns, char * name, char ** pvalue, uint32_t * plen); + bRC_BXATTR os_set_xattr (JCR *jcr, BXATTR_xattr *xattr); +public: + BXATTR_FreeBSD (); +}; + +#endif /* HAVE_XATTR */ + +#endif /* HAVE_FREEBSD_OS */ + +#endif /* __BXATTR_FreeBSD_H_ */ diff --git a/bacula/src/filed/bxattr_linux.c b/bacula/src/filed/bxattr_linux.c new file mode 100644 index 0000000000..6e89602599 --- /dev/null +++ b/bacula/src/filed/bxattr_linux.c @@ -0,0 +1,290 @@ +/* + 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 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 "bxattr_linux.h" + +#if defined(HAVE_LINUX_OS) + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +/* + * 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 specific constructor + */ +BXATTR_Linux::BXATTR_Linux(){ + set_xattr_streams(os_xattr_streams); + set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist); +}; + +/* + * Perform OS specific extended attribute backup + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_Linux::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + return generic_backup_xattr(jcr, ff_pkt); +}; + +/* + * Perform OS specific XATTR restore. Runtime is called only when stream is supported by OS. + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_Linux::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){ + return generic_restore_xattr(jcr, stream); +}; + +/* + * 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 bxattr.h + */ +bRC_BXATTR BXATTR_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_BXATTR_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_BXATTR_skip; + case EOPNOTSUPP: + /* no xattr supported on filesystem, clear a flag and skip it */ + clear_flag(BXATTR_FLAG_NATIVE); + set_content(NULL); + return bRC_BXATTR_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_BXATTR_error; + } + break; + } + case 0: + /* xattr available but empty, skip it */ + return bRC_BXATTR_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_BXATTR_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_BXATTR_error; + } + break; + } + default: + break; + } + /* ensure a list is nul terminated */ + list[len] = '\0'; + /* setup return data */ + *pxlist = list; + *xlen = len; + return bRC_BXATTR_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 bxattr.h + */ +bRC_BXATTR BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_ok; +}; + +/* + * Low level OS specific runtime to set extended attribute on file + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_Linux::os_set_xattr (JCR *jcr, BXATTR_xattr *xattr){ + + /* check input data */ + if (jcr == NULL || xattr == NULL){ + return bRC_BXATTR_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 + * BXATTR_FLAG_NATIVE flag so we skip XATTR restores on all other files + * on the same filesystem. The BXATTR_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(BXATTR_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_BXATTR_error; + } + } + return bRC_BXATTR_ok; +}; + +#endif /* HAVE_XATTR */ + +#endif /* HAVE_LINUX_OS */ diff --git a/bacula/src/filed/bxattr_linux.h b/bacula/src/filed/bxattr_linux.h new file mode 100644 index 0000000000..da8b6b488a --- /dev/null +++ b/bacula/src/filed/bxattr_linux.h @@ -0,0 +1,68 @@ +/* + 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 XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BXATTR_LINUX_H_ +#define __BXATTR_LINUX_H_ + +#if defined(HAVE_LINUX_OS) +#include + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +/* verify a support for XATTR on build os */ +#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 BXATTR_Linux : public BXATTR { +private: + bRC_BXATTR os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_BXATTR os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length); + bRC_BXATTR os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length); + bRC_BXATTR os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen); + bRC_BXATTR os_set_xattr (JCR *jcr, FF_PKT *ff_pkt, char *list, uint32_t length); + bRC_BXATTR os_set_xattr (JCR *jcr, BXATTR_xattr *xattr); +public: + BXATTR_Linux (); +}; + +#endif /* HAVE_XATTR */ + +#endif /* HAVE_LINUX_OS */ + +#endif /* __BXATTR_LINUX_H_ */ diff --git a/bacula/src/filed/bxattr_osx.c b/bacula/src/filed/bxattr_osx.c new file mode 100644 index 0000000000..8b1425eb0c --- /dev/null +++ b/bacula/src/filed/bxattr_osx.c @@ -0,0 +1,291 @@ +/* + 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 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 "bxattr_osx.h" + +#if defined(HAVE_DARWIN_OS) + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +/* + * Define the supported XATTR streams for this OS + */ +static const int os_xattr_streams[] = { + STREAM_XACL_DARWIN_XATTR, + 0 +}; + +static const char *os_xattr_skiplist[] = { + "com.apple.system.extendedsecurity", + "com.apple.ResourceFork", + NULL +}; + +static const char *os_xattr_acl_skiplist[] = { + "com.apple.system.Security", + NULL +}; + +/* + * OS specific constructor + */ +BXATTR_OSX::BXATTR_OSX() +{ + set_xattr_streams(os_xattr_streams); + set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist); +}; + +/* + * Perform OS specific extended attribute backup + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_OSX::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + return generic_backup_xattr(jcr, ff_pkt); +}; + +/* + * Perform OS specific XATTR restore. Runtime is called only when stream is supported by OS. + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_OSX::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){ + return generic_restore_xattr(jcr, stream); +}; + +/* + * 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 bxattr.h + */ +bRC_BXATTR BXATTR_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_BXATTR_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_BXATTR_skip; + case ENOTSUP: + /* no xattr supported on filesystem, clear a flag and skip it */ + clear_flag(BXATTR_FLAG_NATIVE); + set_content(NULL); + return bRC_BXATTR_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_BXATTR_error; + } + break; + } + case 0: + /* xattr available but empty, skip it */ + return bRC_BXATTR_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_BXATTR_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_BXATTR_error; + } + break; + } + default: + break; + } + /* ensure a list is nul terminated */ + list[len] = '\0'; + /* setup return data */ + *pxlist = list; + *xlen = len; + return bRC_BXATTR_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 bxattr.h + */ +bRC_BXATTR BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_ok; +}; + +/* + * Low level OS specific runtime to set extended attribute on file + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_OSX::os_set_xattr (JCR *jcr, BXATTR_xattr *xattr){ + + /* check input data */ + if (jcr == NULL || xattr == NULL){ + return bRC_BXATTR_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 + * BXATTR_FLAG_NATIVE flag so we skip XATTR restores on all other files + * on the same filesystem. The BXATTR_FLAG_NATIVE flag gets set again + * when we change from one filesystem to an other. + */ + clear_flag(BXATTR_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_BXATTR_error; + } + } + return bRC_BXATTR_ok; +}; + +#endif /* HAVE_XATTR */ + +#endif /* HAVE_DARWIN_OS */ diff --git a/bacula/src/filed/bxattr_osx.h b/bacula/src/filed/bxattr_osx.h new file mode 100644 index 0000000000..cc12077325 --- /dev/null +++ b/bacula/src/filed/bxattr_osx.h @@ -0,0 +1,66 @@ +/* + 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 XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BXATTR_OSX_H_ +#define __BXATTR_OSX_H_ + +#if defined(HAVE_DARWIN_OS) +#include + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +#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 BXATTR_OSX : public BXATTR { +private: + bRC_BXATTR os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_BXATTR os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length); + bRC_BXATTR os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length); + bRC_BXATTR os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen); + bRC_BXATTR os_set_xattr (JCR *jcr, BXATTR_xattr *xattr); +public: + BXATTR_OSX (); +}; + +#endif /* HAVE_XATTR */ + +#endif /* HAVE_DARWIN_OS */ + +#endif /* __BXATTR_OSX_H_ */ diff --git a/bacula/src/filed/bxattr_solaris.c b/bacula/src/filed/bxattr_solaris.c new file mode 100644 index 0000000000..c11c1c7fa7 --- /dev/null +++ b/bacula/src/filed/bxattr_solaris.c @@ -0,0 +1,911 @@ +/* + 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 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 "bxattr_solaris.h" + +#if defined(HAVE_SUN_OS) + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +/* + * 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_skiplist[] = { + "..", +#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) + VIEW_READONLY, +#endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */ + NULL +}; + +static const char *os_xattr_acl_skiplist[] = { + NULL +}; + +/* + * OS Specific constructor + */ +BXATTR_Solaris::BXATTR_Solaris(){ + + set_xattr_streams(os_xattr_streams); + set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist); + cache = NULL; +}; + +/* + * OS Specific destructor + */ +BXATTR_Solaris::~BXATTR_Solaris(){ + + delete_xattr_cache(); +}; + +/* + * Perform OS specific extended attribute backup + * + * in/out - check API at bxattr.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 attribute. 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_BXATTR BXATTR_Solaris::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){ + + bRC_BXATTR rc; + POOLMEM *xlist = NULL; + uint32_t xlen; + char *name; + char *lnkname; + uint32_t name_len; + POOLMEM *value = NULL; + uint32_t value_len; + char * bxattrtext; + uint32_t bxattrtext_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_BXATTR_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_BXATTR_ok: + /* it's ok, so go further */ + break; + case bRC_BXATTR_skip: + case bRC_BXATTR_cont: + /* no xattr available, so skip rest of it */ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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, &bxattrtext); + if (rc != bRC_BXATTR_ok){ + goto bailout; + } + bxattrtext_len = strlen(bxattrtext); + + /* + * 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_BXATTR_ok: + /* it's ok, so go further */ + break; + case bRC_BXATTR_skip: + /* no xattr available, so skip rest of it */ + rc = bRC_BXATTR_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, (bxattrtext) ? bxattrtext : "", 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, (bxattrtext) ? bxattrtext : "", 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_BXATTR_error; + goto bailout; + } + /* send stream to the sd */ + rc = send_xattr_stream(jcr, stream); + if (rc != bRC_BXATTR_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_BXATTR_ok; +}; + +/* + * BXATTR_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 * BXATTR_Solaris::find_xattr_cache(JCR *jcr, ino_t ino, char * name){ + + BXATTR_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 = (BXATTR_Solaris_Cache*) malloc (sizeof(BXATTR_Solaris_Cache)); + entry->inode = ino; + entry->name = name; + cache->append(entry); + return NULL; +} + +/* + * The function deletes a cache + * in/out - void + */ +inline void BXATTR_Solaris::delete_xattr_cache(){ + + BXATTR_Solaris_Cache *entry; + + if (cache != NULL){ + foreach_alist(entry, cache){ + free(entry); + } + delete cache; + cache = NULL; + } +} + +/* + * Perform OS specific 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 bxattr.h + */ +bRC_BXATTR BXATTR_Solaris::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){ + + bRC_BXATTR rc = bRC_BXATTR_error; + bool extended = false; + + /* check input data */ + if (jcr == NULL || content == NULL){ + return bRC_BXATTR_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 specific 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_BXATTR_ok - backup acl for extended attribute finish without problems + * bRC_BXATTR_error - backup acl unsuccessful + * bRC_BXATTR_inval - input variables are invalid (null) + * + */ +bRC_BXATTR BXATTR_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_BXATTR rc = bRC_BXATTR_error; + + /* sanity check of input variables */ + if (jcr == NULL || buffer == NULL || *buffer == NULL || fd < 0){ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_ok; +#endif /* HAVE_EXTENDED_ACL */ + return rc; +#else /* HAVE_ACL */ + return bRC_BXATTR_ok; +#endif /* HAVE_ACL */ +} + +/* + * Low level OS specific 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_BXATTR_ok - backup acl for extended attribute finish without problems + * bRC_BXATTR_inval - input variables are invalid (null) + * + */ +bRC_BXATTR BXATTR_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_BXATTR rc = bRC_BXATTR_error; + + /* sanity check of input variables */ + if (jcr == NULL || name == NULL || acltext == NULL || fd < 0){ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_error; + } + free(acls); + } + +#endif /* HAVE_EXTENDED_ACL */ + + return rc; +#else /* HAVE_ACL */ + return bRC_BXATTR_ok; +#endif /* HAVE_ACL */ +}; + +/* + * 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 bxattr.h + */ +bRC_BXATTR BXATTR_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_BXATTR_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_BXATTR_skip; + case EINVAL: + /* no xattr supported on file skip it */ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_error; + } + + *pxlist = list; + *xlen = len; + + return bRC_BXATTR_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 bxattr.h + */ +bRC_BXATTR BXATTR_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_BXATTR_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_BXATTR_skip; + case EINVAL: + /* no xattr supported on file skip it */ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_ok; +}; + +/* + * Low level OS specific runtime to set extended attribute on file + * + * in/out - check API at bxattr.h + */ +bRC_BXATTR BXATTR_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_BXATTR rc = bRC_BXATTR_ok; + + /* check input data */ + if (jcr == NULL || content == NULL){ + return bRC_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_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_BXATTR_error; + } + goto bail_out; + } + } + +#ifdef HAVE_ACL + if (strlen(acltext)){ + rc = os_set_xattr_acl(jcr, attrfd, name, acltext); + if (rc != bRC_BXATTR_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_BXATTR_error; + goto bail_out; + } + } + +bail_out: + if (attrfd != 0){ + close(attrfd); + } + if (attrdirfd != 0){ + close(attrdirfd); + } + return rc; +}; + +#endif /* HAVE_XATTR */ + +#endif /* HAVE_SUN_OS */ diff --git a/bacula/src/filed/bxattr_solaris.h b/bacula/src/filed/bxattr_solaris.h new file mode 100644 index 0000000000..806957f492 --- /dev/null +++ b/bacula/src/filed/bxattr_solaris.h @@ -0,0 +1,83 @@ +/* + 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 XATTR code written by: + * + * Radosław Korzeniewski, MMXVI + * radoslaw@korzeniewski.net, radekk@inteos.pl + * Inteos Sp. z o.o. http://www.inteos.pl/ + * + */ + +#ifndef __BXATTR_Solaris_H_ +#define __BXATTR_Solaris_H_ + +#if defined(HAVE_SUN_OS) + +/* check if XATTR support is enabled */ +#if defined(HAVE_XATTR) + +/* + * + */ +#ifdef HAVE_SYS_ATTR_H +#include +#endif + +/* + * Required for XATTR/ACL backup + */ +#ifdef HAVE_SYS_ACL_H +#include +#endif + +/* + * Cache structure in alist + */ +struct BXATTR_Solaris_Cache { + ino_t inode; + char * name; +}; + +/* + * + * + */ +class BXATTR_Solaris : public BXATTR { +private: + alist * cache; + bRC_BXATTR os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt); + bRC_BXATTR os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length); + bRC_BXATTR os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length); + bRC_BXATTR os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen); + bRC_BXATTR os_set_xattr (JCR *jcr, bool extended, char *content, uint32_t length); + bRC_BXATTR os_get_xattr_acl(JCR *jcr, int fd, char **buffer); + bRC_BXATTR os_set_xattr_acl(JCR *jcr, int fd, char *name, char *acltext); + inline char * find_xattr_cache(JCR *jcr, ino_t ino, char * name); + inline void delete_xattr_cache(); +public: + BXATTR_Solaris (); + ~BXATTR_Solaris (); +}; + +#endif /* HAVE_XATTR */ + +#endif /* HAVE_SUN_OS */ + +#endif /* __BXATTR_Solaris_H_ */ diff --git a/bacula/src/filed/fd_plugins.c b/bacula/src/filed/fd_plugins.c index beb6309c66..89f174b357 100644 --- a/bacula/src/filed/fd_plugins.c +++ b/bacula/src/filed/fd_plugins.c @@ -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. @@ -1065,6 +1065,206 @@ bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd) return true; } +/* + * The Plugin ACL data backup. We are using a new Plugin callback: + * handleXACLdata() for that. The new callback get a pointer to + * struct xacl_pkt as a main argument which consist of the following + * data: + * xacl.func - could be the one of BACL_BACKUP, BACL_RESTORE, + * BXATTR_BACKUP, BXATTR_RESTORE + * xacl.count - the length of data at the content buffer + * xacl.content - the buffer itself + * The buffer (xacl.content) is supplied by Bacula during restore and has to + * be supplied by a Plugin during backup. + * The new callback should return bRC_OK on success and bRC_Error on + * any error. + * + * in: + * jcr - Job Control Record + * ff_pkt - file save packet + * data is a pointer to variable returned + * out: + * data - the pointer to data buffer returned from plugin + * 0 - Success, no more data to save + * > 0 - Success and the number of bytes returned in **data buffer + * -1 - Error, no acls data to backup + */ +int plugin_backup_acl(JCR *jcr, FF_PKT *ff_pkt, char **data) +{ + struct xacl_pkt xacl; + Plugin *plugin = (Plugin *)jcr->plugin; + bRC rc; + + Dmsg0(dbglvl, "plugin_backup_acl\n"); + + /* check of input variables */ + if (!plugin || !jcr->plugin_ctx || !data) { + return 0; + } + + /* prepare the xacl packet */ + memset(&xacl, 0, sizeof(xacl)); + xacl.pkt_size = sizeof(xacl); + xacl.pkt_end = sizeof(xacl); + xacl.func = BACL_BACKUP; + + rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl); + + /* check out status */ + if (rc != bRC_OK){ + Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n"); + return -1; + } + if (xacl.count > 0){ + /* we have something to save, so prepare return data */ + *data = xacl.content; + return xacl.count; + } + + return 0; +} + +/* + * Called here when Bacula got ACL stream to restore but not every stream but + * a specific one: STREAM_XACL_PLUGIN_ACL which means a plugin has to + * be called. + * + * in: + * jcr - Job Control Record + * data - content to restore + * length - the length of the content to restore + * out: + * true - when successful + * false - on any Error + */ +bool plugin_restore_acl(JCR *jcr, char *data, uint32_t length) +{ + struct xacl_pkt xacl; + Plugin *plugin = (Plugin *)jcr->plugin; + bRC rc; + + Dmsg0(dbglvl, "plugin_restore_acl\n"); + + /* check of input variables */ + if (!plugin || !jcr->plugin_ctx || !data || length == 0) { + return true; + } + + /* prepare the xacl packet */ + memset(&xacl, 0, sizeof(xacl)); + xacl.pkt_size = sizeof(xacl); + xacl.pkt_end = sizeof(xacl); + xacl.func = BACL_RESTORE; + xacl.content = data; + xacl.count = length; + + rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl); + + /* check out status */ + if (rc != bRC_OK){ + Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n"); + return false; + } + + return true; +} + +/* + * The Plugin XATTR data backup. We are using a new Plugin callback: + * handleXACLdata() for that. Check plugin_backup_acl for new callback + * description. + * + * in: + * jcr - Job Control Record + * ff_pkt - file save packet + * data is a pointer to variable returned + * out: + * data - the pointer to data buffer returned from plugin + * 0 - Success, no more data to save + * >0 - Success and the number of bytes returned in **data buffer + * <0 - Error + */ +int plugin_backup_xattr(JCR *jcr, FF_PKT *ff_pkt, char **data) +{ + + struct xacl_pkt xacl; + Plugin *plugin = (Plugin *)jcr->plugin; + bRC rc; + + Dmsg0(dbglvl, "plugin_backup_xattr\n"); + + /* check of input variables */ + if (!plugin || !jcr->plugin_ctx || !data) { + return 0; + } + + /* prepare the xacl packet */ + memset(&xacl, 0, sizeof(xacl)); + xacl.pkt_size = sizeof(xacl); + xacl.pkt_end = sizeof(xacl); + xacl.func = BXATTR_BACKUP; + + rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl); + + /* check out status */ + if (rc != bRC_OK){ + Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n"); + return -1; + } + if (xacl.count > 0){ + /* we have something to save, so prepare return data */ + *data = xacl.content; + return xacl.count; + } + + return 0; +} + +/* + * Called here when Bacula got XATTR stream to restore but not every stream but + * a specific one: STREAM_XACL_PLUGIN_XATTR which means a plugin has to + * be called. + * + * in: + * jcr - Job Control Record + * data - content to restore + * length - the length of the content to restore + * out: + * true - when successful + * false - on any Error + */ +bool plugin_restore_xattr(JCR *jcr, char *data, uint32_t length) +{ + struct xacl_pkt xacl; + Plugin *plugin = (Plugin *)jcr->plugin; + bRC rc; + + Dmsg0(dbglvl, "plugin_restore_xattr\n"); + + /* check of input variables */ + if (!plugin || !jcr->plugin_ctx || !data || length == 0) { + return true; + } + + /* prepare the xacl packet */ + memset(&xacl, 0, sizeof(xacl)); + xacl.pkt_size = sizeof(xacl); + xacl.pkt_end = sizeof(xacl); + xacl.func = BXATTR_RESTORE; + xacl.content = data; + xacl.count = length; + + rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl); + + /* check out status */ + if (rc != bRC_OK){ + Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n"); + return false; + } + + return true; +} + /* * Print to file the plugin info. */ @@ -1521,6 +1721,10 @@ static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value) case bVarPrefixLinks: *(int *)value = (int)jcr->prefix_links; break; + case bVarReplace: + *((int*)value) = jcr->replace; + Dmsg1(dbglvl, "Bacula: return replace=%c\n", jcr->replace); + break; case bVarFDName: /* get warning with g++ if we missed one */ case bVarWorkingDir: case bVarExePath: diff --git a/bacula/src/filed/fd_plugins.h b/bacula/src/filed/fd_plugins.h index 5a2857ff79..19e69cb9bb 100644 --- a/bacula/src/filed/fd_plugins.h +++ b/bacula/src/filed/fd_plugins.h @@ -151,6 +151,21 @@ struct io_pkt { int32_t pkt_end; /* end packet sentinel */ }; +enum { + BACL_BACKUP = 1, + BACL_RESTORE = 2, + BXATTR_BACKUP = 3, + BXATTR_RESTORE = 4 +}; + +struct xacl_pkt { + int32_t pkt_size; /* Size of this packet */ + int32_t func; /* Function code */ + int32_t count; /* read/write count */ + char *content; /* read/write buffer */ + int32_t pkt_end; /* end packet sentinel */ +}; + /**************************************************************************** * * * Bacula definitions * @@ -159,28 +174,29 @@ struct io_pkt { /* Bacula Variable Ids */ typedef enum { - bVarJobId = 1, - bVarFDName = 2, - bVarLevel = 3, - bVarType = 4, - bVarClient = 5, - bVarJobName = 6, - bVarJobStatus = 7, - bVarSinceTime = 8, - bVarAccurate = 9, - bVarFileSeen = 10, - bVarVssObject = 11, - bVarVssDllHandle = 12, - bVarWorkingDir = 13, - bVarWhere = 14, - bVarRegexWhere = 15, - bVarExePath = 16, - bVarVersion = 17, - bVarDistName = 18, - bVarxxx = 19, - bVarPrevJobName = 20, - bVarPrefixLinks = 21, - bVarInteractiveSession = 22 + bVarJobId = 1, + bVarFDName = 2, + bVarLevel = 3, + bVarType = 4, + bVarClient = 5, + bVarJobName = 6, + bVarJobStatus = 7, + bVarSinceTime = 8, + bVarAccurate = 9, + bVarFileSeen = 10, + bVarVssObject = 11, + bVarVssDllHandle = 12, + bVarWorkingDir = 13, + bVarWhere = 14, + bVarRegexWhere = 15, + bVarExePath = 16, + bVarVersion = 17, + bVarDistName = 18, + bVarxxx = 19, + bVarPrevJobName = 20, + bVarPrefixLinks = 21, + bVarInteractiveSession = 22, + bVarReplace = 23, } bVariable; /* Events that are passed to plugin */ @@ -242,6 +258,10 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level); int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level); bool plugin_check_file(JCR *jcr, char *fname); bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp); +int plugin_backup_acl(JCR *jcr, FF_PKT *ff_pkt, char **data); +bool plugin_restore_acl(JCR *jcr, char *data, uint32_t length); +int plugin_backup_xattr(JCR *jcr, FF_PKT *ff_pkt, char **data); +bool plugin_restore_xattr(JCR *jcr, char *data, uint32_t length); #endif #ifdef __cplusplus @@ -293,7 +313,7 @@ typedef enum { # define FD_PLUGIN_MAGIC "*FDPluginData*" -#define FD_PLUGIN_INTERFACE_VERSION ( 13 ) +#define FD_PLUGIN_INTERFACE_VERSION ( 14 ) typedef struct s_pluginInfo { uint32_t size; @@ -326,6 +346,7 @@ typedef struct s_pluginFuncs { bRC (*createFile)(bpContext *ctx, struct restore_pkt *rp); bRC (*setFileAttributes)(bpContext *ctx, struct restore_pkt *rp); bRC (*checkFile)(bpContext *ctx, char *fname); + bRC (*handleXACLdata)(bpContext *ctx, struct xacl_pkt *xacl); } pFuncs; #define plug_func(plugin) ((pFuncs *)(plugin->pfuncs)) diff --git a/bacula/src/filed/filed.h b/bacula/src/filed/filed.h index 0ef70ac6bc..eae8329375 100644 --- a/bacula/src/filed/filed.h +++ b/bacula/src/filed/filed.h @@ -40,7 +40,8 @@ #include "fd_plugins.h" #include "fd_snapshot.h" #include "findlib/find.h" -#include "xacl.h" +#include "bacl.h" +#include "bxattr.h" #include "jcr.h" #include "protos.h" /* file daemon prototypes */ #include "lib/runscript.h" diff --git a/bacula/src/filed/restore.c b/bacula/src/filed/restore.c index cedd5fa05b..c4cb40cef5 100644 --- a/bacula/src/filed/restore.c +++ b/bacula/src/filed/restore.c @@ -197,24 +197,26 @@ static inline void push_delayed_restore_stream(r_ctx &rctx, char *msg, int msgle static inline bool do_restore_acl(JCR *jcr, int stream, char *content, uint32_t content_length) { - if (!jcr->xacl) { +#ifdef HAVE_ACL + if (!jcr->bacl) { return true; } - switch (jcr->xacl->restore_acl(jcr, stream, content, content_length)) { - case bRC_XACL_fatal: + switch (jcr->bacl->restore_acl(jcr, stream, content, content_length)) { + case bRC_BACL_fatal: return false; - case bRC_XACL_error: + case bRC_BACL_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) { + if (jcr->bacl->get_acl_nr_errors() < ACL_MAX_ERROR_PRINT_PER_JOB) { Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); } break; default: break; } +#endif return true; } @@ -225,24 +227,27 @@ static inline bool do_restore_acl(JCR *jcr, int stream, char *content, static inline bool do_restore_xattr(JCR *jcr, int stream, char *content, uint32_t content_length) { - if (!jcr->xacl) { +#ifdef HAVE_XATTR + if (!jcr->bxattr) { return true; } - switch (jcr->xacl->restore_xattr(jcr, stream, content, content_length)) { - case bRC_XACL_fatal: + + switch (jcr->bxattr->restore_xattr(jcr, stream, content, content_length)) { + case bRC_BXATTR_fatal: return false; - case bRC_XACL_error: + case bRC_BXATTR_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) { + if (jcr->bxattr->get_xattr_nr_errors() < XATTR_MAX_ERROR_PRINT_PER_JOB) { Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); } break; default: break; } +#endif return true; } @@ -278,6 +283,7 @@ static inline bool pop_delayed_data_streams(r_ctx &rctx) * - *_XATTR_* */ foreach_alist(rds, rctx.delayed_streams) { + Dmsg1(0, "Delayed Stream=%d\n", rds->stream); switch (rds->stream) { case STREAM_UNIX_ACCESS_ACL: case STREAM_UNIX_DEFAULT_ACL: @@ -301,10 +307,12 @@ static inline bool pop_delayed_data_streams(r_ctx &rctx) case STREAM_XACL_FREEBSD_NFS4: case STREAM_XACL_HURD_DEFAULT: case STREAM_XACL_HURD_ACCESS: + case STREAM_XACL_PLUGIN_ACL: if (!do_restore_acl(jcr, rds->stream, rds->content, rds->content_length)) { goto get_out; } break; + case STREAM_XACL_PLUGIN_XATTR: case STREAM_XACL_HURD_XATTR: case STREAM_XACL_IRIX_XATTR: case STREAM_XACL_TRU64_XATTR: @@ -322,6 +330,7 @@ static inline bool pop_delayed_data_streams(r_ctx &rctx) default: Jmsg(jcr, M_WARNING, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"), rds->stream); + Dmsg2(0, "Unknown stream=%d data=%s\n", rds->stream, rds->content); break; } if (rds->content) { @@ -446,7 +455,12 @@ void do_restore(JCR *jcr) binit(&rctx.bfd); binit(&rctx.forkbfd); attr = rctx.attr = new_attr(jcr); - jcr->xacl = (XACL*)new_xacl(); +#ifdef HAVE_ACL + jcr->bacl = (BACL*)new_bacl(); +#endif +#ifdef HAVE_XATTR + jcr->bxattr = (BXATTR*)new_bxattr(); +#endif Dsm_check(200); while ((bget_ret = fdmsg->bget_msg(&bmsg)) >= 0 && !job_canceled(jcr)) { @@ -858,6 +872,7 @@ void do_restore(JCR *jcr) case STREAM_XACL_FREEBSD_NFS4: case STREAM_XACL_HURD_DEFAULT: case STREAM_XACL_HURD_ACCESS: + case STREAM_XACL_PLUGIN_ACL: /* * Do not restore ACLs when * a) The current file is not extracted @@ -886,6 +901,7 @@ void do_restore(JCR *jcr) } break; + case STREAM_XACL_PLUGIN_XATTR: case STREAM_XACL_HURD_XATTR: case STREAM_XACL_IRIX_XATTR: case STREAM_XACL_TRU64_XATTR: @@ -1033,14 +1049,17 @@ ok_out: Dmsg2(10, "End Do Restore. Files=%d Bytes=%s\n", jcr->JobFiles, edit_uint64(jcr->JobBytes, ec1)); - if (jcr->xacl) { - 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 (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()); - } +#ifdef HAVE_ACL + if (jcr->bacl && jcr->bacl->get_acl_nr_errors() > 0) { + Jmsg(jcr, M_WARNING, 0, _("Encountered %ld acl errors while doing restore\n"), jcr->bacl->get_acl_nr_errors()); } +#endif +#ifdef HAVE_XATTR + if (jcr->bxattr && jcr->bxattr->get_xattr_nr_errors() > 0) { + Jmsg(jcr, M_WARNING, 0, _("Encountered %ld xattr errors while doing restore\n"), jcr->bxattr->get_xattr_nr_errors()); + } +#endif + 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); @@ -1096,10 +1115,18 @@ ok_out: jcr->compress_buf_size = 0; } - if (jcr->xacl) { - delete(jcr->xacl); - jcr->xacl = NULL; +#ifdef HAVE_ACL + if (jcr->bacl) { + delete(jcr->bacl); + jcr->bacl = NULL; } +#endif +#ifdef HAVE_XATTR + if (jcr->bxattr) { + delete(jcr->bxattr); + jcr->bxattr = NULL; + } +#endif /* Free the delayed stream stack list. */ if (rctx.delayed_streams) { diff --git a/bacula/src/filed/xacl.c b/bacula/src/filed/xacl.c deleted file mode 100644 index 8eea8a2937..0000000000 --- a/bacula/src/filed/xacl.c +++ /dev/null @@ -1,1335 +0,0 @@ -/* - Bacula(R) - The Network Backup Solution - - Copyright (C) 2000-2017 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(); -#else - return NULL; -#endif -}; diff --git a/bacula/src/filed/xacl.h b/bacula/src/filed/xacl.h deleted file mode 100644 index 19df6539d6..0000000000 --- a/bacula/src/filed/xacl.h +++ /dev/null @@ -1,308 +0,0 @@ -/* - 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 deleted file mode 100644 index 59d1554585..0000000000 --- a/bacula/src/filed/xacl_freebsd.c +++ /dev/null @@ -1,946 +0,0 @@ -/* - 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 deleted file mode 100644 index b3e1d653be..0000000000 --- a/bacula/src/filed/xacl_freebsd.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - 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 deleted file mode 100644 index 409d6b6cc7..0000000000 --- a/bacula/src/filed/xacl_linux.c +++ /dev/null @@ -1,583 +0,0 @@ -/* - 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 deleted file mode 100644 index 4f835ca873..0000000000 --- a/bacula/src/filed/xacl_linux.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - 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 deleted file mode 100644 index ef2fc9a1f5..0000000000 --- a/bacula/src/filed/xacl_osx.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - 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 deleted file mode 100644 index c8d4b2a875..0000000000 --- a/bacula/src/filed/xacl_osx.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - 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 deleted file mode 100644 index c1701d32a0..0000000000 --- a/bacula/src/filed/xacl_solaris.c +++ /dev/null @@ -1,1177 +0,0 @@ -/* - 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 deleted file mode 100644 index f8a8d5b773..0000000000 --- a/bacula/src/filed/xacl_solaris.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - 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.h b/bacula/src/filed/xattr.h deleted file mode 100644 index 1c406189f5..0000000000 --- a/bacula/src/filed/xattr.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - Bacula(R) - The Network Backup Solution - - Copyright (C) 2000-2017 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. -*/ - -#ifndef __BXATTR_H_ -#define __BXATTR_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 /* __BXATTR_H_ */ diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index dae83c06e8..231de35950 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -153,7 +153,8 @@ struct bpContext; #ifdef FILE_DAEMON class VSSClient; class htable; -class XACL; +class BACL; +class BXATTR; class snapshot_manager; struct CRYPTO_CTX { @@ -424,7 +425,8 @@ 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 */ - XACL *xacl; /* ACLs and Extended Attributes for backup/restore */ + BACL *bacl; /* ACLs for backup/restore */ + BXATTR *bxattr; /* 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/lib/plugins.c b/bacula/src/lib/plugins.c index 9234c25195..00559e86e9 100644 --- a/bacula/src/lib/plugins.c +++ b/bacula/src/lib/plugins.c @@ -226,6 +226,13 @@ void unload_plugins() foreach_alist(plugin, b_plugin_list) { /* Shut it down and unload it */ plugin->unloadPlugin(); + /* TODO: + * If you get a SIGSEGV when using plugins then it is very plausible that + * one of the plugins has a memory leakage which cannot be tracked by + * SMARTALLOC mechanism when you need to disable dlclose() below for + * further investigation. + * The problem was registered as #0002330 (http://bugs.bacula.org/view.php?id=2330) + */ dlclose(plugin->pHandle); if (plugin->file) { free(plugin->file); diff --git a/bacula/src/plugins/fd/bpipe-fd.c b/bacula/src/plugins/fd/bpipe-fd.c index 76f187580c..b01ee9c47c 100644 --- a/bacula/src/plugins/fd/bpipe-fd.c +++ b/bacula/src/plugins/fd/bpipe-fd.c @@ -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. @@ -58,6 +58,7 @@ static bRC endRestoreFile(bpContext *ctx); static bRC createFile(bpContext *ctx, struct restore_pkt *rp); static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp); static bRC checkFile(bpContext *ctx, char *fname); +static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl); static char *apply_rp_codes(struct plugin_ctx * p_ctx); @@ -95,7 +96,8 @@ static pFuncs pluginFuncs = { pluginIO, createFile, setFileAttributes, - checkFile + checkFile, + handleXACLdata }; /* @@ -133,16 +135,16 @@ bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs) } /* - * External entry point to unload the plugin + * External entry point to unload the plugin */ -bRC unloadPlugin() +bRC unloadPlugin() { // printf("bpipe-fd: Unloaded\n"); return bRC_OK; } /* - * The following entry points are accessed through the function + * The following entry points are accessed through the function * pointers we supplied to Bacula. Each plugin type (dir, fd, sd) * has its own set of entry points that the plugin must define. */ @@ -180,7 +182,7 @@ static bRC freePlugin(bpContext *ctx) /* * Return some plugin value (none defined) */ -static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) +static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) { return bRC_OK; } @@ -188,7 +190,7 @@ static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) /* * Set a plugin value (none defined) */ -static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) +static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) { return bRC_OK; } @@ -212,7 +214,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) */ switch (event->eventType) { case bEventPluginCommand: - bfuncs->DebugMessage(ctx, fi, li, dbglvl, + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: PluginCommand=%s\n", (char *)value); break; case bEventJobStart: @@ -273,7 +275,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) } *p++ = 0; /* terminate reader string */ p_ctx->writer = p; -// printf("bpipe-fd: plugin=%s fname=%s reader=%s writer=%s\n", +// printf("bpipe-fd: plugin=%s fname=%s reader=%s writer=%s\n", // p_ctx->cmd, p_ctx->fname, p_ctx->reader, p_ctx->writer); break; @@ -284,7 +286,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) return bRC_OK; } -/* +/* * Start the backup of a specific file */ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) @@ -330,7 +332,7 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) if (!p_ctx) { return bRC_Error; } - + io->status = -1; io->io_errno = 0; switch(io->func) { @@ -340,11 +342,11 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) char *writer_codes = apply_rp_codes(p_ctx); p_ctx->pfd = open_bpipe(writer_codes, 0, "ws"); - bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%p writer=%s\n", + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%p writer=%s\n", p_ctx->pfd, writer_codes); if (!p_ctx->pfd) { io->io_errno = errno; - bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Open pipe writer=%s failed: ERR=%s\n", writer_codes, strerror(errno)); if (writer_codes) { free(writer_codes); @@ -357,11 +359,11 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) io->status = fileno(p_ctx->pfd->wfd); } else { p_ctx->pfd = open_bpipe(p_ctx->reader, 0, "rs"); - bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%p reader=%s\n", + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%p reader=%s\n", p_ctx->pfd, p_ctx->reader); if (!p_ctx->pfd) { io->io_errno = errno; - bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Open pipe reader=%s failed: ERR=%s\n", p_ctx->reader, strerror(errno)); return bRC_Error; } @@ -378,9 +380,9 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) io->status = fread(io->buf, 1, io->count, p_ctx->pfd->rfd); // bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_READ buf=%p len=%d\n", io->buf, io->status); if (!feof(p_ctx->pfd->rfd) && io->status == 0 && ferror(p_ctx->pfd->rfd)) { - bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Pipe read error: ERR=%s\n", strerror(errno)); - bfuncs->DebugMessage(ctx, fi, li, dbglvl, + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "Pipe read error: ERR=%s\n", strerror(errno)); return bRC_Error; } @@ -395,9 +397,9 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) io->status = fwrite(io->buf, 1, io->count, p_ctx->pfd->wfd); // printf("bpipe-fd: IO_WRITE buf=%p len=%d\n", io->buf, io->status); if (!feof(p_ctx->pfd->wfd) && io->status == 0 && ferror(p_ctx->pfd->wfd)) { - bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Pipe write error\n"); - bfuncs->DebugMessage(ctx, fi, li, dbglvl, + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "Pipe write error: ERR=%s\n", strerror(errno)); return bRC_Error; } @@ -410,7 +412,7 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) } io->status = close_bpipe(p_ctx->pfd); if (io->status != 0) { - bfuncs->JobMessage(ctx, fi, li, M_ERROR, 0, "bpipe-fd: Error closing for file %s: %d\n", + bfuncs->JobMessage(ctx, fi, li, M_ERROR, 0, "bpipe-fd: Error closing for file %s: %d\n", p_ctx->fname, io->status); } break; @@ -446,7 +448,7 @@ static bRC endRestoreFile(bpContext *ctx) /* * This is called during restore to create the file (if necessary) * We must return in rp->create_status: - * + * * CF_ERROR -- error * CF_SKIP -- skip processing this file * CF_EXTRACT -- extract the file (i.e.call i/o routines) @@ -481,6 +483,14 @@ static bRC checkFile(bpContext *ctx, char *fname) return bRC_OK; } +/* + * New Bacula Plugin API require this + */ +static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl) +{ + return bRC_OK; +} + /************************************************************************* * Apply codes in writer command: * %w -> "where" @@ -524,9 +534,9 @@ static char *apply_rp_codes(struct plugin_ctx * p_ctx) } } - /* Required mem: - * len(imsg) - * + number of "where" codes * (len(where)-2) + /* Required mem: + * len(imsg) + * + number of "where" codes * (len(where)-2) * - number of "replace" codes */ omsg = (char*)malloc(strlen(imsg) + (w_count * (strlen(p_ctx->where)-2)) - r_count + 1); diff --git a/bacula/src/plugins/fd/example-plugin-fd.c b/bacula/src/plugins/fd/example-plugin-fd.c index b3f2dfdcb1..c087704cf7 100644 --- a/bacula/src/plugins/fd/example-plugin-fd.c +++ b/bacula/src/plugins/fd/example-plugin-fd.c @@ -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. @@ -46,7 +46,7 @@ static bRC endRestoreFile(bpContext *ctx); static bRC createFile(bpContext *ctx, struct restore_pkt *rp); static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp); static bRC checkFile(bpContext *ctx, char *fname); - +static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl); /* Pointers to Bacula functions */ static bFuncs *bfuncs = NULL; @@ -80,7 +80,8 @@ static pFuncs pluginFuncs = { pluginIO, createFile, setFileAttributes, - checkFile + checkFile, + handleXACLdata }; /* @@ -104,7 +105,7 @@ loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs) * Bacula is going to exit. */ bRC DLL_IMP_EXP -unloadPlugin() +unloadPlugin() { printf("plugin: Unloaded\n"); return bRC_OK; @@ -127,7 +128,7 @@ static bRC newPlugin(bpContext *ctx) } /* - * Release everything concerning a particular instance of a + * Release everything concerning a particular instance of a * plugin. Normally called when the Job terminates. */ static bRC freePlugin(bpContext *ctx) @@ -142,17 +143,17 @@ static bRC freePlugin(bpContext *ctx) * Called by core code to get a variable from the plugin. * Not currently used. */ -static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) +static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) { // printf("plugin: getPluginValue var=%d\n", var); return bRC_OK; } -/* +/* * Called by core code to set a plugin variable. * Not currently used. */ -static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) +static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) { // printf("plugin: setPluginValue var=%d\n", var); return bRC_OK; @@ -230,13 +231,13 @@ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) * Done backing up a file. */ static bRC endBackupFile(bpContext *ctx) -{ +{ return bRC_OK; } /* * Do actual I/O. Bacula calls this after startBackupFile - * or after startRestoreFile to do the actual file + * or after startRestoreFile to do the actual file * input or output. */ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) @@ -297,6 +298,13 @@ static bRC checkFile(bpContext *ctx, char *fname) return bRC_OK; } +/* + * New Bacula Plugin API require this + */ +static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl) +{ + return bRC_OK; +} #ifdef __cplusplus } diff --git a/bacula/src/plugins/fd/test-deltaseq-fd.c b/bacula/src/plugins/fd/test-deltaseq-fd.c index bc0734a4f2..d7a305e637 100644 --- a/bacula/src/plugins/fd/test-deltaseq-fd.c +++ b/bacula/src/plugins/fd/test-deltaseq-fd.c @@ -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. @@ -55,6 +55,7 @@ static bRC endRestoreFile(bpContext *ctx); static bRC createFile(bpContext *ctx, struct restore_pkt *rp); static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp); static bRC checkFile(bpContext *ctx, char *fname); +static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl); /* Pointers to Bacula functions */ static bFuncs *bfuncs = NULL; @@ -90,7 +91,8 @@ static pFuncs pluginFuncs = { pluginIO, createFile, setFileAttributes, - checkFile + checkFile, + handleXACLdata }; #define get_self(x) ((delta_test*)((x)->pContext)) @@ -109,7 +111,7 @@ public: bool done; int level; - delta_test(bpContext *bpc) { + delta_test(bpContext *bpc) { fd = NULL; ctx = bpc; done = false; @@ -146,16 +148,16 @@ bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs) } /* - * External entry point to unload the plugin + * External entry point to unload the plugin */ -bRC unloadPlugin() +bRC unloadPlugin() { // Dmsg(NULL, dbglvl, "delta-test-fd: Unloaded\n"); return bRC_OK; } /* - * The following entry points are accessed through the function + * The following entry points are accessed through the function * pointers we supplied to Bacula. Each plugin type (dir, fd, sd) * has its own set of entry points that the plugin must define. */ @@ -188,7 +190,7 @@ static bRC freePlugin(bpContext *ctx) /* * Return some plugin value (none defined) */ -static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) +static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) { return bRC_OK; } @@ -196,7 +198,7 @@ static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) /* * Set a plugin value (none defined) */ -static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) +static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) { return bRC_OK; } @@ -222,7 +224,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) */ switch (event->eventType) { case bEventPluginCommand: -// Dmsg(ctx, dbglvl, +// Dmsg(ctx, dbglvl, // "delta-test-fd: PluginCommand=%s\n", (char *)value); break; case bEventJobStart: @@ -263,7 +265,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) if (self->level == 'I' || self->level == 'D') { bfuncs->getBaculaValue(ctx, bVarAccurate, (void *)&accurate); if (!accurate) { /* can be changed to FATAL */ - Jmsg(ctx, M_FATAL, + Jmsg(ctx, M_FATAL, "Accurate mode should be turned on when using the " "delta-test plugin\n"); return bRC_Error; @@ -286,7 +288,7 @@ static const char *files[] = { }; static int nb_files = 4; -/* +/* * Start the backup of a specific file */ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) @@ -313,7 +315,7 @@ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) self->delta = sp->delta_seq + 1; } pm_strcpy(self->fname, files[self->delta % nb_files]); - Dmsg(ctx, dbglvl, "delta-test-fd: delta_seq=%i delta=%i fname=%s\n", + Dmsg(ctx, dbglvl, "delta-test-fd: delta_seq=%i delta=%i fname=%s\n", sp->delta_seq, self->delta, self->fname); // Dmsg(ctx, dbglvl, "delta-test-fd: startBackupFile\n"); return bRC_OK; @@ -342,7 +344,7 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) if (!self) { return bRC_Error; } - + io->status = 0; io->io_errno = 0; switch(io->func) { @@ -357,7 +359,7 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) } if (!self->fd) { io->io_errno = errno; - Jmsg(ctx, M_FATAL, + Jmsg(ctx, M_FATAL, "Open failed: ERR=%s\n", strerror(errno)); return bRC_Error; } @@ -366,7 +368,7 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) self->fd = fopen(self->fname, "r"); if (!self->fd) { io->io_errno = errno; - Jmsg(ctx, M_FATAL, + Jmsg(ctx, M_FATAL, "Open failed: ERR=%s\n", strerror(errno)); return bRC_Error; } @@ -383,7 +385,7 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) } else { /* first time, read 300, then replace 50-250 by other data */ if (self->delta == 0) { - io->status = fread(io->buf, 1, 400, self->fd); + io->status = fread(io->buf, 1, 400, self->fd); } else { io->offset = self->delta * 100 / 2; /* chunks are melted */ io->status = fread(io->buf, 1, 100, self->fd); @@ -392,9 +394,9 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) self->done = true; } if (io->status == 0 && ferror(self->fd)) { - Jmsg(ctx, M_FATAL, + Jmsg(ctx, M_FATAL, "Pipe read error: ERR=%s\n", strerror(errno)); - Dmsg(ctx, dbglvl, + Dmsg(ctx, dbglvl, "Pipe read error: ERR=%s\n", strerror(errno)); return bRC_Error; } @@ -409,9 +411,9 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) Dmsg(ctx, dbglvl, "delta-test-fd: WRITE count=%lld\n", (int64_t)io->count); io->status = fwrite(io->buf, 1, io->count, self->fd); if (io->status == 0 && ferror(self->fd)) { - Jmsg(ctx, M_FATAL, + Jmsg(ctx, M_FATAL, "Pipe write error\n"); - Dmsg(ctx, dbglvl, + Dmsg(ctx, dbglvl, "Pipe read error: ERR=%s\n", strerror(errno)); return bRC_Error; } @@ -461,7 +463,7 @@ static bRC endRestoreFile(bpContext *ctx) /* * This is called during restore to create the file (if necessary) * We must return in rp->create_status: - * + * * CF_ERROR -- error * CF_SKIP -- skip processing this file * CF_EXTRACT -- extract the file (i.e.call i/o routines) @@ -492,6 +494,14 @@ static bRC checkFile(bpContext *ctx, char *fname) return bRC_OK; } +/* + * New Bacula Plugin API require this + */ +static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl) +{ + return bRC_OK; +} + #ifdef __cplusplus } #endif diff --git a/bacula/src/plugins/fd/test-plugin-fd.c b/bacula/src/plugins/fd/test-plugin-fd.c index 5e45c0b510..27ded5181a 100644 --- a/bacula/src/plugins/fd/test-plugin-fd.c +++ b/bacula/src/plugins/fd/test-plugin-fd.c @@ -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. @@ -61,6 +61,7 @@ static bRC endRestoreFile(bpContext *ctx); static bRC createFile(bpContext *ctx, struct restore_pkt *rp); static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp); static bRC checkFile(bpContext *ctx, char *fname); +static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl); /* Pointers to Bacula functions */ static bFuncs *bfuncs = NULL; @@ -96,7 +97,8 @@ static pFuncs pluginFuncs = { pluginIO, createFile, setFileAttributes, - checkFile + checkFile, + handleXACLdata }; static struct ini_items test_items[] = { @@ -105,7 +107,7 @@ static struct ini_items test_items[] = { { "string2", ini_store_str, "2nd String", 0}, { "ok", ini_store_bool, "boolean", 0}, -// We can also use the ITEMS_DEFAULT +// We can also use the ITEMS_DEFAULT // { "ok", ini_store_bool, "boolean", 0, ITEMS_DEFAULT}, { NULL, NULL, NULL, 0} }; @@ -147,16 +149,16 @@ bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs) } /* - * External entry point to unload the plugin + * External entry point to unload the plugin */ -bRC unloadPlugin() +bRC unloadPlugin() { // printf("test-plugin-fd: Unloaded\n"); return bRC_OK; } /* - * The following entry points are accessed through the function + * The following entry points are accessed through the function * pointers we supplied to Bacula. Each plugin type (dir, fd, sd) * has its own set of entry points that the plugin must define. */ @@ -197,7 +199,7 @@ static bRC freePlugin(bpContext *ctx) /* * Return some plugin value (none defined) */ -static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) +static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) { return bRC_OK; } @@ -205,7 +207,7 @@ static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) /* * Set a plugin value (none defined) */ -static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) +static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) { return bRC_OK; } @@ -257,7 +259,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) break; } rop = (restore_object_pkt *)value; - bfuncs->DebugMessage(ctx, fi, li, dbglvl, + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "Get RestoreObject len=%d JobId=%d oname=%s type=%d data=%.127s\n", rop->object_len, rop->JobId, rop->object_name, rop->object_type, rop->object); @@ -283,7 +285,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) } ini.register_items(test_items, sizeof(struct ini_items)); if (ini.parse(ini.out_fname)) { - bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "string1 = %s\n", + bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "string1 = %s\n", ini.items[0].val.strval); } else { bfuncs->JobMessage(ctx, fi, li, M_ERROR, 0, "Can't parse config\n"); @@ -323,7 +325,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) } *p++ = 0; /* terminate reader string */ p_ctx->writer = p; - printf("test-plugin-fd: plugin=%s fname=%s reader=%s writer=%s\n", + printf("test-plugin-fd: plugin=%s fname=%s reader=%s writer=%s\n", p_ctx->cmd, p_ctx->fname, p_ctx->reader, p_ctx->writer); break; } @@ -342,7 +344,7 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) return bRC_OK; } -/* +/* * Start the backup of a specific file */ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) @@ -354,15 +356,15 @@ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) if (p_ctx->nb_obj == 0) { sp->fname = (char *)"takeme.h"; - bfuncs->DebugMessage(ctx, fi, li, dbglvl, "AcceptFile=%s = %d\n", + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "AcceptFile=%s = %d\n", sp->fname, bfuncs->AcceptFile(ctx, sp)); sp->fname = (char *)"/path/to/excludeme.o"; - bfuncs->DebugMessage(ctx, fi, li, dbglvl, "AcceptFile=%s = %d\n", + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "AcceptFile=%s = %d\n", sp->fname, bfuncs->AcceptFile(ctx, sp)); sp->fname = (char *)"/path/to/excludeme.c"; - bfuncs->DebugMessage(ctx, fi, li, dbglvl, "AcceptFile=%s = %d\n", + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "AcceptFile=%s = %d\n", sp->fname, bfuncs->AcceptFile(ctx, sp)); } @@ -577,7 +579,7 @@ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) fclose(fp); } free_pool_memory(q); - + } else if (p_ctx->nb_obj == 1) { ConfigFile ini; p_ctx->buf = get_pool_memory(PM_BSOCK); @@ -589,7 +591,7 @@ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) sp->type = FT_PLUGIN_CONFIG; Dmsg1(0, "RestoreOptions=<%s>\n", p_ctx->buf); - } + } time_t now = time(NULL); sp->index = ++p_ctx->nb_obj; @@ -601,7 +603,7 @@ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) sp->statp.st_blksize = 4096; sp->statp.st_blocks = 1; bfuncs->DebugMessage(ctx, fi, li, dbglvl, - "Creating RestoreObject len=%d oname=%s data=%.127s\n", + "Creating RestoreObject len=%d oname=%s data=%.127s\n", sp->object_len, sp->object_name, sp->object); printf("test-plugin-fd: startBackupFile\n"); @@ -630,7 +632,7 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) if (!p_ctx) { return bRC_Error; } - + io->status = 0; io->io_errno = 0; return bRC_OK; @@ -659,7 +661,7 @@ static bRC endRestoreFile(bpContext *ctx) /* * This is called during restore to create the file (if necessary) * We must return in rp->create_status: - * + * * CF_ERROR -- error * CF_SKIP -- skip processing this file * CF_EXTRACT -- extract the file (i.e.call i/o routines) @@ -694,6 +696,14 @@ static bRC checkFile(bpContext *ctx, char *fname) return bRC_OK; } +/* + * New Bacula Plugin API require this + */ +static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl) +{ + return bRC_OK; +} + #ifdef __cplusplus } #endif diff --git a/bacula/src/qt-console/bat.pro.mingw64 b/bacula/src/qt-console/bat.pro.mingw64 index 4812fecaac..1765985f8a 100644 --- a/bacula/src/qt-console/bat.pro.mingw64 +++ b/bacula/src/qt-console/bat.pro.mingw64 @@ -24,7 +24,7 @@ cross-win32 { LIBS += -mwindows -L../win32/release64 -lbacula } !cross-win32 { - LIBS += -L../lib -lbac -L../findlib -lbacfind -lssl -lcrypto + LIBS += -L../lib -lbac -L../findlib -lbacfind -L/opt/local/lib -lssl -lcrypto } qwt { diff --git a/bacula/src/streams.h b/bacula/src/streams.h index 16a3159945..6c1dc972f3 100644 --- a/bacula/src/streams.h +++ b/bacula/src/streams.h @@ -136,14 +136,15 @@ #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_PLUGIN_ACL 1020 /* Plugin ACL data for plugin specific acls */ +#define STREAM_XACL_PLUGIN_XATTR 1988 /* Plugin XATTR data for plugin specific xattrs */ #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. - */ + * 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 */