]> git.sur5r.net Git - bacula/bacula/commitdiff
XACL - refactoring an ACL and XATTR codes.
authorRadoslaw Korzeniewski <radekk@inteos.pl>
Sat, 19 Nov 2016 15:56:53 +0000 (16:56 +0100)
committerKern Sibbald <kern@sibbald.com>
Sat, 19 Nov 2016 15:56:53 +0000 (16:56 +0100)
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

25 files changed:
bacula/.gitignore
bacula/AUTHORS
bacula/src/filed/Makefile.in
bacula/src/filed/acl.c [deleted file]
bacula/src/filed/acl.h [deleted file]
bacula/src/filed/backup.c
bacula/src/filed/filed.h
bacula/src/filed/protos.h
bacula/src/filed/restore.c
bacula/src/filed/xacl.c [new file with mode: 0644]
bacula/src/filed/xacl.h [new file with mode: 0644]
bacula/src/filed/xacl_freebsd.c [new file with mode: 0644]
bacula/src/filed/xacl_freebsd.h [new file with mode: 0644]
bacula/src/filed/xacl_linux.c [new file with mode: 0644]
bacula/src/filed/xacl_linux.h [new file with mode: 0644]
bacula/src/filed/xacl_osx.c [new file with mode: 0644]
bacula/src/filed/xacl_osx.h [new file with mode: 0644]
bacula/src/filed/xacl_solaris.c [new file with mode: 0644]
bacula/src/filed/xacl_solaris.h [new file with mode: 0644]
bacula/src/filed/xattr.c [deleted file]
bacula/src/filed/xattr.h [deleted file]
bacula/src/findlib/bfile.c
bacula/src/jcr.h
bacula/src/stored/bscan.c
bacula/src/streams.h

index 2e7992fad0e739dd78eab5feedb1686a8077ed29..637dbef7cd2cbb83556f3b847cf7fa8aec4eb1b6 100644 (file)
@@ -450,6 +450,7 @@ src/qt-console/Makefile.mingw32
 src/qt-console/Makefile.mingw32.Debug
 src/qt-console/Makefile.mingw32.Release
 src/qt-console/bat.pro.mingw32
+src/qt-console/bat.pro.mingw64
 
 # src/qt-console/clients/
 src/qt-console/clients/.*.swp
index 462cc81a32a2cb46fc1d1644fde8113a2eb1a925..23c66fb5d1c3171eeb0b980e340fffddfda70b65 100644 (file)
@@ -104,6 +104,7 @@ Peter Much
 Philippe Chauvat
 Phil Stracchino
 Preben Guldberg
+Radoslaw Korzeniewski
 Riccardo Ghetta
 Richard Mortimer
 Robert Nelson
index 7fca1a8e492f4177994c765b89beff47c14da433..fdbffb438556e4734c03d7f238af2a8cf4685d0d 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Bacula Makefile for the File daemon
 #
-# Copyright (C) 2000-2015 Kern Sibbald
+# Copyright (C) 2000-2016 Kern Sibbald
 # License: BSD 2-Clause; see file LICENSE-FOSS
 #
 @MCOMMON@
@@ -30,11 +30,12 @@ first_rule: all
 dummy:
 
 #
-SVRSRCS = filed.c authenticate.c acl.c backup.c crypto.c \
+SVRSRCS = filed.c authenticate.c backup.c crypto.c \
          estimate.c \
          fd_plugins.c accurate.c \
          filed_conf.c heartbeat.c hello.c job.c fd_snapshot.c \
-         restore.c status.c verify.c verify_vol.c xattr.c
+         restore.c status.c verify.c verify_vol.c \
+         xacl.c xacl_linux.c xacl_osx.c xacl_solaris.c xacl_freebsd.c
 SVROBJS = $(SVRSRCS:.c=.o)
 
 # these are the objects that are changed by the .configure process
@@ -59,7 +60,7 @@ all: Makefile bacula-fd @STATIC_FD@
        @echo "==== Make of filed is good ===="
        @echo " "
 
-acl.o: acl.c
+xacl.o: xacl.c
        @echo "Compiling $<"
        $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(LZO_INC) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) $(AFS_CFLAGS) $<
 
diff --git a/bacula/src/filed/acl.c b/bacula/src/filed/acl.c
deleted file mode 100644 (file)
index 5285a4c..0000000
+++ /dev/null
@@ -1,2209 +0,0 @@
-/*
-   Bacula(R) - The Network Backup Solution
-
-   Copyright (C) 2000-2015 Kern Sibbald
-   Copyright (C) 2004-2015 Free Software Foundation Europe e.V.
-
-   The original author of Bacula is Kern Sibbald, with contributions
-   from many others, a complete list can be found in the file AUTHORS.
-
-   You may use this file and others of this release according to the
-   license defined in the LICENSE file, which includes the Affero General
-   Public License, v3.0 ("AGPLv3") and some additional permissions and
-   terms pursuant to its AGPLv3 Section 7.
-
-   This notice must be preserved when any source code is 
-   conveyed and/or propagated.
-
-   Bacula(R) is a registered trademark of Kern Sibbald.
-*/
-/*
- * Functions to handle ACLs for bacula.
- *
- * Currently we support the following OSes:
- *   - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface)
- *   - Darwin
- *   - FreeBSD (POSIX and NFSv4/ZFS acls)
- *   - GNU Hurd
- *   - HPUX
- *   - IRIX
- *   - Linux
- *   - Solaris (POSIX and NFSv4/ZFS acls)
- *   - Tru64
- *
- * Next to OS specific acls we support AFS acls using the pioctl interface.
- *
- * We handle two different types of ACLs: access and default ACLS.
- * On most systems that support default ACLs they only apply to directories.
- *
- * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
- * independently, while others (eg. Solaris) provide both in one call.
- *
- * The Filed saves ACLs in their native format and uses different streams
- * for all different platforms. Currently we only allow ACLs to be restored
- * which were saved in the native format of the platform they are extracted
- * on. Later on we might add conversion functions for mapping from one
- * platform to an other or allow restores of systems that use the same
- * native format.
- *
- * Its also interesting to see what the exact format of acl text is on
- * certain platforms and if they use they same encoding we might allow
- * different platform streams to be decoded on an other similar platform.
- *
- * Original written by Preben 'Peppe' Guldberg, December 2004
- *
- */
-
-#include "bacula.h"
-#include "filed.h"
-
-#if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL)
-/*
- * Entry points when compiled without support for ACLs or on an unsupported platform.
- */
-bool backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   Jmsg(jcr, M_FATAL, 0, "ACL backup requested but not configured in Bacula.\n");
-   return false;
-}
-
-bacl_rtn_code restore_acl_streams(JCR *jcr,
-                                 int stream,
-                                 char *content,
-                                 uint32_t content_length)
-{
-   Jmsg(jcr, M_FATAL, 0, "ACL backup requested but not configured in Bacula.\n");
-   return bacl_rtn_fatal;
-}
-#else
-/*
- * Send an ACL stream to the SD.
- */
-static bacl_rtn_code send_acl_stream(JCR *jcr, int stream)
-{
-   BSOCK *sd = jcr->store_bsock;
-   POOLMEM *msgsave;
-#ifdef FD_NO_SEND_TEST
-   return bacl_rtn_ok;
-#endif
-
-   /*
-    * Sanity check
-    */
-   if (jcr->acl_ctx->content_length <= 0) {
-      return bacl_rtn_ok;
-   }
-
-   /*
-    * Send header
-    */
-   if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
-      return bacl_rtn_fatal;
-   }
-
-   /*
-    * Send the buffer to the storage deamon
-    */
-   Dmsg1(400, "Backing up ACL <%s>\n", jcr->acl_ctx->content);
-   msgsave = sd->msg;
-   sd->msg = jcr->acl_ctx->content;
-   sd->msglen = jcr->acl_ctx->content_length + 1;
-   if (!sd->send()) {
-      sd->msg = msgsave;
-      sd->msglen = 0;
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
-      return bacl_rtn_fatal;
-   }
-
-   jcr->JobBytes += sd->msglen;
-   sd->msg = msgsave;
-   if (!sd->signal(BNET_EOD)) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
-      return bacl_rtn_fatal;
-   }
-
-   Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
-   return bacl_rtn_ok;
-}
-
-/*
- * First the native ACLs.
- */
-#if defined(HAVE_ACL)
-#if defined(HAVE_AIX_OS)
-
-#if defined(HAVE_EXTENDED_ACL)
-
-#include <sys/access.h>
-#include <sys/acl.h>
-
-static bool acl_is_trivial(struct acl *acl)
-{
-   return (acl_last(acl) != acl->acl_ext ? false : true);
-}
-
-static bool acl_nfs4_is_trivial(nfs4_acl_int_t *acl)
-{
-   int i;
-   int count = acl->aclEntryN;
-   nfs4_ace_int_t *ace;
-
-   for (i = 0; i < count; i++) {
-      ace = &acl->aclEntry[i];
-      if (!((ace->flags & ACE4_ID_SPECIAL) != 0 &&
-            (ace->aceWho.special_whoid == ACE4_WHO_OWNER ||
-             ace->aceWho.special_whoid == ACE4_WHO_GROUP ||
-             ace->aceWho.special_whoid == ACE4_WHO_EVERYONE) &&
-             ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE &&
-             ace->aceFlags == 0 &&
-            (ace->aceMask & ~(ACE4_READ_DATA |
-                              ACE4_LIST_DIRECTORY |
-                              ACE4_WRITE_DATA |
-                              ACE4_ADD_FILE |
-                              ACE4_EXECUTE)) == 0)) {
-         return false;
-      }
-   }
-   return true;
-}
-
-/*
- * Define the supported ACL streams for this OS
- */
-static int os_access_acl_streams[3] = {
-   STREAM_ACL_AIX_TEXT,
-   STREAM_ACL_AIX_AIXC,
-   STREAM_ACL_AIX_NFS4
-};
-static int os_default_acl_streams[1] = {
-   -1
-};
-
-static bacl_rtn_code aix_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   mode_t mode;
-   acl_type_t type;
-   size_t aclsize, acltxtsize;
-   bacl_rtn_code retval = bacl_rtn_error;
-   POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE);
-
-   /*
-    * First see how big the buffers should be.
-    */
-   memset(&type, 0, sizeof(acl_type_t));
-   type.u64 = ACL_ANY;
-   if (aclx_get(jcr->last_fname, GET_ACLINFO_ONLY, &type, NULL, &aclsize, &mode) < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         retval = bacl_rtn_ok;
-      } else if (errno == ENOSYS) {
-         /*
-          * If the filesystem reports it doesn't support ACLs we clear the
-          * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
-          * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
-          * when we change from one filesystem to an other.
-          */
-         jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE;
-         retval = bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-      }
-      goto get_out;
-   }
-
-   /*
-    * Make sure the buffers are big enough.
-    */
-   aclbuf = check_pool_memory_size(aclbuf, aclsize + 1);
-
-   /*
-    * Retrieve the ACL info.
-    */
-   if (aclx_get(jcr->last_fname, 0, &type, aclbuf, &aclsize, &mode) < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         retval = bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-      }
-      goto get_out;
-   }
-
-   /*
-    * See if the acl is non trivial.
-    */
-   switch (type.u64) {
-   case ACL_AIXC:
-      if (acl_is_trivial((struct acl *)aclbuf)) {
-         retval = bacl_rtn_ok;
-         goto get_out;
-      }
-      break;
-   case ACL_NFS4:
-      if (acl_nfs4_is_trivial((nfs4_acl_int_t *)aclbuf)) {
-         retval = bacl_rtn_ok;
-         goto get_out;
-      }
-      break;
-   default:
-      Mmsg2(jcr->errmsg,  _("Unknown acl type encountered on file \"%s\": %ld\n"),
-            jcr->last_fname, type.u64);
-      Dmsg1(100, "%s", jcr->errmsg);
-      goto get_out;
-   }
-
-   /*
-    * We have a non-trivial acl lets convert it into some ASCII form.
-    */
-   acltxtsize = sizeof_pool_memory(jcr->acl_ctx->content);
-   if (aclx_printStr(jcr->acl_ctx->content, &acltxtsize, aclbuf,
-                     aclsize, type, jcr->last_fname, 0) < 0) {
-      if (errno == ENOSPC) {
-         /*
-          * Our buffer is not big enough, acltxtsize should be updated with the value
-          * the aclx_printStr really need. So we increase the buffer and try again.
-          */
-         jcr->acl_ctx->content = check_pool_memory_size(jcr->acl_ctx->content, acltxtsize + 1);
-         if (aclx_printStr(jcr->acl_ctx->content, &acltxtsize, aclbuf,
-                           aclsize, type, jcr->last_fname, 0) < 0) {
-            Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"),
-                  jcr->last_fname);
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      } else {
-         Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-   }
-
-   jcr->acl_ctx->content_length = strlen(jcr->acl_ctx->content) + 1;
-   switch (type.u64) {
-   case ACL_AIXC:
-      retval = send_acl_stream(jcr, STREAM_ACL_AIX_AIXC);
-      break;
-   case ACL_NFS4:
-      retval = send_acl_stream(jcr, STREAM_ACL_AIX_NFS4);
-      break;
-   }
-
-get_out:
-   free_pool_memory(aclbuf);
-
-   return retval;
-}
-
-/*
- * See if a specific type of ACLs are supported on the filesystem
- * the file is located on.
- */
-static inline bool aix_query_acl_support(JCR *jcr,
-                                         uint64_t aclType,
-                                         acl_type_t *pacl_type_info)
-{
-   unsigned int i;
-   acl_types_list_t acl_type_list;
-   size_t acl_type_list_len = sizeof(acl_types_list_t);
-
-   memset(&acl_type_list, 0, sizeof(acl_type_list));
-   if (aclx_gettypes(jcr->last_fname, &acl_type_list, &acl_type_list_len)) {
-      return false;
-   }
-
-   for (i = 0; i < acl_type_list.num_entries; i++) {
-      if (acl_type_list.entries[i].u64 == aclType) {
-         memcpy(pacl_type_info, acl_type_list.entries + i, sizeof(acl_type_t));
-         return true;
-      }
-   }
-   return false;
-}
-
-static bacl_rtn_code aix_restore_acl_streams(JCR *jcr,
-                                            int stream,
-                                            char *content,
-                                            uint32_t content_length)
-{
-   int cnt;
-   acl_type_t type;
-   size_t aclsize;
-   bacl_rtn_code retval = bacl_rtn_error;
-   POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE);
-
-   switch (stream) {
-   case STREAM_ACL_AIX_TEXT:
-      /*
-       * Handle the old stream using the old system call for now.
-       */
-      if (acl_put(jcr->last_fname, content, 0) != 0) {
-         retval = bacl_rtn_error;
-         goto get_out;
-      }
-      retval = bacl_rtn_ok;
-      goto get_out;
-   case STREAM_ACL_AIX_AIXC:
-      if (!aix_query_acl_support(jcr, ACL_AIXC, &type)) {
-         Mmsg1(jcr->errmsg,
-               _("Trying to restore POSIX acl on file \"%s\" on filesystem without AIXC acl support\n"),
-               jcr->last_fname);
-         goto get_out;
-      }
-      break;
-   case STREAM_ACL_AIX_NFS4:
-      if (!aix_query_acl_support(jcr, ACL_NFS4, &type)) {
-         Mmsg1(jcr->errmsg,
-               _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without NFS4 acl support\n"),
-               jcr->last_fname);
-         goto get_out;
-      }
-      break;
-   default:
-      goto get_out;
-   }
-
-   /*
-    * Set the acl buffer to an initial size. For now we set it
-    * to the same size as the ASCII representation.
-    */
-   aclbuf = check_pool_memory_size(aclbuf, content_length);
-   aclsize = content_length;
-   if (aclx_scanStr(content, aclbuf, &aclsize, type) < 0) {
-      berrno be;
-      if (errno == ENOSPC) {
-         /*
-          * The buffer isn't big enough. The man page doesn't say that aclsize
-          * is updated to the needed size as what is done with aclx_printStr.
-          * So for now we try to increase the buffer a maximum of 3 times
-          * and retry the conversion.
-          */
-         for (cnt = 0; cnt < 3; cnt++) {
-            aclsize = 2 * aclsize;
-            aclbuf = check_pool_memory_size(aclbuf, aclsize);
-
-            if (aclx_scanStr(content, aclbuf, &aclsize, type) == 0) {
-               break;
-            }
-
-            /*
-             * See why we failed this time, ENOSPC retry if max retries not met,
-             * otherwise abort.
-             */
-            if (errno == ENOSPC && cnt < 3) {
-               continue;
-            }
-            Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
-                  jcr->last_fname, be.bstrerror(errno));
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      } else {
-         Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out
-      }
-   }
-
-   if (aclx_put(jcr->last_fname, SET_ACL, type, aclbuf, aclsize, 0) < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         retval = bacl_rtn_ok;
-      } else if (ENOSYS) {
-         /*
-          * If the filesystem reports it doesn't support ACLs we clear the
-          * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
-          * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
-          * when we change from one filesystem to an other.
-          */
-         jcr->acl_ctx->flags &= ~BACL_FLAG_RESTORE_NATIVE;
-      } else {
-         Mmsg2(jcr->errmsg, _("aclx_put error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-   }
-
-   retval = bacl_rtn_ok;
-
-get_out:
-   free_pool_memory(aclbuf);
-   return retval;
-}
-
-#else /* HAVE_EXTENDED_ACL */
-
-#include <sys/access.h>
-
-/*
- * Define the supported ACL streams for this OS
- */
-static int os_access_acl_streams[1] = {
-   STREAM_ACL_AIX_TEXT
-};
-static int os_default_acl_streams[1] = {
-   -1
-};
-
-static bacl_rtn_code aix_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   char *acl_text;
-
-   if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
-      jcr->acl_ctx->content_length =
-         pm_strcpy(jcr->acl_ctx->content, acl_text);
-      actuallyfree(acl_text);
-      return send_acl_stream(jcr, STREAM_ACL_AIX_TEXT);
-   }
-   return bacl_rtn_error;
-}
-
-static bacl_rtn_code aix_restore_acl_streams(JCR *jcr,
-                                            int stream,
-                                            char *content,
-                                            uint32_t content_length)
-{
-   if (acl_put(jcr->last_fname, content, 0) != 0) {
-      return bacl_rtn_error;
-   }
-   return bacl_rtn_ok;
-}
-#endif /* HAVE_EXTENDED_ACL */
-
-/*
- * For this OS setup the build and parse function pointer to the OS specific functions.
- */
-static bacl_rtn_code (*os_backup_acl_streams)
-                      (JCR *jcr, FF_PKT *ff_pkt) =
-                      aix_backup_acl_streams;
-static bacl_rtn_code (*os_restore_acl_streams)
-                      (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                      aix_restore_acl_streams;
-
-#elif defined(HAVE_DARWIN_OS) || \
-      defined(HAVE_FREEBSD_OS) || \
-      defined(HAVE_IRIX_OS) || \
-      defined(HAVE_OSF1_OS) || \
-      defined(HAVE_LINUX_OS) || \
-      defined(HAVE_HURD_OS)
-
-#include <sys/types.h>
-
-#ifdef HAVE_SYS_ACL_H
-#include <sys/acl.h>
-#else
-#error "configure failed to detect availability of sys/acl.h"
-#endif
-
-/*
- * On IRIX we can get shortened ACLs
- */
-#if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
-#define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
-#endif
-
-/*
- * On Linux we can get numeric and/or shorted ACLs
- */
-#if defined(HAVE_LINUX_OS)
-#if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
-#define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
-#elif defined(BACL_WANT_SHORT_ACLS)
-#define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
-#elif defined(BACL_WANT_NUMERIC_IDS)
-#define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
-#endif
-#ifdef BACL_ALTERNATE_TEXT
-#include <acl/libacl.h>
-#define acl_to_text(acl,len)     (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
-#endif
-#endif
-
-/*
- * On FreeBSD we can get numeric ACLs
- */
-#if defined(HAVE_FREEBSD_OS)
-#if defined(BACL_WANT_NUMERIC_IDS)
-#define BACL_ALTERNATE_TEXT            ACL_TEXT_NUMERIC_IDS
-#endif
-#ifdef BACL_ALTERNATE_TEXT
-#define acl_to_text(acl,len)     (acl_to_text_np((acl), (len), BACL_ALTERNATE_TEXT))
-#endif
-#endif
-
-/*
- * Some generic functions used by multiple OSes.
- */
-static acl_type_t bac_to_os_acltype(bacl_type acltype)
-{
-   acl_type_t ostype;
-
-   switch (acltype) {
-   case BACL_TYPE_ACCESS:
-      ostype = ACL_TYPE_ACCESS;
-      break;
-   case BACL_TYPE_DEFAULT:
-      ostype = ACL_TYPE_DEFAULT;
-      break;
-#ifdef HAVE_ACL_TYPE_NFS4
-      /*
-       * FreeBSD has an additional acl type named ACL_TYPE_NFS4.
-       */
-   case BACL_TYPE_NFS4:
-      ostype = ACL_TYPE_NFS4;
-      break;
-#endif
-#ifdef HAVE_ACL_TYPE_DEFAULT_DIR
-   case BACL_TYPE_DEFAULT_DIR:
-      /*
-       * TRU64 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
-       */
-      ostype = ACL_TYPE_DEFAULT_DIR;
-      break;
-#endif
-#ifdef HAVE_ACL_TYPE_EXTENDED
-   case BACL_TYPE_EXTENDED:
-      /*
-       * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
-       */
-      ostype = ACL_TYPE_EXTENDED;
-      break;
-#endif
-   default:
-      /*
-       * This should never happen, as the per OS version function only tries acl
-       * types supported on a certain platform.
-       */
-      ostype = (acl_type_t)ACL_TYPE_NONE;
-      break;
-   }
-   return ostype;
-}
-
-static int acl_count_entries(acl_t acl)
-{
-   int count = 0;
-#if defined(HAVE_FREEBSD_OS) || \
-    defined(HAVE_LINUX_OS) || \
-    defined(HAVE_HURD_OS)
-   acl_entry_t ace;
-   int entry_available;
-
-   entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
-   while (entry_available == 1) {
-      count++;
-      entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
-   }
-#elif defined(HAVE_IRIX_OS)
-   count = acl->acl_cnt;
-#elif defined(HAVE_OSF1_OS)
-   count = acl->acl_num;
-#elif defined(HAVE_DARWIN_OS)
-   acl_entry_t ace;
-   int entry_available;
-
-   entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
-   while (entry_available == 0) {
-      count++;
-      entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
-   }
-#endif
-   return count;
-}
-
-#if !defined(HAVE_DARWIN_OS)
-/*
- * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
- * There is no need to store those acls as we already store the stat bits too.
- */
-static bool acl_is_trivial(acl_t acl)
-{
-  /*
-   * acl is trivial if it has only the following entries:
-   * "user::",
-   * "group::",
-   * "other::"
-   */
-   acl_entry_t ace;
-   acl_tag_t tag;
-#if defined(HAVE_FREEBSD_OS) || \
-    defined(HAVE_LINUX_OS) || \
-    defined(HAVE_HURD_OS)
-   int entry_available;
-
-   entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
-   while (entry_available == 1) {
-      /*
-       * Get the tag type of this acl entry.
-       * If we fail to get the tagtype we call the acl non-trivial.
-       */
-      if (acl_get_tag_type(ace, &tag) < 0)
-         return true;
-      /*
-       * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
-       */
-      if (tag != ACL_USER_OBJ &&
-          tag != ACL_GROUP_OBJ &&
-          tag != ACL_OTHER)
-         return false;
-      entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
-   }
-   return true;
-#elif defined(HAVE_IRIX_OS)
-   int n;
-
-   for (n = 0; n < acl->acl_cnt; n++) {
-      ace = &acl->acl_entry[n];
-      tag = ace->ae_tag;
-
-      /*
-       * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
-       */
-      if (tag != ACL_USER_OBJ &&
-          tag != ACL_GROUP_OBJ &&
-          tag != ACL_OTHER_OBJ)
-         return false;
-   }
-   return true;
-#elif defined(HAVE_OSF1_OS)
-   int count;
-
-   ace = acl->acl_first;
-   count = acl->acl_num;
-
-   while (count > 0) {
-      tag = ace->entry->acl_type;
-      /*
-       * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
-       */
-      if (tag != ACL_USER_OBJ &&
-          tag != ACL_GROUP_OBJ &&
-          tag != ACL_OTHER)
-         return false;
-      /*
-       * On Tru64, perm can also contain non-standard bits such as
-       * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
-       */
-      if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
-         return false;
-      ace = ace->next;
-      count--;
-   }
-   return true;
-#endif
-}
-#endif
-
-/*
- * Generic wrapper around acl_get_file call.
- */
-static bacl_rtn_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype)
-{
-   acl_t acl;
-   acl_type_t ostype;
-   char *acl_text;
-   bacl_rtn_code retval = bacl_rtn_ok;
-
-   ostype = bac_to_os_acltype(acltype);
-   acl = acl_get_file(jcr->last_fname, ostype);
-   if (acl) {
-      /*
-       * From observation, IRIX's acl_get_file() seems to return a
-       * non-NULL acl with a count field of -1 when a file has no ACL
-       * defined, while IRIX's acl_to_text() returns NULL when presented
-       * with such an ACL.
-       *
-       * For all other implmentations we check if there are more then
-       * zero entries in the acl returned.
-       */
-      if (acl_count_entries(acl) <= 0) {
-         goto get_out;
-      }
-
-      /*
-       * Make sure this is not just a trivial ACL.
-       */
-#ifndef HAVE_DARWIN_OS
-      if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
-         /*
-          * The ACLs simply reflect the (already known) standard permissions
-          * So we don't send an ACL stream to the SD.
-          */
-         goto get_out;
-      }
-#endif
-
-#if defined(HAVE_FREEBSD_OS) && defined(_PC_ACL_NFS4)
-      if (acltype == BACL_TYPE_NFS4) {
-         int trivial;
-         if (acl_is_trivial_np(acl, &trivial) == 0) {
-            /* Trivial ACLS are standard permissions. Do not send to SD. */
-            if (trivial == 1) {
-               goto get_out;
-            }
-         }
-      }
-#endif
-
-      /*
-       * Convert the internal acl representation into a text representation.
-       */
-      if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
-         jcr->acl_ctx->content_length =
-            pm_strcpy(jcr->acl_ctx->content, acl_text);
-         acl_free(acl);
-         acl_free(acl_text);
-         return bacl_rtn_ok;
-      }
-
-      berrno be;
-      Mmsg2(jcr->errmsg,
-            _("acl_to_text error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      retval = bacl_rtn_error;
-   } else {
-      berrno be;
-
-      /*
-       * Handle errors gracefully.
-       */
-      switch (errno) {
-      case BACL_ENOTSUP:
-         /*
-          * If the filesystem reports it doesn't support ACLs we clear the
-          * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
-          * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
-          * when we change from one filesystem to an other.
-          */
-         jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE;
-         break;
-      case ENOENT:
-         break;
-      default:
-         /* Some real error */
-         Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         retval = bacl_rtn_error;
-         break;
-      }
-   }
-
-get_out:
-   if (acl) {
-      acl_free(acl);
-   }
-   pm_strcpy(jcr->acl_ctx->content, "");
-   jcr->acl_ctx->content_length = 0;
-   return retval;
-}
-
-/*
- * Generic wrapper around acl_set_file call.
- */
-static bacl_rtn_code generic_set_acl_on_os(JCR *jcr,
-                                            bacl_type acltype,
-                                            char *content,
-                                            uint32_t content_length)
-{
-   acl_t acl;
-   acl_type_t ostype;
-
-   /*
-    * If we get empty default ACLs, clear ACLs now
-    */
-   ostype = bac_to_os_acltype(acltype);
-   if (ostype == ACL_TYPE_DEFAULT && strlen(content) == 0) {
-      if (acl_delete_def_file(jcr->last_fname) == 0) {
-         return bacl_rtn_ok;
-      }
-      berrno be;
-
-      switch (errno) {
-      case ENOENT:
-         return bacl_rtn_ok;
-      case BACL_ENOTSUP:
-         /*
-          * If the filesystem reports it doesn't support ACLs we clear the
-          * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
-          * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
-          * when we change from one filesystem to an other.
-          */
-         jcr->acl_ctx->flags &= ~BACL_FLAG_RESTORE_NATIVE;
-         Mmsg1(jcr->errmsg,
-               _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"),
-               jcr->last_fname);
-         return bacl_rtn_error;
-      default:
-         Mmsg2(jcr->errmsg,
-               _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         return bacl_rtn_error;
-      }
-   }
-
-   acl = acl_from_text(content);
-   if (acl == NULL) {
-      berrno be;
-
-      Mmsg2(jcr->errmsg,
-            _("acl_from_text error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      return bacl_rtn_error;
-   }
-
-#ifndef HAVE_FREEBSD_OS
-   /*
-    * FreeBSD always fails acl_valid() - at least on valid input...
-    * As it does the right thing, given valid input, just ignore acl_valid().
-    */
-   if (acl_valid(acl) != 0) {
-      berrno be;
-
-      Mmsg2(jcr->errmsg,
-            _("acl_valid error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      acl_free(acl);
-      return bacl_rtn_error;
-   }
-#endif
-
-   /*
-    * Restore the ACLs, but don't complain about links which really should
-    * not have attributes, and the file it is linked to may not yet be restored.
-    * This is only true for the old acl streams as in the new implementation we
-    * don't save acls of symlinks (which cannot have acls anyhow)
-    */
-   if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
-      berrno be;
-      switch (errno) {
-      case ENOENT:
-         acl_free(acl);
-         return bacl_rtn_ok;
-      case BACL_ENOTSUP:
-         /*
-          * If the filesystem reports it doesn't support ACLs we clear the
-          * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
-          * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
-          * when we change from one filesystem to an other.
-          */
-         jcr->acl_ctx->flags &= ~BACL_FLAG_RESTORE_NATIVE;
-         Mmsg1(jcr->errmsg,
-               _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         acl_free(acl);
-         return bacl_rtn_error;
-      default:
-         Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         acl_free(acl);
-         return bacl_rtn_error;
-      }
-   }
-   acl_free(acl);
-   return bacl_rtn_ok;
-}
-
-/*
- * OS specific functions for handling different types of acl streams.
- */
-#if defined(HAVE_DARWIN_OS)
-/*
- * Define the supported ACL streams for this OS
- */
-static int os_access_acl_streams[1] = {
-   STREAM_ACL_DARWIN_ACCESS
-};
-static int os_default_acl_streams[1] = {
-   -1
-};
-
-static bacl_rtn_code darwin_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-#if defined(HAVE_ACL_TYPE_EXTENDED)
-   /*
-    * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
-    * and acl_get_file (name, ACL_TYPE_DEFAULT)
-    * always return NULL / EINVAL.  There is no point in making
-    * these two useless calls.  The real ACL is retrieved through
-    * acl_get_file (name, ACL_TYPE_EXTENDED).
-    *
-    * Read access ACLs for files, dirs and links
-    */
-   if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bacl_rtn_fatal)
-      return bacl_rtn_fatal;
-#else
-   /*
-    * Read access ACLs for files, dirs and links
-    */
-   if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_rtn_fatal)
-      return bacl_rtn_fatal;
-#endif
-
-   if (jcr->acl_ctx->content_length > 0) {
-      return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS);
-   }
-   return bacl_rtn_ok;
-}
-
-static bacl_rtn_code darwin_restore_acl_streams(JCR *jcr,
-                                               int stream,
-                                               char *content,
-                                               uint32_t content_length)
-{
-#if defined(HAVE_ACL_TYPE_EXTENDED)
-      return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED, content, content_length);
-#else
-      return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length);
-#endif
-}
-
-/*
- * For this OS setup the build and parse function pointer to the OS specific functions.
- */
-static bacl_rtn_code (*os_backup_acl_streams)
-                      (JCR *jcr, FF_PKT *ff_pkt) =
-                      darwin_backup_acl_streams;
-static bacl_rtn_code (*os_restore_acl_streams)
-                      (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                      darwin_restore_acl_streams;
-
-#elif defined(HAVE_FREEBSD_OS)
-/*
- * Define the supported ACL streams for these OSes
- */
-static int os_access_acl_streams[2] = {
-   STREAM_ACL_FREEBSD_ACCESS,
-   STREAM_ACL_FREEBSD_NFS4
-};
-static int os_default_acl_streams[1] = {
-   STREAM_ACL_FREEBSD_DEFAULT
-};
-
-static bacl_rtn_code freebsd_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   int acl_enabled = 0;
-   bacl_type acltype = BACL_TYPE_NONE;
-
-#if defined(_PC_ACL_NFS4)
-   /*
-    * See if filesystem supports NFS4 acls.
-    */
-   acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4);
-   if (acl_enabled < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         return bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bacl_rtn_error;
-      }
-   } else if (acl_enabled != 0) {
-      acltype = BACL_TYPE_NFS4;
-   }
-#endif
-
-   if (acl_enabled == 0) {
-      /*
-       * See if filesystem supports POSIX acls.
-       */
-      acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
-      if (acl_enabled < 0) {
-         berrno be;
-         if (errno == ENOENT) {
-            return bacl_rtn_ok;
-         } else {
-            Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
-                  jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            return bacl_rtn_error;
-         }
-      } else if (acl_enabled != 0) {
-         acltype = BACL_TYPE_ACCESS;
-      }
-   }
-
-   /*
-    * If the filesystem reports it doesn't support ACLs we clear the
-    * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
-    * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
-    * when we change from one filesystem to an other.
-    */
-   if (acl_enabled == 0) {
-      jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE;
-      pm_strcpy(jcr->acl_ctx->content, "");
-      jcr->acl_ctx->content_length = 0;
-      return bacl_rtn_ok;
-   }
-
-   /*
-    * Based on the supported ACLs retrieve and store them.
-    */
-   switch (acltype) {
-   case BACL_TYPE_NFS4:
-      /*
-       * Read NFS4 ACLs for files, dirs and links
-       */
-      if (generic_get_acl_from_os(jcr, BACL_TYPE_NFS4) == bacl_rtn_fatal)
-         return bacl_rtn_fatal;
-
-      if (jcr->acl_ctx->content_length > 0) {
-         if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_NFS4) == bacl_rtn_fatal)
-            return bacl_rtn_fatal;
-      }
-      break;
-   case BACL_TYPE_ACCESS:
-      /*
-       * Read access ACLs for files, dirs and links
-       */
-      if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_rtn_fatal)
-         return bacl_rtn_fatal;
-
-      if (jcr->acl_ctx->content_length > 0) {
-         if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_ACCESS) == bacl_rtn_fatal)
-            return bacl_rtn_fatal;
-      }
-
-      /*
-       * Directories can have default ACLs too
-       */
-      if (ff_pkt->type == FT_DIREND) {
-         if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_rtn_fatal)
-            return bacl_rtn_fatal;
-         if (jcr->acl_ctx->content_length > 0) {
-            if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_DEFAULT) == bacl_rtn_fatal)
-               return bacl_rtn_fatal;
-         }
-      }
-      break;
-   default:
-      break;
-   }
-
-   return bacl_rtn_ok;
-}
-
-static bacl_rtn_code freebsd_restore_acl_streams(JCR *jcr,
-                                                int stream,
-                                                char *content,
-                                                uint32_t content_length)
-{
-   int acl_enabled = 0;
-   const char *acl_type_name;
-
-   /*
-    * First make sure the filesystem supports acls.
-    */
-   switch (stream) {
-   case STREAM_UNIX_ACCESS_ACL:
-   case STREAM_ACL_FREEBSD_ACCESS:
-   case STREAM_UNIX_DEFAULT_ACL:
-   case STREAM_ACL_FREEBSD_DEFAULT:
-      acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
-      acl_type_name = "POSIX";
-      break;
-   case STREAM_ACL_FREEBSD_NFS4:
-#if defined(_PC_ACL_NFS4)
-      acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4);
-#endif
-      acl_type_name = "NFS4";
-      break;
-   default:
-      acl_type_name = "unknown";
-      break;
-   }
-
-   if (acl_enabled < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         return bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bacl_rtn_error;
-      }
-   } else if (acl_enabled == 0) {
-      /*
-       * If the filesystem reports it doesn't support ACLs we clear the
-       * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
-       * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
-       * when we change from one filesystem to an other.
-       */
-      jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE;
-      Mmsg2(jcr->errmsg,
-            _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"),
-            jcr->last_fname, acl_type_name);
-      return bacl_rtn_error;
-   }
-
-   /*
-    * Restore the ACLs.
-    */
-   switch (stream) {
-   case STREAM_UNIX_ACCESS_ACL:
-   case STREAM_ACL_FREEBSD_ACCESS:
-      return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length);
-   case STREAM_UNIX_DEFAULT_ACL:
-   case STREAM_ACL_FREEBSD_DEFAULT:
-      return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT, content, content_length);
-   case STREAM_ACL_FREEBSD_NFS4:
-      return generic_set_acl_on_os(jcr, BACL_TYPE_NFS4, content, content_length);
-   default:
-      break;
-   }
-   return bacl_rtn_error;
-}
-
-/*
- * For this OSes setup the build and parse function pointer to the OS specific functions.
- */
-static bacl_rtn_code (*os_backup_acl_streams)
-                      (JCR *jcr, FF_PKT *ff_pkt) =
-                      freebsd_backup_acl_streams;
-static bacl_rtn_code (*os_restore_acl_streams)
-                      (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                      freebsd_restore_acl_streams;
-
-#elif defined(HAVE_IRIX_OS) || \
-      defined(HAVE_LINUX_OS) || \
-      defined(HAVE_HURD_OS)
-/*
- * Define the supported ACL streams for these OSes
- */
-#if defined(HAVE_IRIX_OS)
-static int os_access_acl_streams[1] = {STREAM_ACL_IRIX_ACCESS_ACL};
-static int os_default_acl_streams[1] = {STREAM_ACL_IRIX_DEFAULT_ACL};
-#elif defined(HAVE_LINUX_OS)
-static int os_access_acl_streams[1] = {STREAM_ACL_LINUX_ACCESS};
-static int os_default_acl_streams[1] = {STREAM_ACL_LINUX_DEFAULT};
-#elif defined(HAVE_HURD_OS)
-static int os_access_acl_streams[1] = {STREAM_ACL_HURD_ACCESS};
-static int os_default_acl_streams[1] = {STREAM_ACL_HURD_DEFAULT};
-#endif
-
-static bacl_rtn_code generic_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   /*
-    * Read access ACLs for files, dirs and links
-    */
-   if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_rtn_fatal)
-      return bacl_rtn_fatal;
-
-   if (jcr->acl_ctx->content_length > 0) {
-      if (send_acl_stream(jcr, os_access_acl_streams[0]) == bacl_rtn_fatal)
-         return bacl_rtn_fatal;
-   }
-
-   /*
-    * Directories can have default ACLs too
-    */
-   if (ff_pkt->type == FT_DIREND) {
-      if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_rtn_fatal)
-         return bacl_rtn_fatal;
-      if (jcr->acl_ctx->content_length > 0) {
-         if (send_acl_stream(jcr, os_default_acl_streams[0]) == bacl_rtn_fatal)
-            return bacl_rtn_fatal;
-      }
-   }
-   return bacl_rtn_ok;
-}
-
-static bacl_rtn_code generic_restore_acl_streams(JCR *jcr,
-                                                int stream,
-                                                char *content,
-                                                uint32_t content_length)
-{
-   unsigned int cnt;
-
-   switch (stream) {
-   case STREAM_UNIX_ACCESS_ACL:
-      return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length);
-   case STREAM_UNIX_DEFAULT_ACL:
-      return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT, content, content_length);
-   default:
-      /*
-       * See what type of acl it is.
-       */
-      for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
-         if (os_access_acl_streams[cnt] == stream) {
-            return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length);
-         }
-      }
-      for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
-         if (os_default_acl_streams[cnt] == stream) {
-            return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT, content, content_length);
-         }
-      }
-      break;
-   }
-   return bacl_rtn_error;
-}
-
-/*
- * For this OSes setup the build and parse function pointer to the OS specific functions.
- */
-static bacl_rtn_code (*os_backup_acl_streams)
-                      (JCR *jcr, FF_PKT *ff_pkt) =
-                      generic_backup_acl_streams;
-static bacl_rtn_code (*os_restore_acl_streams)
-                      (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                      generic_restore_acl_streams;
-
-#elif defined(HAVE_OSF1_OS)
-
-/*
- * Define the supported ACL streams for this OS
- */
-static int os_access_acl_streams[1] = {
-   STREAM_ACL_TRU64_ACCESS
-};
-static int os_default_acl_streams[2] = {
-   STREAM_ACL_TRU64_DEFAULT,
-   STREAM_ACL_TRU64_DEFAULT_DIR
-};
-
-static bacl_rtn_code tru64_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   /*
-    * Read access ACLs for files, dirs and links
-    */
-   if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_rtn_fatal) {
-      return bacl_rtn_error;
-   if (jcr->acl_ctx->content_length > 0) {
-      if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
-         return bacl_rtn_error;
-   }
-   /*
-    * Directories can have default ACLs too
-    */
-   if (ff_pkt->type == FT_DIREND) {
-      if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_rtn_fatal) {
-         return bacl_rtn_error;
-      if (jcr->acl_ctx->content_length > 0) {
-         if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
-            return bacl_rtn_error;
-      }
-      /*
-       * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
-       * This is an inherited acl for all subdirs.
-       * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
-       * Section 21.5 Default ACLs
-       */
-      if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR) == bacl_rtn_fatal) {
-         return bacl_rtn_error;
-      if (jcr->acl_ctx->content_length > 0) {
-         if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
-            return bacl_rtn_error;
-      }
-   }
-   return bacl_rtn_ok;
-}
-
-static bacl_rtn_code tru64_restore_acl_streams(JCR *jcr,
-                                              int stream,
-                                              char *content,
-                                              uint32_t content_length)
-{
-   switch (stream) {
-   case STREAM_UNIX_ACCESS_ACL:
-   case STREAM_ACL_TRU64_ACCESS_ACL:
-      return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS, content, content_length);
-   case STREAM_UNIX_DEFAULT_ACL:
-   case STREAM_ACL_TRU64_DEFAULT_ACL:
-      return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT, content, content_length);
-   case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
-      return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR, content, content_length);
-}
-
-/*
- * For this OS setup the build and parse function pointer to the OS specific functions.
- */
-static bacl_rtn_code (*os_backup_acl_streams)
-                      (JCR *jcr, FF_PKT *ff_pkt) =
-                      tru64_backup_acl_streams;
-static bacl_rtn_code (*os_restore_acl_streams)
-                      (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                      tru64_restore_acl_streams;
-
-#endif
-
-#elif defined(HAVE_HPUX_OS)
-#ifdef HAVE_SYS_ACL_H
-#include <sys/acl.h>
-#else
-#error "configure failed to detect availability of sys/acl.h"
-#endif
-
-#include <acllib.h>
-
-/*
- * Define the supported ACL streams for this OS
- */
-static int os_access_acl_streams[1] = {
-   STREAM_ACL_HPUX_ACL_ENTRY
-};
-static int os_default_acl_streams[1] = {
-   -1
-};
-
-/*
- * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
- * There is no need to store those acls as we already store the stat bits too.
- */
-static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
-{
-   int n;
-   struct acl_entry ace
-
-   for (n = 0; n < count; n++) {
-      ace = entries[n];
-      /*
-       * See if this acl just is the stat mode in acl form.
-       */
-      if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
-            (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
-            (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
-         return false;
-   }
-   return true;
-}
-
-/*
- * OS specific functions for handling different types of acl streams.
- */
-static bacl_rtn_code hpux_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   int n;
-   struct acl_entry acls[NACLENTRIES];
-   char *acl_text;
-
-   if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
-      berrno be;
-      switch (errno) {
-      case BACL_ENOTSUP:
-         /*
-          * Not supported, just pretend there is nothing to see
-          *
-          * If the filesystem reports it doesn't support ACLs we clear the
-          * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
-          * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
-          * when we change from one filesystem to an other.
-          */
-         jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE;
-         pm_strcpy(jcr->acl_ctx->content, "");
-         jcr->acl_ctx->content_length = 0;
-         return bacl_rtn_ok;
-      case ENOENT:
-         pm_strcpy(jcr->acl_ctx->content, "");
-         jcr->acl_ctx->content_length = 0;
-         return bacl_rtn_ok;
-      default:
-         Mmsg2(jcr->errmsg, _("getacl error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         pm_strcpy(jcr->acl_ctx->content, "");
-         jcr->acl_ctx->content_length = 0;
-         return bacl_rtn_error;
-      }
-   }
-   if (n == 0) {
-      pm_strcpy(jcr->acl_ctx->content, "");
-      jcr->acl_ctx->content_length = 0;
-      return bacl_rtn_ok;
-   }
-   if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
-      if (acl_is_trivial(n, acls, ff_pkt->statp)) {
-         /*
-          * The ACLs simply reflect the (already known) standard permissions
-          * So we don't send an ACL stream to the SD.
-          */
-         pm_strcpy(jcr->acl_ctx->content, "");
-         jcr->acl_ctx->content_length = 0;
-         return bacl_rtn_ok;
-      }
-      if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
-         jcr->acl_ctx->content_length =
-           pm_strcpy(jcr->acl_ctx->content, acl_text);
-         actuallyfree(acl_text);
-
-         return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
-      }
-
-      berrno be;
-      Mmsg2(jcr->errmsg, _("acltostr error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      return bacl_rtn_error;
-   }
-   return bacl_rtn_error;
-}
-
-static bacl_rtn_code hpux_restore_acl_streams(JCR *jcr,
-                                             int stream,
-                                             char *content,
-                                             uint32_t content_length)
-{
-   int n, stat;
-   struct acl_entry acls[NACLENTRIES];
-
-   n = strtoacl(content, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
-   if (n <= 0) {
-      berrno be;
-      Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
-         jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      return bacl_rtn_error;
-   }
-   if (strtoacl(content, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
-      berrno be;
-      Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
-         jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      return bacl_rtn_error;
-   }
-   /*
-    * Restore the ACLs, but don't complain about links which really should
-    * not have attributes, and the file it is linked to may not yet be restored.
-    * This is only true for the old acl streams as in the new implementation we
-    * don't save acls of symlinks (which cannot have acls anyhow)
-    */
-   if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
-      berrno be;
-
-      switch (errno) {
-      case ENOENT:
-         return bacl_rtn_ok;
-      case BACL_ENOTSUP:
-         /*
-          * If the filesystem reports it doesn't support ACLs we clear the
-          * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
-          * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
-          * when we change from one filesystem to an other.
-          */
-         jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE;
-         Mmsg1(jcr->errmsg,
-               _("setacl error on file \"%s\": filesystem doesn't support ACLs\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bacl_rtn_error;
-      default:
-         Mmsg2(jcr->errmsg, _("setacl error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bacl_rtn_error;
-      }
-   }
-   return bacl_rtn_ok;
-}
-
-/*
- * For this OS setup the build and parse function pointer to the OS specific functions.
- */
-static bacl_rtn_code (*os_backup_acl_streams)
-                      (JCR *jcr, FF_PKT *ff_pkt) =
-                      hpux_backup_acl_streams;
-
-static bacl_rtn_code (*os_restore_acl_streams)
-                      (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                      hpux_restore_acl_streams;
-
-#elif defined(HAVE_SUN_OS)
-#ifdef HAVE_SYS_ACL_H
-#include <sys/acl.h>
-#else
-#error "configure failed to detect availability of sys/acl.h"
-#endif
-
-#if defined(HAVE_EXTENDED_ACL)
-/*
- * We define some internals of the Solaris acl libs here as those
- * are not exposed yet. Probably because they want us to see the
- * acls as opague data. But as we need to support different platforms
- * and versions of Solaris we need to expose some data to be able
- * to determine the type of acl used to stuff it into the correct
- * data stream. I know this is far from portable, but maybe the
- * proper interface is exposed later on and we can get ride of
- * this kludge. Newer versions of Solaris include sys/acl_impl.h
- * which has implementation details of acls, if thats included we
- * don't have to define it ourself.
- */
-#if !defined(_SYS_ACL_IMPL_H)
-typedef enum acl_type {
-   ACLENT_T = 0,
-   ACE_T = 1
-} acl_type_t;
-#endif
-
-/*
- * Two external references to functions in the libsec library function not in current include files.
- */
-extern "C" {
-int acl_type(acl_t *);
-char *acl_strerror(int);
-}
-
-/*
- * Define the supported ACL streams for this OS
- */
-static int os_access_acl_streams[2] = {
-   STREAM_ACL_SOLARIS_POSIX,
-   STREAM_ACL_SOLARIS_NFS4
-};
-static int os_default_acl_streams[1] = {
-   -1
-};
-
-/*
- * As the new libsec interface with acl_totext and acl_fromtext also handles
- * the old format from acltotext we can use the new functions even
- * for acls retrieved and stored in the database with older fd versions. If the
- * new interface is not defined (Solaris 9 and older we fall back to the old code)
- */
-static bacl_rtn_code solaris_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   int acl_enabled, flags;
-   acl_t *aclp;
-   char *acl_text;
-   bacl_rtn_code stream_status = bacl_rtn_error;
-
-   /*
-    * See if filesystem supports acls.
-    */
-   acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
-   if (acl_enabled == 0) {
-      /*
-       * If the filesystem reports it doesn't support ACLs we clear the
-       * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
-       * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
-       * when we change from one filesystem to an other.
-       */
-      jcr->acl_ctx->flags &= ~BACL_FLAG_SAVE_NATIVE;
-      pm_strcpy(jcr->acl_ctx->content, "");
-      jcr->acl_ctx->content_length = 0;
-      return bacl_rtn_ok;
-   }
-   if (acl_enabled < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         return bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bacl_rtn_error;
-      }
-   }
-
-   /*
-    * Get ACL info: don't bother allocating space if there is only a trivial ACL.
-    */
-   if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         return bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, acl_strerror(errno));
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bacl_rtn_error;
-      }
-   }
-
-   if (!aclp) {
-      /*
-       * The ACLs simply reflect the (already known) standard permissions
-       * So we don't send an ACL stream to the SD.
-       */
-      pm_strcpy(jcr->acl_ctx->content, "");
-      jcr->acl_ctx->content_length = 0;
-      return bacl_rtn_ok;
-   }
-
-#if defined(ACL_SID_FMT)
-   /*
-    * New format flag added in newer Solaris versions.
-    */
-   flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
-#else
-   flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
-#endif /* ACL_SID_FMT */
-
-   if ((acl_text = acl_totext(aclp, flags)) != NULL) {
-      jcr->acl_ctx->content_length =
-         pm_strcpy(jcr->acl_ctx->content, acl_text);
-      actuallyfree(acl_text);
-
-      switch (acl_type(aclp)) {
-      case ACLENT_T:
-         stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_POSIX);
-         break;
-      case ACE_T:
-         stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_NFS4);
-         break;
-      default:
-         break;
-      }
-
-      acl_free(aclp);
-   }
-   return stream_status;
-}
-
-static bacl_rtn_code solaris_restore_acl_streams(JCR *jcr, int stream, char *content,
-                                                 uint32_t content_length)
-{
-   acl_t *aclp;
-   int acl_enabled, error;
-
-   if (stream != STREAM_UNIX_ACCESS_ACL || stream != STREAM_ACL_SOLARIS_POSIX ||
-       stream != STREAM_ACL_SOLARIS_NFS4) {
-      return bacl_rtn_error;
-   }
-
-   /*
-    * First make sure the filesystem supports acls.
-    */
-   acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
-   if (acl_enabled == 0) {
-      /*
-       * If the filesystem reports it doesn't support ACLs we clear the
-       * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
-       * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
-       * when we change from one filesystem to an other.
-       */
-      jcr->acl_ctx->flags &= ~BACL_FLAG_RESTORE_NATIVE;
-      Mmsg1(jcr->errmsg,
-            _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
-            jcr->last_fname);
-      return bacl_rtn_error;
-   } else if (acl_enabled < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         return bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bacl_rtn_error;
-      }
-   }
-   /*
-    * On a filesystem with ACL support make sure this particular ACL type can be restored.
-    */
-   switch (stream) {
-   case STREAM_ACL_SOLARIS_POSIX:
-      /*
-       * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
-       */
-      if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
-         Mmsg1(jcr->errmsg,
-               _("Trying to restore POSIX acl on file \"%s\" on filesystem without aclent acl support\n"),
-               jcr->last_fname);
-         return bacl_rtn_error;
-      }
-      break;
-   case STREAM_ACL_SOLARIS_NFS4:
-      /*
-       * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
-       */
-      if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
-         Mmsg1(jcr->errmsg,
-               _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without ace acl support\n"),
-               jcr->last_fname);
-         return bacl_rtn_error;
-      }
-      break;
-   default:
-      /*
-       * Stream id which doesn't describe the type of acl which is encoded.
-       */
-      break;
-   }
-
-   if ((error = acl_fromtext(content, &aclp)) != 0) {
-      Mmsg2(jcr->errmsg,
-            _("acl_fromtext error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, acl_strerror(error));
-      Dmsg1(100, "%s", jcr->errmsg);
-      return bacl_rtn_error;
-   }
-
-   /*
-    * Validate that the conversion gave us the correct acl type.
-    */
-   switch (stream) {
-   case STREAM_ACL_SOLARIS_POSIX:
-      if (acl_type(aclp) != ACLENT_T) {
-         Mmsg1(jcr->errmsg,
-               _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
-               jcr->last_fname);
-         return bacl_rtn_error;
-      }
-      break;
-   case STREAM_ACL_SOLARIS_NFS4:
-      if (acl_type(aclp) != ACE_T) {
-         Mmsg1(jcr->errmsg,
-               _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
-               jcr->last_fname);
-         return bacl_rtn_error;
-      }
-      break;
-   default:
-      /*
-       * Stream id which doesn't describe the type of acl which is encoded.
-       */
-      break;
-   }
-
-   /*
-    * Restore the ACLs, but don't complain about links which really should
-    * not have attributes, and the file it is linked to may not yet be restored.
-    * This is only true for the old acl streams as in the new implementation we
-    * don't save acls of symlinks (which cannot have acls anyhow)
-    */
-   if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
-      if (errno == ENOENT) {
-         acl_free(aclp);
-         return bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, acl_strerror(error));
-         Dmsg1(100, "%s", jcr->errmsg);
-         acl_free(aclp);
-         return bacl_rtn_error;
-      }
-   }
-
-   acl_free(aclp);
-   return bacl_rtn_ok;
-}
-
-#else /* HAVE_EXTENDED_ACL */
-
-/*
- * Define the supported ACL streams for this OS
- */
-static int os_access_acl_streams[1] = {
-   STREAM_ACL_SOLARIS_POSIX
-};
-static int os_default_acl_streams[1] = {
-   -1
-};
-
-/*
- * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
- * There is no need to store those acls as we already store the stat bits too.
- */
-static bool acl_is_trivial(int count, aclent_t *entries)
-{
-   int n;
-   aclent_t *ace;
-
-   for (n = 0; n < count; n++) {
-      ace = &entries[n];
-
-      if (!(ace->a_type == USER_OBJ ||
-            ace->a_type == GROUP_OBJ ||
-            ace->a_type == OTHER_OBJ ||
-            ace->a_type == CLASS_OBJ))
-        return false;
-   }
-   return true;
-}
-
-/*
- * OS specific functions for handling different types of acl streams.
- */
-static bacl_rtn_code solaris_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   int n;
-   aclent_t *acls;
-   char *acl_text;
-
-   n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
-   if (n < MIN_ACL_ENTRIES) {
-      return bacl_rtn_error;
-   }
-
-   acls = (aclent_t *)malloc(n * sizeof(aclent_t));
-   if (acl(jcr->last_fname, GETACL, n, acls) == n) {
-      if (acl_is_trivial(n, acls)) {
-         /*
-          * The ACLs simply reflect the (already known) standard permissions
-          * So we don't send an ACL stream to the SD.
-          */
-         free(acls);
-         pm_strcpy(jcr->acl_ctx->content, "");
-         jcr->acl_ctx->content_length = 0;
-         return bacl_rtn_ok;
-      }
-
-      if ((acl_text = acltotext(acls, n)) != NULL) {
-         jcr->acl_ctx->content_length =
-            pm_strcpy(jcr->acl_ctx->content, acl_text);
-         actuallyfree(acl_text);
-         free(acls);
-         return send_acl_stream(jcr, STREAM_ACL_SOLARIS_POSIX);
-      }
-
-      berrno be;
-      Mmsg2(jcr->errmsg, _("acltotext error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-   }
-
-   free(acls);
-   return bacl_rtn_error;
-}
-
-static bacl_rtn_code solaris_restore_acl_streams(JCR *jcr, int stream, char *content,
-                                                uint32_t content_length)
-{
-   int n;
-   aclent_t *acls;
-
-   acls = aclfromtext(content, &n);
-   if (!acls) {
-      berrno be;
-
-      Mmsg2(jcr->errmsg, _("aclfromtext error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      return bacl_rtn_error;
-   }
-
-   /*
-    * Restore the ACLs, but don't complain about links which really should
-    * not have attributes, and the file it is linked to may not yet be restored.
-    */
-   if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
-      berrno be;
-      if (errno == ENOENT) {
-         actuallyfree(acls);
-         return bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         actuallyfree(acls);
-         return bacl_rtn_error;
-      }
-   }
-   actuallyfree(acls);
-   return bacl_rtn_ok;
-}
-#endif /* HAVE_EXTENDED_ACL */
-
-/*
- * For this OS setup the build and parse function pointer to the OS specific functions.
- */
-static bacl_rtn_code (*os_backup_acl_streams)
-                      (JCR *jcr, FF_PKT *ff_pkt) =
-                      solaris_backup_acl_streams;
-static bacl_rtn_code (*os_restore_acl_streams)
-                      (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                      solaris_restore_acl_streams;
-
-#endif /* HAVE_SUN_OS */
-#endif /* HAVE_ACL */
-
-#if defined(HAVE_AFS_ACL)
-
-#if defined(HAVE_AFS_AFSINT_H) && defined(HAVE_AFS_VENUS_H)
-#include <afs/afsint.h>
-#include <afs/venus.h>
-#else
-#error "configure failed to detect availability of afs/afsint.h and/or afs/venus.h"
-#endif
-
-/*
- * External references to functions in the libsys library function not in current include files.
- */
-extern "C" {
-long pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow);
-}
-
-static bacl_rtn_code afs_backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   int error;
-   struct ViceIoctl vip;
-   char acl_text[BUFSIZ];
-
-   /*
-    * AFS ACLs can only be set on a directory, so no need to try to
-    * request them for anything other then that.
-    */
-   if (ff_pkt->type != FT_DIREND) {
-      return bacl_rtn_ok;
-   }
-
-   vip.in = NULL;
-   vip.in_size = 0;
-   vip.out = acl_text;
-   vip.out_size = sizeof(acl_text);
-   memset((caddr_t)acl_text, 0, sizeof(acl_text));
-
-   if ((error = pioctl(jcr->last_fname, VIOCGETAL, &vip, 0)) < 0) {
-      berrno be;
-      Mmsg2(jcr->errmsg, _("pioctl VIOCGETAL error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-      return bacl_rtn_error;
-   }
-   jcr->acl_ctx->content_length = pm_strcpy(jcr->acl_ctx->content, acl_text);
-   return send_acl_stream(jcr, STREAM_ACL_AFS_TEXT);
-}
-
-static bacl_rtn_code afs_restore_acl_stream(JCR *jcr, int stream, char *content,
-                                           uint32_t content_length)
-{
-   int error;
-   struct ViceIoctl vip;
-
-   vip.in = content;
-   vip.in_size = content_length;
-   vip.out = NULL;
-   vip.out_size = 0;
-
-   if ((error = pioctl(jcr->last_fname, VIOCSETAL, &vip, 0)) < 0) {
-      berrno be;
-      Mmsg2(jcr->errmsg, _("pioctl VIOCSETAL error on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      return bacl_rtn_error;
-   }
-   return bacl_rtn_ok;
-}
-#endif /* HAVE_AFS_ACL */
-
-/*
- * Entry points when compiled with support for ACLs on a supported platform.
- */
-
-/*
- * Read and send an ACL for the last encountered file.
- */
-bool backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   bacl_rtn_code rtn = bacl_rtn_error;
-
-   if (!(ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK && !ff_pkt->cmd_plugin)) {
-      return true;      /* No acl request */
-   }
-   jcr->errmsg[0] = 0;
-
-   /*
-    * See if we are changing from one device to an other.
-    * We save the current device we are scanning and compare
-    * it with the current st_dev in the last stat performed on
-    * the file we are currently storing.
-    */
-   if (jcr->acl_ctx->current_dev != ff_pkt->statp.st_dev) {
-      /*
-       * Reset the acl save flags.
-       */
-      jcr->acl_ctx->flags = 0;
-
-#if defined(HAVE_AFS_ACL)
-      /*
-       * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem
-       * Set the BACL_FLAG_SAVE_AFS flag if it is. If not set the BACL_FLAG_SAVE_NATIVE flag.
-       */
-      if (fstype_cmp(ff_pkt, "afs")) {
-         jcr->acl_ctx->flags |= BACL_FLAG_SAVE_AFS;
-      } else {
-         jcr->acl_ctx->flags |= BACL_FLAG_SAVE_NATIVE;
-      }
-#else
-      jcr->acl_ctx->flags |= BACL_FLAG_SAVE_NATIVE;
-#endif
-
-      /*
-       * Save that we started scanning a new filesystem.
-       */
-      jcr->acl_ctx->current_dev = ff_pkt->statp.st_dev;
-   }
-
-#ifdef HAVE_AFS_ACL
-   /*
-    * See if the BACL_FLAG_SAVE_AFS flag is set which lets us know if we should
-    * save AFS ACLs.
-    */
-   if (jcr->acl_ctx->flags & BACL_FLAG_SAVE_AFS) {
-      rtn = afs_backup_acl_streams(jcr, ff_pkt);
-      goto get_out;
-   }
-#endif
-
-#ifdef HAVE_ACL
-   /*
-    * See if the BACL_FLAG_SAVE_NATIVE flag is set which lets us know if we should
-    * save native ACLs.
-    */
-   if (jcr->acl_ctx->flags & BACL_FLAG_SAVE_NATIVE) {
-      /*
-       * Call the appropriate function.
-       */
-      if (os_backup_acl_streams) {
-         rtn = os_backup_acl_streams(jcr, ff_pkt);
-         goto get_out;
-      }
-   } else {
-      return true;
-   }
-#endif
-
-get_out:
-   switch (rtn) {
-   case bacl_rtn_fatal:
-      return false;
-   case bacl_rtn_ok:
-      return true;
-   case bacl_rtn_error:
-      if (jcr->acl_ctx->nr_errors < ACL_MAX_ERROR_PRINT_PER_JOB) {
-         if (jcr->errmsg[0]) {
-            Jmsg(jcr, M_WARNING, 0, "Operating system ACLs not configured.\n");
-         } else {
-            Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
-         }
-         jcr->acl_ctx->nr_errors++;
-      }
-      return true;
-   }
-   /* Theoretically we cannot get here */
-   return false;
-}
-
-bacl_rtn_code restore_acl_streams(JCR *jcr, int stream,
-                  char *content, uint32_t content_length)
-{
-   int ret;
-   struct stat st;
-   unsigned int cnt;
-
-   /*
-    * See if we are changing from one device to an other.
-    * We save the current device we are restoring to and compare
-    * it with the current st_dev in the last stat performed on
-    * the file we are currently restoring.
-    */
-   ret = lstat(jcr->last_fname, &st);
-   if (ret < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         return bacl_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("Unable to stat file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bacl_rtn_error;
-      }
-   }
-   if (jcr->acl_ctx->current_dev != st.st_dev) {
-      /*
-       * Reset the acl save flags.
-       */
-      jcr->acl_ctx->flags = 0;
-
-#if defined(HAVE_AFS_ACL)
-      /*
-       * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem
-       * Set the BACL_FLAG_RESTORE_AFS flag if it is. If not set the BACL_FLAG_RETORE_NATIVE flag.
-       */
-      if (fstype_cmp(ff_pkt, "afs")) {
-         jcr->acl_ctx->flags |= BACL_FLAG_RESTORE_AFS;
-      } else {
-         jcr->acl_ctx->flags |= BACL_FLAG_RESTORE_NATIVE;
-      }
-#else
-      jcr->acl_ctx->flags |= BACL_FLAG_RESTORE_NATIVE;
-#endif
-
-      /* Save that we started restoring to a new filesystem. */
-      jcr->acl_ctx->current_dev = st.st_dev;
-   }
-
-   switch (stream) {
-#ifdef HAVE_AFS_ACL
-   case STREAM_ACL_AFS_TEXT:
-      if (jcr->acl_ctx->flags & BACL_FLAG_RESTORE_AFS) {
-         return afs_restore_acl_stream(jcr, stream, content, content_length);
-      } else {
-         /*
-          * Increment error count but don't log an error again for the same filesystem.
-          */
-         jcr->acl_ctx->nr_errors++;
-         return bacl_rtn_ok;
-      }
-#endif
-
-#ifdef HAVE_ACL
-   case STREAM_UNIX_ACCESS_ACL:
-   case STREAM_UNIX_DEFAULT_ACL:
-      /*
-       * Handle legacy ACL streams.
-       */
-      if ((jcr->acl_ctx->flags & BACL_FLAG_RESTORE_NATIVE) && os_restore_acl_streams) {
-         return os_restore_acl_streams(jcr, stream, content, content_length);
-      } else {
-         /*
-          * Increment error count but don't log an error again for the same filesystem.
-          */
-         jcr->acl_ctx->nr_errors++;
-         return bacl_rtn_ok;
-      }
-      break;
-   default:
-      if ((jcr->acl_ctx->flags & BACL_FLAG_RESTORE_NATIVE) && os_restore_acl_streams) {
-         /*
-          * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
-          */
-         for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
-            if (os_access_acl_streams[cnt] == stream) {
-               return os_restore_acl_streams(jcr, stream, content, content_length);
-            }
-         }
-         /*
-          * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
-          */
-         for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
-            if (os_default_acl_streams[cnt] == stream) {
-               return os_restore_acl_streams(jcr, stream, content, content_length);
-            }
-         }
-      } else {
-         /*
-          * Increment error count but don't log an error again for the same filesystem.
-          */
-         jcr->acl_ctx->nr_errors++;
-         return bacl_rtn_ok;
-      }
-      break;
-#else
-   default:
-      break;
-#endif
-   }
-   Qmsg2(jcr, M_WARNING, 0, _("Cannot restore ACLs of %s - incompatible acl stream encountered - %d\n"),
-         jcr->last_fname, stream);
-   return bacl_rtn_error;
-}
-#endif
diff --git a/bacula/src/filed/acl.h b/bacula/src/filed/acl.h
deleted file mode 100644 (file)
index 716a7d5..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-   Bacula(R) - The Network Backup Solution
-
-   Copyright (C) 2000-2015 Kern Sibbald
-   Copyright (C) 2004-2014 Free Software Foundation Europe e.V.
-
-   The original author of Bacula is Kern Sibbald, with contributions
-   from many others, a complete list can be found in the file AUTHORS.
-
-   You may use this file and others of this release according to the
-   license defined in the LICENSE file, which includes the Affero General
-   Public License, v3.0 ("AGPLv3") and some additional permissions and
-   terms pursuant to its AGPLv3 Section 7.
-
-   This notice must be preserved when any source code is 
-   conveyed and/or propagated.
-
-   Bacula(R) is a registered trademark of Kern Sibbald.
-*/
-/*
- * Properties we use for getting and setting ACLs.
- */
-
-#ifndef __BACL_H_
-#define __BACL_H_
-
-/* Global JCR data */
-struct acl_ctx_t  {
-   uint32_t nr_errors;
-   uint32_t flags;              /* See BACL_FLAG_* */
-   uint32_t current_dev;
-   uint32_t content_length;
-   POOLMEM *content;
-};
-
-/*
- * We support the following types of ACLs
- */
-typedef enum {
-   BACL_TYPE_NONE        = 0,
-   BACL_TYPE_ACCESS      = 1,
-   BACL_TYPE_DEFAULT     = 2,
-   BACL_TYPE_DEFAULT_DIR = 3,
-   BACL_TYPE_EXTENDED    = 4,
-   BACL_TYPE_NFS4        = 5
-} bacl_type;
-
-#define BACL_FLAG_SAVE_NATIVE     0x01
-#define BACL_FLAG_SAVE_AFS        0x02
-#define BACL_FLAG_RESTORE_NATIVE  0x04
-#define BACL_FLAG_RESTORE_AFS     0x08
-
-/*
- * Ensure we have none
- */
-#ifndef ACL_TYPE_NONE
-#define ACL_TYPE_NONE 0x0
-#endif
-#ifdef HAVE_IRIX_OS
-#define BACL_ENOTSUP  ENOSYS
-#else
-#define BACL_ENOTSUP  EOPNOTSUPP
-#endif /* HAVE_IRIX_OS */
-
-#endif /* __BACL_H_ */
index 9d2b58f1061b4fa60edb92c4fd21b31a5d3a8eee..a036cb6a078bd84fc9ee8bf5cf1629ce3e49642f 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2000-2015 Kern Sibbald
+   Copyright (C) 2000-2016 Kern Sibbald
 
    The original author of Bacula is Kern Sibbald, with contributions
    from many others, a complete list can be found in the file AUTHORS.
    Public License, v3.0 ("AGPLv3") and some additional permissions and
    terms pursuant to its AGPLv3 Section 7.
 
-   This notice must be preserved when any source code is 
+   This notice must be preserved when any source code is
    conveyed and/or propagated.
 
    Bacula(R) is a registered trademark of Kern Sibbald.
  */
 /*
- *  Bacula File Daemon  backup.c  send file attributes and data
+ *  Bacula File Daemon backup.c  send file attributes and data
  *   to the Storage daemon.
  *
  *    Kern Sibbald, March MM
 #include "bacula.h"
 #include "filed.h"
 #include "backup.h"
+
 #ifdef HAVE_LZO
 const bool have_lzo = true;
-#else 
+#else
 const bool have_lzo = false;
-#endif 
+#endif
+
 #ifdef HAVE_LIBZ
 const bool have_libz = true;
-#else 
+#else
 const bool have_libz = false;
-#endif 
+#endif
+
 /* Forward referenced functions */
 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
 static int send_data(bctx_t &bctx, int stream);
@@ -82,7 +82,7 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
    if (client) {
       buf_size = client->max_network_buffer_size;
    } else {
-      buf_size = 0;                   /* use default */
+      buf_size = 0;                  /* use default */
    }
    if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) {
       jcr->setJobStatus(JS_ErrorTerminated);
@@ -99,7 +99,7 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
     *  same output buffer can be used without growing it.
     *
     *  For LZO1X compression the recommended value is :
-    *                  output_block_size = input_block_size + (input_block_size / 16) + 64 + 3 + sizeof(comp_stream_header)
+    *                 output_block_size = input_block_size + (input_block_size / 16) + 64 + 3 + sizeof(comp_stream_header)
     *
     * The zlib compression workset is initialized here to minimize
     *  the "per file" load. The jcr member is only set, if the init
@@ -124,9 +124,9 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
       pZlibStream->state = Z_NULL;
 
       if (deflateInit(pZlibStream, Z_DEFAULT_COMPRESSION) == Z_OK) {
-         jcr->pZLIB_compress_workset = pZlibStream;
+        jcr->pZLIB_compress_workset = pZlibStream;
       } else {
-         free (pZlibStream);
+        free (pZlibStream);
       }
    }
 #endif
@@ -135,9 +135,9 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
    lzo_voidp pLzoMem = (lzo_voidp) malloc(LZO1X_1_MEM_COMPRESS);
    if (pLzoMem) {
       if (lzo_init() == LZO_E_OK) {
-         jcr->LZO_compress_workset = pLzoMem;
+        jcr->LZO_compress_workset = pLzoMem;
       } else {
-         free (pLzoMem);
+        free (pLzoMem);
       }
    }
 #endif
@@ -155,58 +155,36 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
    }
    start_heartbeat_monitor(jcr);
 
-#ifdef HAVE_ACL
-   jcr->acl_ctx = (acl_ctx_t *)malloc(sizeof(acl_ctx_t));
-   memset(jcr->acl_ctx, 0, sizeof(acl_ctx_t));
-   jcr->acl_ctx->content = get_pool_memory(PM_MESSAGE);
-#endif
-#ifdef HAVE_XATTR
-   jcr->xattr_ctx = (xattr_ctx_t *)malloc(sizeof(xattr_ctx_t));
-   memset(jcr->xattr_ctx, 0, sizeof(xattr_ctx_t));
-   jcr->xattr_ctx->content = get_pool_memory(PM_MESSAGE);
-#endif
+   jcr->xacl = (XACL*)new_xacl();
+
    /* Subroutine save_file() is called for each file */
    if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, plugin_save)) {
-      ok = false;                     /* error */
+      ok = false;                    /* error */
       jcr->setJobStatus(JS_ErrorTerminated);
    }
 
-#ifdef HAVE_ACL
-   if (jcr->acl_ctx->nr_errors > 0) {
-      Jmsg(jcr, M_WARNING, 0, _("Had %ld acl errors while doing backup\n"),
-           jcr->acl_ctx->nr_errors);
-   } 
-#endif
-#ifdef HAVE_XATTR
-   if (jcr->xattr_ctx->nr_errors > 0) {
-      Jmsg(jcr, M_WARNING, 0, _("Had %ld xattr errors while doing backup\n"),
-           jcr->xattr_ctx->nr_errors);
-   } 
-#endif
+   if (jcr->xacl->get_acl_nr_errors() > 0) {
+      Jmsg(jcr, M_WARNING, 0, _("Had %ld acl errors while doing backup\n"), jcr->xacl->get_acl_nr_errors());
+   }
+
+   if (jcr->xacl->get_xattr_nr_errors() > 0) {
+      Jmsg(jcr, M_WARNING, 0, _("Had %ld xattr errors while doing backup\n"), jcr->xacl->get_xattr_nr_errors());
+   }
+
    /* Delete or keep snapshots */
    close_snapshot_backup_session(jcr);
    close_vss_backup_session(jcr);
 
-   accurate_finish(jcr);              /* send deleted or base file list to SD */
+   accurate_finish(jcr);             /* send deleted or base file list to SD */
 
    stop_heartbeat_monitor(jcr);
 
-   sd->signal(BNET_EOD);            /* end of sending data */
+   sd->signal(BNET_EOD);           /* end of sending data */
 
-
-#ifdef HAVE_ACL
-   if (jcr->acl_ctx) {
-      free_and_null_pool_memory(jcr->acl_ctx->content);
-      bfree_and_null(jcr->acl_ctx);
-   } 
-#endif
-#ifdef HAVE_XATTR
-   if (jcr->xattr_ctx) {
-      free_and_null_pool_memory(jcr->xattr_ctx->content);
-      bfree_and_null(jcr->xattr_ctx);
-   } 
-#endif
+   if (jcr->xacl) {
+      delete(jcr->xacl);
+      jcr->xacl = NULL;
+   }
    if (jcr->big_buf) {
       bfree_and_null(jcr->big_buf);
    }
@@ -239,8 +217,8 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
  *  Send the file and its data to the Storage daemon.
  *
  *  Returns: 1 if OK
- *           0 if error
- *          -1 to ignore file/directory (not used here)
+ *          0 if error
+ *         -1 to ignore file/directory (not used here)
  */
 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
 {
@@ -250,9 +228,9 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    int stat;
    int rtnstat = 0;
    bool has_file_data = false;
-   struct save_pkt sp;          /* used by option plugin */
+   struct save_pkt sp;         /* used by option plugin */
    BSOCK *sd = jcr->store_bsock;
-   bctx_t bctx;                  /* backup context */
+   bctx_t bctx;                 /* backup context */
 
    memset(&bctx, 0, sizeof(bctx));
    bctx.sd = sd;
@@ -263,10 +241,10 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    time_t now = time(NULL);
    if (jcr->last_stat_time == 0) {
       jcr->last_stat_time = now;
-      jcr->stat_interval = 30;  /* Default 30 seconds */
+      jcr->stat_interval = 30; /* Default 30 seconds */
    } else if (now >= jcr->last_stat_time + jcr->stat_interval) {
       jcr->dir_bsock->fsend("Progress Job=x files=%ld bytes=%lld bps=%ld\n",
-         jcr->JobFiles, jcr->JobBytes, jcr->LastRate);
+        jcr->JobFiles, jcr->JobBytes, jcr->LastRate);
       jcr->last_stat_time = now;
    }
 
@@ -275,10 +253,10 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       return 0;
    }
 
-   jcr->num_files_examined++;         /* bump total file count */
+   jcr->num_files_examined++;        /* bump total file count */
 
    switch (ff_pkt->type) {
-   case FT_LNKSAVED:                  /* Hard linked, file already saved */
+   case FT_LNKSAVED:                 /* Hard linked, file already saved */
       Dmsg2(130, "FT_LNKSAVED hard link: %s => %s\n", ff_pkt->fname, ff_pkt->link);
       break;
    case FT_REGE:
@@ -300,28 +278,28 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       break;
    case FT_DIRBEGIN:
       jcr->num_files_examined--;      /* correct file count */
-      return 1;                       /* not used */
+      return 1;                      /* not used */
    case FT_NORECURSE:
-      Jmsg(jcr, M_INFO, 1, _("     Recursion turned off. Will not descend from %s into %s\n"),
-           ff_pkt->top_fname, ff_pkt->fname);
+      Jmsg(jcr, M_INFO, 1, _("    Recursion turned off. Will not descend from %s into %s\n"),
+          ff_pkt->top_fname, ff_pkt->fname);
       ff_pkt->type = FT_DIREND;       /* Backup only the directory entry */
       break;
    case FT_NOFSCHG:
       /* Suppress message for /dev filesystems */
       if (!is_in_fileset(ff_pkt)) {
-         Jmsg(jcr, M_INFO, 1, _("     %s is a different filesystem. Will not descend from %s into it.\n"),
-              ff_pkt->fname, ff_pkt->top_fname);
+        Jmsg(jcr, M_INFO, 1, _("     %s is a different filesystem. Will not descend from %s into it.\n"),
+             ff_pkt->fname, ff_pkt->top_fname);
       }
       ff_pkt->type = FT_DIREND;       /* Backup only the directory entry */
       break;
    case FT_INVALIDFS:
-      Jmsg(jcr, M_INFO, 1, _("     Disallowed filesystem. Will not descend from %s into %s\n"),
-           ff_pkt->top_fname, ff_pkt->fname);
+      Jmsg(jcr, M_INFO, 1, _("    Disallowed filesystem. Will not descend from %s into %s\n"),
+          ff_pkt->top_fname, ff_pkt->fname);
       ff_pkt->type = FT_DIREND;       /* Backup only the directory entry */
       break;
    case FT_INVALIDDT:
-      Jmsg(jcr, M_INFO, 1, _("     Disallowed drive type. Will not descend into %s\n"),
-           ff_pkt->fname);
+      Jmsg(jcr, M_INFO, 1, _("    Disallowed drive type. Will not descend into %s\n"),
+          ff_pkt->fname);
       break;
    case FT_REPARSE:
    case FT_JUNCTION:
@@ -331,8 +309,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    case FT_SPEC:
       Dmsg1(130, "FT_SPEC saving: %s\n", ff_pkt->fname);
       if (S_ISSOCK(ff_pkt->statp.st_mode)) {
-        Jmsg(jcr, M_SKIPPED, 1, _("     Socket file skipped: %s\n"), ff_pkt->fname);
-        return 1;
+       Jmsg(jcr, M_SKIPPED, 1, _("     Socket file skipped: %s\n"), ff_pkt->fname);
+       return 1;
       }
       break;
    case FT_RAW:
@@ -345,21 +323,21 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    case FT_NOACCESS: {
       berrno be;
       Jmsg(jcr, M_NOTSAVED, 0, _("     Could not access \"%s\": ERR=%s\n"), ff_pkt->fname,
-         be.bstrerror(ff_pkt->ff_errno));
+        be.bstrerror(ff_pkt->ff_errno));
       jcr->JobErrors++;
       return 1;
    }
    case FT_NOFOLLOW: {
       berrno be;
       Jmsg(jcr, M_NOTSAVED, 0, _("     Could not follow link \"%s\": ERR=%s\n"),
-           ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno));
+          ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno));
       jcr->JobErrors++;
       return 1;
    }
    case FT_NOSTAT: {
       berrno be;
       Jmsg(jcr, M_NOTSAVED, 0, _("     Could not stat \"%s\": ERR=%s\n"), ff_pkt->fname,
-         be.bstrerror(ff_pkt->ff_errno));
+        be.bstrerror(ff_pkt->ff_errno));
       jcr->JobErrors++;
       return 1;
    }
@@ -373,7 +351,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    case FT_NOOPEN: {
       berrno be;
       Jmsg(jcr, M_NOTSAVED, 0, _("     Could not open directory \"%s\": ERR=%s\n"),
-           ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno));
+          ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno));
       jcr->JobErrors++;
       return 1;
    }
@@ -381,8 +359,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       Dmsg1(130, "FT_DELETED: %s\n", ff_pkt->fname);
       break;
    default:
-      Jmsg(jcr, M_NOTSAVED, 0,  _("     Unknown file type %d; not saved: %s\n"),
-           ff_pkt->type, ff_pkt->fname);
+      Jmsg(jcr, M_NOTSAVED, 0, _("     Unknown file type %d; not saved: %s\n"),
+          ff_pkt->type, ff_pkt->fname);
       jcr->JobErrors++;
       return 1;
    }
@@ -409,27 +387,27 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       /* ask the option plugin what to do with this file */
       switch (plugin_option_handle_file(jcr, ff_pkt, &sp)) {
       case bRC_OK:
-         Dmsg2(10, "Option plugin %s will be used to backup %s\n",
-               ff_pkt->plugin, ff_pkt->fname);
-         do_plugin_set = true;
-         break;
+        Dmsg2(10, "Option plugin %s will be used to backup %s\n",
+              ff_pkt->plugin, ff_pkt->fname);
+        do_plugin_set = true;
+        break;
       case bRC_Skip:
-         Dmsg2(10, "Option plugin %s decided to skip %s\n",
-               ff_pkt->plugin, ff_pkt->fname);
-         goto good_rtn;
+        Dmsg2(10, "Option plugin %s decided to skip %s\n",
+              ff_pkt->plugin, ff_pkt->fname);
+        goto good_rtn;
       default:
-         Dmsg2(10, "Option plugin %s decided to let bacula handle %s\n",
-               ff_pkt->plugin, ff_pkt->fname);
-         break;
+        Dmsg2(10, "Option plugin %s decided to let bacula handle %s\n",
+              ff_pkt->plugin, ff_pkt->fname);
+        break;
       }
    }
 
    if (do_plugin_set) {
       /* Tell bfile that it needs to call plugin */
       if (!set_cmd_plugin(&ff_pkt->bfd, jcr)) {
-         goto bail_out;
+        goto bail_out;
       }
-      send_plugin_name(jcr, sd, true);      /* signal start of plugin data */
+      send_plugin_name(jcr, sd, true);     /* signal start of plugin data */
       plugin_started = true;
    }
 
@@ -448,7 +426,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    /** Set up the encryption context and send the session data to the SD */
    if (has_file_data && jcr->crypto.pki_encrypt) {
       if (!crypto_session_send(jcr, sd)) {
-         goto bail_out;
+        goto bail_out;
       }
    }
 
@@ -465,8 +443,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       do_read = ff_pkt->statp.st_size > 0;
 #endif
    } else if (ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO ||
-              ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_JUNCTION ||
-         (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) {
+             ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_JUNCTION ||
+        (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) {
       do_read = true;
    }
 
@@ -479,41 +457,41 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       btimer_t *tid;
 
       if (ff_pkt->type == FT_FIFO) {
-         tid = start_thread_timer(jcr, pthread_self(), 60);
+        tid = start_thread_timer(jcr, pthread_self(), 60);
       } else {
-         tid = NULL;
+        tid = NULL;
       }
       int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0;
       ff_pkt->bfd.reparse_point = (ff_pkt->type == FT_REPARSE ||
-                                   ff_pkt->type == FT_JUNCTION);
+                                  ff_pkt->type == FT_JUNCTION);
       set_fattrs(&ff_pkt->bfd, &ff_pkt->statp);
       if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0) < 0) {
-         ff_pkt->ff_errno = errno;
-         berrno be;
-         Jmsg(jcr, M_NOTSAVED, 0, _("     Cannot open \"%s\": ERR=%s.\n"), ff_pkt->fname,
-              be.bstrerror());
-         jcr->JobErrors++;
-         if (tid) {
-            stop_thread_timer(tid);
-            tid = NULL;
-         }
-         goto good_rtn;
+        ff_pkt->ff_errno = errno;
+        berrno be;
+        Jmsg(jcr, M_NOTSAVED, 0, _("     Cannot open \"%s\": ERR=%s.\n"), ff_pkt->fname,
+             be.bstrerror());
+        jcr->JobErrors++;
+        if (tid) {
+           stop_thread_timer(tid);
+           tid = NULL;
+        }
+        goto good_rtn;
       }
       if (tid) {
-         stop_thread_timer(tid);
-         tid = NULL;
+        stop_thread_timer(tid);
+        tid = NULL;
       }
 
       stat = send_data(bctx, bctx.data_stream);
 
       if (ff_pkt->flags & FO_CHKCHANGES) {
-         has_file_changed(jcr, ff_pkt);
+        has_file_changed(jcr, ff_pkt);
       }
 
       bclose(&ff_pkt->bfd);
 
       if (!stat) {
-         goto bail_out;
+        goto bail_out;
       }
    }
 
@@ -524,24 +502,17 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
 #endif
 
    /*
-    * Save ACLs when requested and available for anything not being a symlink
-    * and not being a plugin.
-    */
-#ifdef HAVE_ACL
-   if (!backup_acl_streams(jcr, ff_pkt)) {
-      goto bail_out;
-   }
-#endif
-
-   /*
-    * Save Extended Attributes when requested and available for all files not
-    * being a plugin.
+    * Save ACLs and Extended Attributes when requested and available
+    * for anything not being a symlink and not being a plugin (why not?).
     */
-#ifdef HAVE_XATTR
-   if (!backup_xattr_streams(jcr, ff_pkt)) {
-      goto bail_out;
+   if (jcr->xacl){
+      if (jcr->xacl->backup_acl(jcr, ff_pkt) == bRC_XACL_error) {
+        goto bail_out;
+      }
+      if (jcr->xacl->backup_xattr(jcr, ff_pkt) == bRC_XACL_error) {
+        goto bail_out;
+      }
    }
-#endif
 
    if (!crypto_terminate_digests(bctx)) {
       goto bail_out;
@@ -559,7 +530,7 @@ bail_out:
       send_plugin_name(jcr, sd, false); /* signal end of plugin data */
    }
    if (ff_pkt->opt_plugin) {
-      jcr->plugin_sp = NULL;    /* sp is local to this function */
+      jcr->plugin_sp = NULL;   /* sp is local to this function */
       jcr->plugin_ctx = NULL;
       jcr->plugin = NULL;
       jcr->opt_plugin = false;
@@ -592,9 +563,9 @@ static int send_data(bctx_t &bctx, int stream)
    bctx.fileAddr = 0;
    bctx.cipher_ctx = NULL;
    bctx.msgsave = sd->msg;
-   bctx.rbuf = sd->msg;                    /* read buffer */
-   bctx.wbuf = sd->msg;                    /* write buffer */
-   bctx.cipher_input = (uint8_t *)bctx.rbuf;    /* encrypt uncompressed data */
+   bctx.rbuf = sd->msg;                   /* read buffer */
+   bctx.wbuf = sd->msg;                   /* write buffer */
+   bctx.cipher_input = (uint8_t *)bctx.rbuf;   /* encrypt uncompressed data */
 
    Dmsg1(300, "Saving data, type=%d\n", bctx.ff_pkt->type);
 
@@ -608,13 +579,13 @@ static int send_data(bctx_t &bctx, int stream)
 
    /**
     * Send Data header to Storage daemon
-    *    <file-index> <stream> <expected stream length>
+    *   <file-index> <stream> <expected stream length>
     */
    if (!sd->fsend("%ld %d %lld", jcr->JobFiles, stream,
-        (int64_t)bctx.ff_pkt->statp.st_size)) {
+       (int64_t)bctx.ff_pkt->statp.st_size)) {
       if (!jcr->is_job_canceled()) {
-         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-               sd->bstrerror());
+        Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+              sd->bstrerror());
       }
       goto err;
    }
@@ -622,7 +593,7 @@ static int send_data(bctx_t &bctx, int stream)
 
    /**
     * Make space at beginning of buffer for fileAddr because this
-    *   same buffer will be used for writing if compression is off.
+    *  same buffer will be used for writing if compression is off.
     */
    if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
       bctx.rbuf += OFFSET_FADDR_SIZE;
@@ -644,13 +615,13 @@ static int send_data(bctx_t &bctx, int stream)
    Dmsg1(200, "Fattrs=0X%x\n", bctx.ff_pkt->bfd.fattrs);
    if (bctx.ff_pkt->bfd.fattrs & FILE_ATTRIBUTE_ENCRYPTED) {
       if (!p_ReadEncryptedFileRaw) {
-         Jmsg0(bctx.jcr, M_FATAL, 0, _("Windows Encrypted data not supported on this OS.\n"));
-         goto err;
+        Jmsg0(bctx.jcr, M_FATAL, 0, _("Windows Encrypted data not supported on this OS.\n"));
+        goto err;
       }
       /* This single call reads all EFS data delivers it to a callback */
       if (p_ReadEncryptedFileRaw((PFE_EXPORT_FUNC)read_efs_data_cb, &bctx,
-            bctx.ff_pkt->bfd.pvContext) != 0) {
-         goto err;
+           bctx.ff_pkt->bfd.pvContext) != 0) {
+        goto err;
       }
       /* All read, so skip to finish sending */
       goto finish_sending;
@@ -663,18 +634,18 @@ static int send_data(bctx_t &bctx, int stream)
     */
    while ((sd->msglen=(uint32_t)bread(&bctx.ff_pkt->bfd, bctx.rbuf, bctx.rsize)) > 0) {
       if (!process_and_send_data(bctx)) {
-         goto err;
+        goto err;
       }
    } /* end while read file data */
    goto finish_sending;
 
 finish_sending:
-   if (sd->msglen < 0) {                 /* error */
+   if (sd->msglen < 0) {                /* error */
       berrno be;
       Jmsg(jcr, M_ERROR, 0, _("Read error on file %s. ERR=%s\n"),
-         bctx.ff_pkt->fname, be.bstrerror(bctx.ff_pkt->bfd.berrno));
-      if (jcr->JobErrors++ > 1000) {       /* insanity check */
-         Jmsg(jcr, M_FATAL, 0, _("Too many errors. JobErrors=%d.\n"), jcr->JobErrors);
+        bctx.ff_pkt->fname, be.bstrerror(bctx.ff_pkt->bfd.berrno));
+      if (jcr->JobErrors++ > 1000) {      /* insanity check */
+        Jmsg(jcr, M_FATAL, 0, _("Too many errors. JobErrors=%d.\n"), jcr->JobErrors);
       }
    } else if (bctx.ff_pkt->flags & FO_ENCRYPT) {
       /**
@@ -682,34 +653,34 @@ finish_sending:
        *  buffered data.
        */
       if (!crypto_cipher_finalize(bctx.cipher_ctx, (uint8_t *)jcr->crypto.crypto_buf,
-           &bctx.encrypted_len)) {
-         /* Padding failed. Shouldn't happen. */
-         Jmsg(jcr, M_FATAL, 0, _("Encryption padding error\n"));
-         goto err;
+          &bctx.encrypted_len)) {
+        /* Padding failed. Shouldn't happen. */
+        Jmsg(jcr, M_FATAL, 0, _("Encryption padding error\n"));
+        goto err;
       }
 
       /** Note, on SSL pre-0.9.7, there is always some output */
       if (bctx.encrypted_len > 0) {
-         sd->msglen = bctx.encrypted_len;     /* set encrypted length */
-         sd->msg = jcr->crypto.crypto_buf;    /* set correct write buffer */
-         if (!sd->send()) {
-            if (!jcr->is_job_canceled()) {
-               Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-                     sd->bstrerror());
-            }
-            goto err;
-         }
-         Dmsg1(130, "Send data to SD len=%d\n", sd->msglen);
-         jcr->JobBytes += sd->msglen;     /* count bytes saved possibly compressed/encrypted */
-         sd->msg = bctx.msgsave;          /* restore bnet buffer */
+        sd->msglen = bctx.encrypted_len;     /* set encrypted length */
+        sd->msg = jcr->crypto.crypto_buf;    /* set correct write buffer */
+        if (!sd->send()) {
+           if (!jcr->is_job_canceled()) {
+              Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+                    sd->bstrerror());
+           }
+           goto err;
+        }
+        Dmsg1(130, "Send data to SD len=%d\n", sd->msglen);
+        jcr->JobBytes += sd->msglen;     /* count bytes saved possibly compressed/encrypted */
+        sd->msg = bctx.msgsave;          /* restore bnet buffer */
       }
    }
 
 
    if (!sd->signal(BNET_EOD)) {        /* indicate end of file data */
       if (!jcr->is_job_canceled()) {
-         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-               sd->bstrerror());
+        Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+              sd->bstrerror());
       }
       goto err;
    }
@@ -746,20 +717,20 @@ bool process_and_send_data(bctx_t &bctx)
       ser_declare;
       bool allZeros = false;
       if ((sd->msglen == bctx.rsize &&
-           bctx.fileAddr+sd->msglen < (uint64_t)bctx.ff_pkt->statp.st_size) ||
-          ((bctx.ff_pkt->type == FT_RAW || bctx.ff_pkt->type == FT_FIFO) &&
-            (uint64_t)bctx.ff_pkt->statp.st_size == 0)) {
-         allZeros = is_buf_zero(bctx.rbuf, bctx.rsize);
+          bctx.fileAddr+sd->msglen < (uint64_t)bctx.ff_pkt->statp.st_size) ||
+         ((bctx.ff_pkt->type == FT_RAW || bctx.ff_pkt->type == FT_FIFO) &&
+           (uint64_t)bctx.ff_pkt->statp.st_size == 0)) {
+        allZeros = is_buf_zero(bctx.rbuf, bctx.rsize);
       }
       if (!allZeros) {
-         /** Put file address as first data in buffer */
-         ser_begin(bctx.wbuf, OFFSET_FADDR_SIZE);
-         ser_uint64(bctx.fileAddr);     /* store fileAddr in begin of buffer */
+        /** Put file address as first data in buffer */
+        ser_begin(bctx.wbuf, OFFSET_FADDR_SIZE);
+        ser_uint64(bctx.fileAddr);     /* store fileAddr in begin of buffer */
       }
-      bctx.fileAddr += sd->msglen;      /* update file address */
+      bctx.fileAddr += sd->msglen;     /* update file address */
       /** Skip block of all zeros */
       if (allZeros) {
-         return true;                 /* skip block of zeros */
+        return true;                 /* skip block of zeros */
       }
    } else if (bctx.ff_pkt->flags & FO_OFFSETS) {
       ser_declare;
@@ -767,7 +738,7 @@ bool process_and_send_data(bctx_t &bctx)
       ser_uint64(bctx.ff_pkt->bfd.offset);     /* store offset in begin of buffer */
    }
 
-   jcr->ReadBytes += sd->msglen;         /* count bytes read */
+   jcr->ReadBytes += sd->msglen;        /* count bytes read */
 
    /** Uncompressed cipher input length */
    bctx.cipher_input_len = sd->msglen;
@@ -808,7 +779,7 @@ bool process_and_send_data(bctx_t &bctx)
       ser_declare;
 
       if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
-         bctx.cipher_input_len += OFFSET_FADDR_SIZE;
+        bctx.cipher_input_len += OFFSET_FADDR_SIZE;
       }
 
       /** Encrypt the length of the input block */
@@ -819,26 +790,26 @@ bool process_and_send_data(bctx_t &bctx)
       Dmsg1(20, "Encrypt len=%d\n", bctx.cipher_input_len);
 
       if (!crypto_cipher_update(bctx.cipher_ctx, packet_len, sizeof(packet_len),
-          (uint8_t *)jcr->crypto.crypto_buf, &initial_len)) {
-         /** Encryption failed. Shouldn't happen. */
-         Jmsg(jcr, M_FATAL, 0, _("Encryption error\n"));
-         goto err;
+         (uint8_t *)jcr->crypto.crypto_buf, &initial_len)) {
+        /** Encryption failed. Shouldn't happen. */
+        Jmsg(jcr, M_FATAL, 0, _("Encryption error\n"));
+        goto err;
       }
 
       /** Encrypt the input block */
       if (crypto_cipher_update(bctx.cipher_ctx, bctx.cipher_input, bctx.cipher_input_len,
-          (uint8_t *)&jcr->crypto.crypto_buf[initial_len], &bctx.encrypted_len)) {
-         if ((initial_len + bctx.encrypted_len) == 0) {
-            /** No full block of data available, read more data */
-            return true;
-         }
-         Dmsg2(400, "encrypted len=%d unencrypted len=%d\n", bctx.encrypted_len,
-               sd->msglen);
-         sd->msglen = initial_len + bctx.encrypted_len; /* set encrypted length */
+         (uint8_t *)&jcr->crypto.crypto_buf[initial_len], &bctx.encrypted_len)) {
+        if ((initial_len + bctx.encrypted_len) == 0) {
+           /** No full block of data available, read more data */
+           return true;
+        }
+        Dmsg2(400, "encrypted len=%d unencrypted len=%d\n", bctx.encrypted_len,
+              sd->msglen);
+        sd->msglen = initial_len + bctx.encrypted_len; /* set encrypted length */
       } else {
-         /** Encryption failed. Shouldn't happen. */
-         Jmsg(jcr, M_FATAL, 0, _("Encryption error\n"));
-         goto err;
+        /** Encryption failed. Shouldn't happen. */
+        Jmsg(jcr, M_FATAL, 0, _("Encryption error\n"));
+        goto err;
       }
    }
 
@@ -846,18 +817,18 @@ bool process_and_send_data(bctx_t &bctx)
    if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
       sd->msglen += OFFSET_FADDR_SIZE; /* include fileAddr in size */
    }
-   sd->msg = bctx.wbuf;              /* set correct write buffer */
+   sd->msg = bctx.wbuf;             /* set correct write buffer */
    if (!sd->send()) {
       if (!jcr->is_job_canceled()) {
-         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-               sd->bstrerror());
+        Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+              sd->bstrerror());
       }
       goto err;
    }
    Dmsg1(130, "Send data to SD len=%d\n", sd->msglen);
-   /*          #endif */
+   /*         #endif */
    jcr->JobBytes += sd->msglen;      /* count bytes saved possibly compressed/encrypted */
-   sd->msg = bctx.msgsave;                /* restore read buffer */
+   sd->msg = bctx.msgsave;               /* restore read buffer */
    return true;
 
 err:
@@ -901,7 +872,7 @@ bool encode_and_send_attributes(bctx_t &bctx)
    Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx);
 
    jcr->lock();
-   jcr->JobFiles++;                    /* increment number of files sent */
+   jcr->JobFiles++;                   /* increment number of files sent */
    ff_pkt->FileIndex = jcr->JobFiles;  /* return FileIndex */
    pm_strcpy(jcr->last_fname, ff_pkt->fname);
    jcr->unlock();
@@ -921,12 +892,12 @@ bool encode_and_send_attributes(bctx_t &bctx)
 
    /**
     * Send Attributes header to Storage daemon
-    *    <file-index> <stream> <info>
+    *   <file-index> <stream> <info>
     */
    if (!sd->fsend("%ld %d 0", jcr->JobFiles, attr_stream)) {
       if (!jcr->is_canceled() && !jcr->is_incomplete()) {
-         Jmsg2(jcr, M_FATAL, 0, _("Network send error to SD. Data=%s ERR=%s\n"),
-               sd->msg, sd->bstrerror());
+        Jmsg2(jcr, M_FATAL, 0, _("Network send error to SD. Data=%s ERR=%s\n"),
+              sd->msg, sd->bstrerror());
       }
       return false;
    }
@@ -934,23 +905,23 @@ bool encode_and_send_attributes(bctx_t &bctx)
 
    /**
     * Send file attributes to Storage daemon
-    *   File_index
-    *   File type
-    *   Filename (full path)
-    *   Encoded attributes
-    *   Link name (if type==FT_LNK or FT_LNKSAVED)
-    *   Encoded extended-attributes (for Win32)
+    *  File_index
+    *  File type
+    *  Filename (full path)
+    *  Encoded attributes
+    *  Link name (if type==FT_LNK or FT_LNKSAVED)
+    *  Encoded extended-attributes (for Win32)
     *
     * or send Restore Object to Storage daemon
-    *   File_index
-    *   File_type
-    *   Object_index
-    *   Object_len  (possibly compressed)
-    *   Object_full_len (not compressed)
-    *   Object_compression
-    *   Plugin_name
-    *   Object_name
-    *   Binary Object data
+    *  File_index
+    *  File_type
+    *  Object_index
+    *  Object_len  (possibly compressed)
+    *  Object_full_len (not compressed)
+    *  Object_compression
+    *  Plugin_name
+    *  Object_name
+    *  Binary Object data
     *
     * For a directory, link is the same as fname, but with trailing
     * slash. For a linked file, link is the link.
@@ -963,58 +934,58 @@ bool encode_and_send_attributes(bctx_t &bctx)
    case FT_LNKSAVED:
       Dmsg3(300, "Link %d %s to %s\n", jcr->JobFiles, ff_pkt->fname, ff_pkt->link);
       stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c%u%c", jcr->JobFiles,
-                       ff_pkt->type, ff_pkt->fname, 0, attribs, 0,
-                       ff_pkt->link, 0, attribsEx, 0, ff_pkt->delta_seq, 0);
+                      ff_pkt->type, ff_pkt->fname, 0, attribs, 0,
+                      ff_pkt->link, 0, attribsEx, 0, ff_pkt->delta_seq, 0);
       break;
    case FT_DIREND:
    case FT_REPARSE:
    case FT_JUNCTION:
       /* Here link is the canonical filename (i.e. with trailing slash) */
       stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles,
-                       ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0,
-                       attribsEx, 0, ff_pkt->delta_seq, 0);
+                      ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0,
+                      attribsEx, 0, ff_pkt->delta_seq, 0);
       break;
    case FT_PLUGIN_CONFIG:
    case FT_RESTORE_FIRST:
       comp_len = ff_pkt->object_len;
       ff_pkt->object_compression = 0;
       if (ff_pkt->object_len > 1000) {
-         /* Big object, compress it */
-         comp_len = ff_pkt->object_len + 1000;
-         POOLMEM *comp_obj = get_memory(comp_len);
-         /* *** FIXME *** check Zdeflate error */
-         Zdeflate(ff_pkt->object, ff_pkt->object_len, comp_obj, comp_len);
-         if (comp_len < ff_pkt->object_len) {
-            ff_pkt->object = comp_obj;
-            ff_pkt->object_compression = 1;    /* zlib level 9 compression */
-         } else {
-            /* Uncompressed object smaller, use it */
-            comp_len = ff_pkt->object_len;
-         }
-         Dmsg2(100, "Object compressed from %d to %d bytes\n", ff_pkt->object_len, comp_len);
+        /* Big object, compress it */
+        comp_len = ff_pkt->object_len + 1000;
+        POOLMEM *comp_obj = get_memory(comp_len);
+        /* *** FIXME *** check Zdeflate error */
+        Zdeflate(ff_pkt->object, ff_pkt->object_len, comp_obj, comp_len);
+        if (comp_len < ff_pkt->object_len) {
+           ff_pkt->object = comp_obj;
+           ff_pkt->object_compression = 1;    /* zlib level 9 compression */
+        } else {
+           /* Uncompressed object smaller, use it */
+           comp_len = ff_pkt->object_len;
+        }
+        Dmsg2(100, "Object compressed from %d to %d bytes\n", ff_pkt->object_len, comp_len);
       }
       sd->msglen = Mmsg(sd->msg, "%d %d %d %d %d %d %s%c%s%c",
-                        jcr->JobFiles, ff_pkt->type, ff_pkt->object_index,
-                        comp_len, ff_pkt->object_len, ff_pkt->object_compression,
-                        ff_pkt->fname, 0, ff_pkt->object_name, 0);
+                       jcr->JobFiles, ff_pkt->type, ff_pkt->object_index,
+                       comp_len, ff_pkt->object_len, ff_pkt->object_compression,
+                       ff_pkt->fname, 0, ff_pkt->object_name, 0);
       sd->msg = check_pool_memory_size(sd->msg, sd->msglen + comp_len + 2);
       memcpy(sd->msg + sd->msglen, ff_pkt->object, comp_len);
       /* Note we send one extra byte so Dir can store zero after object */
       sd->msglen += comp_len + 1;
       stat = sd->send();
       if (ff_pkt->object_compression) {
-         free_and_null_pool_memory(ff_pkt->object);
+        free_and_null_pool_memory(ff_pkt->object);
       }
       break;
    case FT_REG:
       stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%d%c", jcr->JobFiles,
-               ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0,
-               ff_pkt->delta_seq, 0);
+              ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0,
+              ff_pkt->delta_seq, 0);
       break;
    default:
       stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles,
-                       ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0,
-                       attribsEx, 0, ff_pkt->delta_seq, 0);
+                      ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0,
+                      attribsEx, 0, ff_pkt->delta_seq, 0);
       break;
    }
 
@@ -1025,9 +996,9 @@ bool encode_and_send_attributes(bctx_t &bctx)
    Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
    if (!stat && !jcr->is_job_canceled()) {
       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
+           sd->bstrerror());
    }
-   sd->signal(BNET_EOD);            /* indicate end of attributes data */
+   sd->signal(BNET_EOD);           /* indicate end of attributes data */
    return stat;
 }
 
@@ -1047,13 +1018,13 @@ static bool setup_compression(bctx_t &bctx)
 
    if ((bctx.ff_pkt->flags & FO_COMPRESS) && bctx.ff_pkt->Compress_algo == COMPRESS_GZIP) {
       if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
-         bctx.cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE;
-         bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE;
+        bctx.cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE;
+        bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE;
       } else {
-         bctx.cbuf = (Bytef *)jcr->compress_buf;
-         bctx.max_compress_len = jcr->compress_buf_size; /* set max length */
+        bctx.cbuf = (Bytef *)jcr->compress_buf;
+        bctx.max_compress_len = jcr->compress_buf_size; /* set max length */
       }
-      bctx.wbuf = jcr->compress_buf;    /* compressed output here */
+      bctx.wbuf = jcr->compress_buf;   /* compressed output here */
       bctx.cipher_input = (uint8_t *)jcr->compress_buf; /* encrypt compressed data */
 
       /**
@@ -1063,13 +1034,13 @@ static bool setup_compression(bctx_t &bctx)
        */
 
       if (((z_stream*)jcr->pZLIB_compress_workset)->total_in == 0) {
-         /** set gzip compression level - must be done per file */
-         if ((zstat=deflateParams((z_stream*)jcr->pZLIB_compress_workset,
-              bctx.ff_pkt->Compress_level, Z_DEFAULT_STRATEGY)) != Z_OK) {
-            Jmsg(jcr, M_FATAL, 0, _("Compression deflateParams error: %d\n"), zstat);
-            jcr->setJobStatus(JS_ErrorTerminated);
-            return false;
-         }
+        /** set gzip compression level - must be done per file */
+        if ((zstat=deflateParams((z_stream*)jcr->pZLIB_compress_workset,
+             bctx.ff_pkt->Compress_level, Z_DEFAULT_STRATEGY)) != Z_OK) {
+           Jmsg(jcr, M_FATAL, 0, _("Compression deflateParams error: %d\n"), zstat);
+           jcr->setJobStatus(JS_ErrorTerminated);
+           return false;
+        }
       }
    }
  #endif
@@ -1079,17 +1050,17 @@ static bool setup_compression(bctx_t &bctx)
 
    if ((bctx.ff_pkt->flags & FO_COMPRESS) && bctx.ff_pkt->Compress_algo == COMPRESS_LZO1X) {
       if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
-         bctx.cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE;
-         bctx.cbuf2 = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE + sizeof(comp_stream_header);
-         bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE;
+        bctx.cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE;
+        bctx.cbuf2 = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE + sizeof(comp_stream_header);
+        bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE;
       } else {
-         bctx.cbuf = (Bytef *)jcr->compress_buf;
-         bctx.cbuf2 = (Bytef *)jcr->compress_buf + sizeof(comp_stream_header);
-         bctx.max_compress_len = jcr->compress_buf_size; /* set max length */
+        bctx.cbuf = (Bytef *)jcr->compress_buf;
+        bctx.cbuf2 = (Bytef *)jcr->compress_buf + sizeof(comp_stream_header);
+        bctx.max_compress_len = jcr->compress_buf_size; /* set max length */
       }
       bctx.ch.magic = COMPRESS_LZO1X;
       bctx.ch.version = COMP_HEAD_VERSION;
-      bctx.wbuf = jcr->compress_buf;    /* compressed output here */
+      bctx.wbuf = jcr->compress_buf;   /* compressed output here */
       bctx.cipher_input = (uint8_t *)jcr->compress_buf; /* encrypt compressed data */
    }
  #endif
@@ -1114,32 +1085,32 @@ static bool send_resource_fork(bctx_t &bctx)
    if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) &&
        ff_pkt->flags & FO_HFSPLUS)) {
       if (ff_pkt->hfsinfo.rsrclength > 0) {
-         int flags;
-         int rsrc_stream;
-         if (bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
-            ff_pkt->ff_errno = errno;
-            berrno be;
-            Jmsg(jcr, M_NOTSAVED, -1, _("     Cannot open resource fork for \"%s\": ERR=%s.\n"),
-                 ff_pkt->fname, be.bstrerror());
-            jcr->JobErrors++;
-            if (is_bopen(&ff_pkt->bfd)) {
-               bclose(&ff_pkt->bfd);
-            }
-            return true;
-         }
-         flags = ff_pkt->flags;
-         ff_pkt->flags &= ~(FO_COMPRESS|FO_SPARSE|FO_OFFSETS);
-         if (flags & FO_ENCRYPT) {
-            rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA;
-         } else {
-            rsrc_stream = STREAM_MACOS_FORK_DATA;
-         }
-         stat = send_data(bctx, rsrc_stream);
-         ff_pkt->flags = flags;
-         bclose(&ff_pkt->bfd);
-         if (!stat) {
-            return false;
-         }
+        int flags;
+        int rsrc_stream;
+        if (bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
+           ff_pkt->ff_errno = errno;
+           berrno be;
+           Jmsg(jcr, M_NOTSAVED, -1, _("     Cannot open resource fork for \"%s\": ERR=%s.\n"),
+                ff_pkt->fname, be.bstrerror());
+           jcr->JobErrors++;
+           if (is_bopen(&ff_pkt->bfd)) {
+              bclose(&ff_pkt->bfd);
+           }
+           return true;
+        }
+        flags = ff_pkt->flags;
+        ff_pkt->flags &= ~(FO_COMPRESS|FO_SPARSE|FO_OFFSETS);
+        if (flags & FO_ENCRYPT) {
+           rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA;
+        } else {
+           rsrc_stream = STREAM_MACOS_FORK_DATA;
+        }
+        stat = send_data(bctx, rsrc_stream);
+        ff_pkt->flags = flags;
+        bclose(&ff_pkt->bfd);
+        if (!stat) {
+           return false;
+        }
       }
 
       Dmsg1(300, "Saving Finder Info for \"%s\"\n", ff_pkt->fname);
@@ -1148,10 +1119,10 @@ static bool send_resource_fork(bctx_t &bctx)
       pm_memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32);
       sd->msglen = 32;
       if (bctx.digest) {
-         crypto_digest_update(bctx.digest, (uint8_t *)sd->msg, sd->msglen);
+        crypto_digest_update(bctx.digest, (uint8_t *)sd->msg, sd->msglen);
       }
       if (bctx.signing_digest) {
-         crypto_digest_update(bctx.signing_digest, (uint8_t *)sd->msg, sd->msglen);
+        crypto_digest_update(bctx.signing_digest, (uint8_t *)sd->msg, sd->msglen);
       }
       sd->send();
       sd->signal(BNET_EOD);
@@ -1172,27 +1143,27 @@ static bool do_libz_compression(bctx_t &bctx)
       Dmsg3(400, "cbuf=0x%x rbuf=0x%x len=%u\n", bctx.cbuf, bctx.rbuf, sd->msglen);
 
       ((z_stream*)jcr->pZLIB_compress_workset)->next_in   = (Bytef *)bctx.rbuf;
-             ((z_stream*)jcr->pZLIB_compress_workset)->avail_in  = sd->msglen;
+            ((z_stream*)jcr->pZLIB_compress_workset)->avail_in  = sd->msglen;
       ((z_stream*)jcr->pZLIB_compress_workset)->next_out  = bctx.cbuf;
-             ((z_stream*)jcr->pZLIB_compress_workset)->avail_out = bctx.max_compress_len;
+            ((z_stream*)jcr->pZLIB_compress_workset)->avail_out = bctx.max_compress_len;
 
       if ((zstat=deflate((z_stream*)jcr->pZLIB_compress_workset, Z_FINISH)) != Z_STREAM_END) {
-         Jmsg(jcr, M_FATAL, 0, _("Compression deflate error: %d\n"), zstat);
-         jcr->setJobStatus(JS_ErrorTerminated);
-         return false;
+        Jmsg(jcr, M_FATAL, 0, _("Compression deflate error: %d\n"), zstat);
+        jcr->setJobStatus(JS_ErrorTerminated);
+        return false;
       }
       bctx.compress_len = ((z_stream*)jcr->pZLIB_compress_workset)->total_out;
       /** reset zlib stream to be able to begin from scratch again */
       if ((zstat=deflateReset((z_stream*)jcr->pZLIB_compress_workset)) != Z_OK) {
-         Jmsg(jcr, M_FATAL, 0, _("Compression deflateReset error: %d\n"), zstat);
-         jcr->setJobStatus(JS_ErrorTerminated);
-         return false;
+        Jmsg(jcr, M_FATAL, 0, _("Compression deflateReset error: %d\n"), zstat);
+        jcr->setJobStatus(JS_ErrorTerminated);
+        return false;
       }
 
       Dmsg2(400, "GZIP compressed len=%d uncompressed len=%d\n", bctx.compress_len,
-            sd->msglen);
+           sd->msglen);
 
-      sd->msglen = bctx.compress_len;      /* set compressed length */
+      sd->msglen = bctx.compress_len;     /* set compressed length */
       bctx.cipher_input_len = bctx.compress_len;
    }
 #endif
@@ -1208,7 +1179,7 @@ static bool do_lzo_compression(bctx_t &bctx)
 
    /** Do compression if turned on */
    if (bctx.ff_pkt->flags & FO_COMPRESS && bctx.ff_pkt->Compress_algo == COMPRESS_LZO1X && jcr->LZO_compress_workset) {
-      lzo_uint len;          /* TODO: See with the latest patch how to handle lzo_uint with 64bit */
+      lzo_uint len;         /* TODO: See with the latest patch how to handle lzo_uint with 64bit */
 
       ser_declare;
       ser_begin(bctx.cbuf, sizeof(comp_stream_header));
@@ -1216,26 +1187,26 @@ static bool do_lzo_compression(bctx_t &bctx)
       Dmsg3(400, "cbuf=0x%x rbuf=0x%x len=%u\n", bctx.cbuf, bctx.rbuf, sd->msglen);
 
       lzores = lzo1x_1_compress((const unsigned char*)bctx.rbuf, sd->msglen, bctx.cbuf2,
-                                &len, jcr->LZO_compress_workset);
+                               &len, jcr->LZO_compress_workset);
       bctx.compress_len = len;
       if (lzores == LZO_E_OK && bctx.compress_len <= bctx.max_compress_len) {
-         /* complete header */
-         ser_uint32(COMPRESS_LZO1X);
-         ser_uint32(bctx.compress_len);
-         ser_uint16(bctx.ch.level);
-         ser_uint16(bctx.ch.version);
+        /* complete header */
+        ser_uint32(COMPRESS_LZO1X);
+        ser_uint32(bctx.compress_len);
+        ser_uint16(bctx.ch.level);
+        ser_uint16(bctx.ch.version);
       } else {
-         /** this should NEVER happen */
-         Jmsg(jcr, M_FATAL, 0, _("Compression LZO error: %d\n"), lzores);
-         jcr->setJobStatus(JS_ErrorTerminated);
-         return false;
+        /** this should NEVER happen */
+        Jmsg(jcr, M_FATAL, 0, _("Compression LZO error: %d\n"), lzores);
+        jcr->setJobStatus(JS_ErrorTerminated);
+        return false;
       }
 
       Dmsg2(400, "LZO compressed len=%d uncompressed len=%d\n", bctx.compress_len,
-            sd->msglen);
+           sd->msglen);
 
       bctx.compress_len += sizeof(comp_stream_header); /* add size of header */
-      sd->msglen = bctx.compress_len;      /* set compressed length */
+      sd->msglen = bctx.compress_len;     /* set compressed length */
       bctx.cipher_input_len = bctx.compress_len;
    }
 #endif
@@ -1258,13 +1229,13 @@ static bool do_snap_strip(FF_PKT *ff)
       last = MAX(last - 1, 0);
 
       if (ff->snap_fname[last] == '/') {
-         if (ff->fname[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */
-            ff->snap_fname[last] = 0;
-         }
+        if (ff->fname[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */
+           ff->snap_fname[last] = 0;
+        }
       } else {
-         if (ff->fname[sp_first] != '/') {
-            pm_strcat(ff->snap_fname, "/");
-         }
+        if (ff->fname[sp_first] != '/') {
+           pm_strcat(ff->snap_fname, "/");
+        }
       }
 
       pm_strcat(ff->snap_fname, ff->fname + sp_first);
@@ -1277,13 +1248,13 @@ static bool do_snap_strip(FF_PKT *ff)
       last = MAX(last - 1, 0);
 
       if (ff->snap_fname[last] == '/') {
-         if (ff->link[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */
-            ff->snap_fname[last] = 0;
-         }
+        if (ff->link[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */
+           ff->snap_fname[last] = 0;
+        }
       } else {
-         if (ff->link[sp_first] != '/') {
-            pm_strcat(ff->snap_fname, "/");
-         }
+        if (ff->link[sp_first] != '/') {
+           pm_strcat(ff->snap_fname, "/");
+        }
       }
 
       pm_strcat(ff->snap_fname, ff->link + sp_first);
@@ -1308,29 +1279,29 @@ static bool do_strip(int count, char *in)
    while (*in && !IsPathSeparator(*in)) {
       out++; in++;
    }
-   if (*in) {                    /* Not at the end of the string */
+   if (*in) {                   /* Not at the end of the string */
       out++; in++;
-      numsep++;                  /* one separator seen */
+      numsep++;                 /* one separator seen */
    }
    for (stripped=0; stripped<count && *in; stripped++) {
       while (*in && !IsPathSeparator(*in)) {
-         in++;                   /* skip chars */
+        in++;                   /* skip chars */
       }
       if (*in) {
-         numsep++;               /* count separators seen */
-         in++;                   /* skip separator */
+        numsep++;               /* count separators seen */
+        in++;                   /* skip separator */
       }
    }
    /* Copy to end */
-   while (*in) {                /* copy to end */
+   while (*in) {               /* copy to end */
       if (IsPathSeparator(*in)) {
-         numsep++;
+        numsep++;
       }
       *out++ = *in++;
    }
    *out = 0;
    Dmsg4(500, "stripped=%d count=%d numsep=%d sep>count=%d\n",
-         stripped, count, numsep, numsep>count);
+        stripped, count, numsep, numsep>count);
    return stripped==count && numsep>count;
 }
 
@@ -1346,7 +1317,7 @@ static bool do_strip(int count, char *in)
  */
 void strip_path(FF_PKT *ff_pkt)
 {
-   if (!ff_pkt->strip_snap_path        && 
+   if (!ff_pkt->strip_snap_path        &&
        (!(ff_pkt->flags & FO_STRIPPATH) || ff_pkt->strip_path <= 0))
    {
       Dmsg1(200, "No strip for %s\n", ff_pkt->fname);
@@ -1362,15 +1333,15 @@ void strip_path(FF_PKT *ff_pkt)
    if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) {
       pm_strcpy(ff_pkt->link_save, ff_pkt->link);
       Dmsg2(500, "strcpy link_save=%d link=%d\n", strlen(ff_pkt->link_save),
-         strlen(ff_pkt->link));
+        strlen(ff_pkt->link));
       Dsm_check(200);
-   } 
+   }
 
    if (ff_pkt->strip_snap_path) {
       if (!do_snap_strip(ff_pkt)) {
-         Dmsg1(0, "Something wrong with do_snap_strip(%s)\n", ff_pkt->fname);
-         unstrip_path(ff_pkt);
-         goto rtn;
+        Dmsg1(0, "Something wrong with do_snap_strip(%s)\n", ff_pkt->fname);
+        unstrip_path(ff_pkt);
+        goto rtn;
       }
    }
 
@@ -1394,7 +1365,7 @@ void strip_path(FF_PKT *ff_pkt)
    /** Strip links but not symlinks */
    if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) {
       if (!do_strip(ff_pkt->strip_path, ff_pkt->link)) {
-         unstrip_path(ff_pkt);
+        unstrip_path(ff_pkt);
       }
    }
 
@@ -1410,14 +1381,14 @@ void unstrip_path(FF_PKT *ff_pkt)
    {
       return;
    }
-   
+
    strcpy(ff_pkt->fname, ff_pkt->fname_save);
    if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) {
       Dmsg2(10, "strcpy link=%s link_save=%s\n", ff_pkt->link,
-          ff_pkt->link_save);
+         ff_pkt->link_save);
       strcpy(ff_pkt->link, ff_pkt->link_save);
       Dmsg2(10, "strcpy link=%d link_save=%d\n", strlen(ff_pkt->link),
-          strlen(ff_pkt->link_save));
+         strlen(ff_pkt->link_save));
       Dsm_check(200);
    }
 }
@@ -1429,28 +1400,28 @@ static void close_vss_backup_session(JCR *jcr)
    /* tell vss to close the backup session */
    if (jcr->Snapshot) {
       if (g_pVSSClient->CloseBackup()) {
-         /* inform user about writer states */
-         for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) {
-            int msg_type = M_INFO;
-            if (g_pVSSClient->GetWriterState(i) < 1) {
-               msg_type = M_WARNING;
-               jcr->JobErrors++;
-            }
-            Jmsg(jcr, msg_type, 0, _("VSS Writer (BackupComplete): %s\n"), g_pVSSClient->GetWriterInfo(i));
-         }
+        /* inform user about writer states */
+        for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) {
+           int msg_type = M_INFO;
+           if (g_pVSSClient->GetWriterState(i) < 1) {
+              msg_type = M_WARNING;
+              jcr->JobErrors++;
+           }
+           Jmsg(jcr, msg_type, 0, _("VSS Writer (BackupComplete): %s\n"), g_pVSSClient->GetWriterInfo(i));
+        }
       }
       /* Generate Job global writer metadata */
       WCHAR *metadata = g_pVSSClient->GetMetadata();
       if (metadata) {
-         FF_PKT *ff_pkt = jcr->ff;
-         ff_pkt->fname = (char *)"*all*"; /* for all plugins */
-         ff_pkt->type = FT_RESTORE_FIRST;
-         ff_pkt->LinkFI = 0;
-         ff_pkt->object_name = (char *)"job_metadata.xml";
-         ff_pkt->object = (char *)metadata;
-         ff_pkt->object_len = (wcslen(metadata) + 1) * sizeof(WCHAR);
-         ff_pkt->object_index = (int)time(NULL);
-         save_file(jcr, ff_pkt, true);
+        FF_PKT *ff_pkt = jcr->ff;
+        ff_pkt->fname = (char *)"*all*"; /* for all plugins */
+        ff_pkt->type = FT_RESTORE_FIRST;
+        ff_pkt->LinkFI = 0;
+        ff_pkt->object_name = (char *)"job_metadata.xml";
+        ff_pkt->object = (char *)metadata;
+        ff_pkt->object_len = (wcslen(metadata) + 1) * sizeof(WCHAR);
+        ff_pkt->object_index = (int)time(NULL);
+        save_file(jcr, ff_pkt, true);
      }
    }
 #endif
index cc156939da011395db6fdca5bf8b051132f6f9b4..735021e16e819d5847e08fc3fb2eddd553a0916f 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2000-2015 Kern Sibbald
+   Copyright (C) 2000-2016 Kern Sibbald
 
    The original author of Bacula is Kern Sibbald, with contributions
    from many others, a complete list can be found in the file AUTHORS.
@@ -11,7 +11,7 @@
    Public License, v3.0 ("AGPLv3") and some additional permissions and
    terms pursuant to its AGPLv3 Section 7.
 
-   This notice must be preserved when any source code is 
+   This notice must be preserved when any source code is
    conveyed and/or propagated.
 
    Bacula(R) is a registered trademark of Kern Sibbald.
  */
 
 //#define TEST_WORKER
-#ifdef  TEST_WORKER
+#ifdef TEST_WORKER
 #define ERROR_BUFFER_OVERFLOW 1
 #define ERROR_SUCCESS 0
 #endif
+
 /* acl errors to report per job. */
 #define ACL_MAX_ERROR_PRINT_PER_JOB   25
+
 /* xattr errors to report per job. */
 #define XATTR_MAX_ERROR_PRINT_PER_JOB 25
-/* Return values from acl subroutines. */
-enum bacl_rtn_code {
-   bacl_rtn_fatal = -1,
-   bacl_rtn_error =  0,
-   bacl_rtn_ok    =  1
-};
-/* Return values from xattr subroutines. */
-enum bxattr_rtn_code {
-   bxattr_rtn_fatal = -1,
-   bxattr_rtn_error =  0,
-   bxattr_rtn_ok    =  1
-};
 
 #define FILE_DAEMON 1
 #include  "lib/htable.h"
@@ -55,14 +41,13 @@ enum bxattr_rtn_code {
 #include  "fd_plugins.h"
 #include  "fd_snapshot.h"
 #include  "findlib/find.h"
-#include  "acl.h"
-#include  "xattr.h"
+#include  "xacl.h"
 #include  "jcr.h"
-#include  "protos.h"                   /* file daemon prototypes */
+#include  "protos.h"                  /* file daemon prototypes */
 #include  "lib/runscript.h"
 #include  "lib/breg.h"
 #ifdef HAVE_LIBZ
-#include <zlib.h>                     /* compression headers */
+#include <zlib.h>                    /* compression headers */
 #else
 #define uLongf uint32_t
 #endif
@@ -71,8 +56,8 @@ enum bxattr_rtn_code {
 #include <lzo/lzo1x.h>
 #endif
 
-extern CLIENT *me;                    /* "Global" Client resource */
-extern bool win32decomp;              /* Use decomposition of BackupRead data */
+extern CLIENT *me;                   /* "Global" Client resource */
+extern bool win32decomp;             /* Use decomposition of BackupRead data */
 extern bool no_win32_write_errors;    /* Ignore certain errors */
 
 void terminate_filed(int sig);
index 39160becb50554d37f81baa54176ef577615aea7..155432bde3d5e89c3f62f52fcd969eda061246dd 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2000-2015 Kern Sibbald
+   Copyright (C) 2000-2016 Kern Sibbald
 
    The original author of Bacula is Kern Sibbald, with contributions
    from many others, a complete list can be found in the file AUTHORS.
@@ -11,7 +11,7 @@
    Public License, v3.0 ("AGPLv3") and some additional permissions and
    terms pursuant to its AGPLv3 Section 7.
 
-   This notice must be preserved when any source code is 
+   This notice must be preserved when any source code is
    conveyed and/or propagated.
 
    Bacula(R) is a registered trademark of Kern Sibbald.
@@ -52,11 +52,6 @@ void stop_heartbeat_monitor(JCR *jcr);
 void start_dir_heartbeat(JCR *jcr);
 void stop_dir_heartbeat(JCR *jcr);
 
-/* From acl.c */
-bool backup_acl_streams(JCR *jcr, FF_PKT *ff_pkt);
-bacl_rtn_code restore_acl_streams(JCR *jcr, int stream, char *content, 
-   uint32_t content_length);
-
 /* from accurate.c */
 bool accurate_finish(JCR *jcr);
 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt);
@@ -67,11 +62,6 @@ void accurate_free(JCR *jcr);
 void strip_path(FF_PKT *ff_pkt);
 void unstrip_path(FF_PKT *ff_pkt);
 
-/* from xattr.c */
-bool backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt);
-bxattr_rtn_code restore_xattr_streams(JCR *jcr, int stream, 
-   char *content, uint32_t content_length);
-
 /* from job.c */
 findINCEXE *new_exclude(JCR *jcr);
 findINCEXE *new_preinclude(JCR *jcr);
index b6c2819e8542114f4d5a6f12caa6ecd78b851a2a..20ef9abf227926944dee51b6e2b6aa79f38a65fb 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2000-2015 Kern Sibbald
+   Copyright (C) 2000-2016 Kern Sibbald
    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
 
    The original author of Bacula is Kern Sibbald, with contributions
@@ -12,7 +12,7 @@
    Public License, v3.0 ("AGPLv3") and some additional permissions and
    terms pursuant to its AGPLv3 Section 7.
 
-   This notice must be preserved when any source code is 
+   This notice must be preserved when any source code is
    conveyed and/or propagated.
 
    Bacula(R) is a registered trademark of Kern Sibbald.
@@ -141,7 +141,7 @@ static bool restore_finderinfo(JCR *jcr, POOLMEM *buf, int32_t buflen)
 
 /*
  * Cleanup of delayed restore stack with streams for later processing.
- */ 
+ */
 static void drop_delayed_restore_streams(r_ctx &rctx, bool reuse)
 {
    RESTORE_DATA_STREAM *rds;
@@ -197,23 +197,22 @@ static inline void push_delayed_restore_stream(r_ctx &rctx, char *msg, int msgle
  * This can either be a delayed restore or direct restore.
  */
 static inline bool do_restore_acl(JCR *jcr, int stream, char *content,
-                                  uint32_t content_length) 
+                                  uint32_t content_length)
 {
-   switch (restore_acl_streams(jcr, stream, content, content_length)) {
-   case bacl_rtn_fatal:
-      return false;
-   case bacl_rtn_error:
-      /*
-       * Non-fatal errors, count them and when the number is under ACL_MAX_ERROR_PRINT_PER_JOB
-       * print the error message set by the lower level routine in jcr->errmsg.
-       */
-      if (jcr->acl_ctx->nr_errors < ACL_MAX_ERROR_PRINT_PER_JOB) {
-         Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
-      }
-      jcr->acl_ctx->nr_errors++;
-      break;
-   case bacl_rtn_ok:
-      break;
+   switch (jcr->xacl->restore_acl(jcr, stream, content, content_length)) {
+      case bRC_XACL_fatal:
+         return false;
+      case bRC_XACL_error:
+         /*
+          * Non-fatal errors, count them and when the number is under ACL_MAX_ERROR_PRINT_PER_JOB
+          * print the error message set by the lower level routine in jcr->errmsg.
+          */
+         if (jcr->xacl->get_acl_nr_errors() < ACL_MAX_ERROR_PRINT_PER_JOB) {
+            Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+         }
+         break;
+      default:
+         break;
    }
    return true;
 }
@@ -223,23 +222,22 @@ static inline bool do_restore_acl(JCR *jcr, int stream, char *content,
  * This can either be a delayed restore or direct restore.
  */
 static inline bool do_restore_xattr(JCR *jcr, int stream, char *content,
-                                    uint32_t content_length) 
+                                    uint32_t content_length)
 {
-   switch (restore_xattr_streams(jcr, stream, content, content_length)) {
-   case bxattr_rtn_fatal:
-      return false;
-   case bxattr_rtn_error:
-      /*
-       * Non-fatal errors, count them and when the number is under XATTR_MAX_ERROR_PRINT_PER_JOB
-       * print the error message set by the lower level routine in jcr->errmsg.
-       */
-      if (jcr->xattr_ctx->nr_errors < XATTR_MAX_ERROR_PRINT_PER_JOB) {
-         Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
-      }
-      jcr->xattr_ctx->nr_errors++;
-      break;
-   case bxattr_rtn_ok:
-      break;
+   switch (jcr->xacl->restore_xattr(jcr, stream, content, content_length)) {
+      case bRC_XACL_fatal:
+         return false;
+      case bRC_XACL_error:
+         /*
+          * Non-fatal errors, count them and when the number is under XATTR_MAX_ERROR_PRINT_PER_JOB
+          * print the error message set by the lower level routine in jcr->errmsg.
+          */
+         if (jcr->xacl->get_xattr_nr_errors() < XATTR_MAX_ERROR_PRINT_PER_JOB) {
+            Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+         }
+         break;
+      default:
+         break;
    }
    return true;
 }
@@ -279,40 +277,40 @@ static inline bool pop_delayed_data_streams(r_ctx &rctx)
       switch (rds->stream) {
       case STREAM_UNIX_ACCESS_ACL:
       case STREAM_UNIX_DEFAULT_ACL:
-      case STREAM_ACL_AIX_TEXT:
-      case STREAM_ACL_DARWIN_ACCESS:
-      case STREAM_ACL_FREEBSD_DEFAULT:
-      case STREAM_ACL_FREEBSD_ACCESS:
-      case STREAM_ACL_HPUX_ACL_ENTRY:
-      case STREAM_ACL_IRIX_DEFAULT:
-      case STREAM_ACL_IRIX_ACCESS:
-      case STREAM_ACL_LINUX_DEFAULT:
-      case STREAM_ACL_LINUX_ACCESS:
-      case STREAM_ACL_TRU64_DEFAULT:
-      case STREAM_ACL_TRU64_DEFAULT_DIR:
-      case STREAM_ACL_TRU64_ACCESS:
-      case STREAM_ACL_SOLARIS_POSIX:
-      case STREAM_ACL_SOLARIS_NFS4:
-      case STREAM_ACL_AFS_TEXT:
-      case STREAM_ACL_AIX_AIXC:
-      case STREAM_ACL_AIX_NFS4:
-      case STREAM_ACL_FREEBSD_NFS4:
-      case STREAM_ACL_HURD_DEFAULT:
-      case STREAM_ACL_HURD_ACCESS:
+      case STREAM_XACL_AIX_TEXT:
+      case STREAM_XACL_DARWIN_ACCESS:
+      case STREAM_XACL_FREEBSD_DEFAULT:
+      case STREAM_XACL_FREEBSD_ACCESS:
+      case STREAM_XACL_HPUX_ACL_ENTRY:
+      case STREAM_XACL_IRIX_DEFAULT:
+      case STREAM_XACL_IRIX_ACCESS:
+      case STREAM_XACL_LINUX_DEFAULT:
+      case STREAM_XACL_LINUX_ACCESS:
+      case STREAM_XACL_TRU64_DEFAULT:
+      case STREAM_XACL_TRU64_DEFAULT_DIR:
+      case STREAM_XACL_TRU64_ACCESS:
+      case STREAM_XACL_SOLARIS_POSIX:
+      case STREAM_XACL_SOLARIS_NFS4:
+      case STREAM_XACL_AFS_TEXT:
+      case STREAM_XACL_AIX_AIXC:
+      case STREAM_XACL_AIX_NFS4:
+      case STREAM_XACL_FREEBSD_NFS4:
+      case STREAM_XACL_HURD_DEFAULT:
+      case STREAM_XACL_HURD_ACCESS:
          if (!do_restore_acl(jcr, rds->stream, rds->content, rds->content_length)) {
             goto get_out;
          }
          break;
-      case STREAM_XATTR_HURD:
-      case STREAM_XATTR_IRIX:
-      case STREAM_XATTR_TRU64:
-      case STREAM_XATTR_AIX:
-      case STREAM_XATTR_OPENBSD:
-      case STREAM_XATTR_SOLARIS_SYS:
-      case STREAM_XATTR_DARWIN:
-      case STREAM_XATTR_FREEBSD:
-      case STREAM_XATTR_LINUX:
-      case STREAM_XATTR_NETBSD:
+      case STREAM_XACL_HURD_XATTR:
+      case STREAM_XACL_IRIX_XATTR:
+      case STREAM_XACL_TRU64_XATTR:
+      case STREAM_XACL_AIX_XATTR:
+      case STREAM_XACL_OPENBSD_XATTR:
+      case STREAM_XACL_SOLARIS_SYS_XATTR:
+      case STREAM_XACL_DARWIN_XATTR:
+      case STREAM_XACL_FREEBSD_XATTR:
+      case STREAM_XACL_LINUX_XATTR:
+      case STREAM_XACL_NETBSD_XATTR:
          if (!do_restore_xattr(jcr, rds->stream, rds->content, rds->content_length)) {
             goto get_out;
          }
@@ -327,7 +325,7 @@ static inline bool pop_delayed_data_streams(r_ctx &rctx)
          rds->content = NULL;
       }
    }
-   
+
    drop_delayed_restore_streams(rctx, true);
    return true;
 
@@ -444,14 +442,7 @@ void do_restore(JCR *jcr)
    binit(&rctx.bfd);
    binit(&rctx.forkbfd);
    attr = rctx.attr = new_attr(jcr);
-   if (have_acl) {
-      jcr->acl_ctx = (acl_ctx_t *)malloc(sizeof(acl_ctx_t));
-      memset(jcr->acl_ctx, 0, sizeof(acl_ctx_t));
-   }
-   if (have_xattr) {
-      jcr->xattr_ctx = (xattr_ctx_t *)malloc(sizeof(xattr_ctx_t));
-      memset(jcr->xattr_ctx, 0, sizeof(xattr_ctx_t));
-   }
+   jcr->xacl = (XACL*)new_xacl();
 
    Dsm_check(200);
    while (fdmsg->bget_msg(&bmsg) >= 0 && !job_canceled(jcr)) {
@@ -829,26 +820,26 @@ void do_restore(JCR *jcr)
 
       case STREAM_UNIX_ACCESS_ACL:
       case STREAM_UNIX_DEFAULT_ACL:
-      case STREAM_ACL_AIX_TEXT:
-      case STREAM_ACL_DARWIN_ACCESS:
-      case STREAM_ACL_FREEBSD_DEFAULT:
-      case STREAM_ACL_FREEBSD_ACCESS:
-      case STREAM_ACL_HPUX_ACL_ENTRY:
-      case STREAM_ACL_IRIX_DEFAULT:
-      case STREAM_ACL_IRIX_ACCESS:
-      case STREAM_ACL_LINUX_DEFAULT:
-      case STREAM_ACL_LINUX_ACCESS:
-      case STREAM_ACL_TRU64_DEFAULT:
-      case STREAM_ACL_TRU64_DEFAULT_DIR:
-      case STREAM_ACL_TRU64_ACCESS:
-      case STREAM_ACL_SOLARIS_POSIX:
-      case STREAM_ACL_SOLARIS_NFS4:
-      case STREAM_ACL_AFS_TEXT:
-      case STREAM_ACL_AIX_AIXC:
-      case STREAM_ACL_AIX_NFS4:
-      case STREAM_ACL_FREEBSD_NFS4:
-      case STREAM_ACL_HURD_DEFAULT:
-      case STREAM_ACL_HURD_ACCESS:
+      case STREAM_XACL_AIX_TEXT:
+      case STREAM_XACL_DARWIN_ACCESS:
+      case STREAM_XACL_FREEBSD_DEFAULT:
+      case STREAM_XACL_FREEBSD_ACCESS:
+      case STREAM_XACL_HPUX_ACL_ENTRY:
+      case STREAM_XACL_IRIX_DEFAULT:
+      case STREAM_XACL_IRIX_ACCESS:
+      case STREAM_XACL_LINUX_DEFAULT:
+      case STREAM_XACL_LINUX_ACCESS:
+      case STREAM_XACL_TRU64_DEFAULT:
+      case STREAM_XACL_TRU64_DEFAULT_DIR:
+      case STREAM_XACL_TRU64_ACCESS:
+      case STREAM_XACL_SOLARIS_POSIX:
+      case STREAM_XACL_SOLARIS_NFS4:
+      case STREAM_XACL_AFS_TEXT:
+      case STREAM_XACL_AIX_AIXC:
+      case STREAM_XACL_AIX_NFS4:
+      case STREAM_XACL_FREEBSD_NFS4:
+      case STREAM_XACL_HURD_DEFAULT:
+      case STREAM_XACL_HURD_ACCESS:
          /*
           * Do not restore ACLs when
           * a) The current file is not extracted
@@ -877,16 +868,16 @@ void do_restore(JCR *jcr)
          }
          break;
 
-      case STREAM_XATTR_HURD:
-      case STREAM_XATTR_IRIX:
-      case STREAM_XATTR_TRU64:
-      case STREAM_XATTR_AIX:
-      case STREAM_XATTR_OPENBSD:
-      case STREAM_XATTR_SOLARIS_SYS:
-      case STREAM_XATTR_DARWIN:
-      case STREAM_XATTR_FREEBSD:
-      case STREAM_XATTR_LINUX:
-      case STREAM_XATTR_NETBSD:
+      case STREAM_XACL_HURD_XATTR:
+      case STREAM_XACL_IRIX_XATTR:
+      case STREAM_XACL_TRU64_XATTR:
+      case STREAM_XACL_AIX_XATTR:
+      case STREAM_XACL_OPENBSD_XATTR:
+      case STREAM_XACL_SOLARIS_SYS_XATTR:
+      case STREAM_XACL_DARWIN_XATTR:
+      case STREAM_XACL_FREEBSD_XATTR:
+      case STREAM_XACL_LINUX_XATTR:
+      case STREAM_XACL_NETBSD_XATTR:
          /*
           * Do not restore Extended Attributes when
           * a) The current file is not extracted
@@ -915,7 +906,7 @@ void do_restore(JCR *jcr)
          }
          break;
 
-      case STREAM_XATTR_SOLARIS:
+      case STREAM_XACL_SOLARIS_XATTR:
          /*
           * Do not restore Extended Attributes when
           * a) The current file is not extracted
@@ -1014,34 +1005,34 @@ ok_out:
     */
    Dmsg2(10, "End Do Restore. Files=%d Bytes=%s\n", jcr->JobFiles,
       edit_uint64(jcr->JobBytes, ec1));
-   if (have_acl && jcr->acl_ctx->nr_errors > 0) {
-      Jmsg(jcr, M_WARNING, 0, _("Encountered %ld acl errors while doing restore\n"),
-           jcr->acl_ctx->nr_errors);
+
+   if (jcr->xacl->get_acl_nr_errors() > 0) {
+      Jmsg(jcr, M_WARNING, 0, _("Encountered %ld acl errors while doing restore\n"), jcr->xacl->get_acl_nr_errors());
    }
-   if (have_xattr && jcr->xattr_ctx->nr_errors > 0) {
-      Jmsg(jcr, M_WARNING, 0, _("Encountered %ld xattr errors while doing restore\n"),
-           jcr->xattr_ctx->nr_errors);
+   if (jcr->xacl->get_xattr_nr_errors() > 0) {
+      Jmsg(jcr, M_WARNING, 0, _("Encountered %ld xattr errors while doing restore\n"), jcr->xacl->get_xattr_nr_errors());
    }
+
    if (non_suppored_data > 1 || non_suppored_attr > 1) {
       Jmsg(jcr, M_WARNING, 0, _("%d non-supported data streams and %d non-supported attrib streams ignored.\n"),
          non_suppored_data, non_suppored_attr);
-   } 
+   }
    if (non_suppored_rsrc) {
       Jmsg(jcr, M_INFO, 0, _("%d non-supported resource fork streams ignored.\n"), non_suppored_rsrc);
-   } 
+   }
    if (non_suppored_finfo) {
       Jmsg(jcr, M_INFO, 0, _("%d non-supported Finder Info streams ignored.\n"), non_suppored_finfo);
-   } 
+   }
    if (non_suppored_acl) {
       Jmsg(jcr, M_INFO, 0, _("%d non-supported acl streams ignored.\n"), non_suppored_acl);
-   } 
+   }
    if (non_suppored_crypto) {
       Jmsg(jcr, M_INFO, 0, _("%d non-supported crypto streams ignored.\n"), non_suppored_acl);
-   } 
+   }
    if (non_suppored_xattr) {
       Jmsg(jcr, M_INFO, 0, _("%d non-supported xattr streams ignored.\n"), non_suppored_xattr);
-   } 
+   }
+
    /* Free Signature & Crypto Data */
    free_signature(rctx);
    free_session(rctx);
@@ -1055,7 +1046,7 @@ ok_out:
       crypto_cipher_free(rctx.cipher_ctx.cipher);
       rctx.cipher_ctx.cipher = NULL;
    }
+
    if (rctx.cipher_ctx.buf) {
       free_pool_memory(rctx.cipher_ctx.buf);
       rctx.cipher_ctx.buf = NULL;
@@ -1076,22 +1067,11 @@ ok_out:
       jcr->compress_buf = NULL;
       jcr->compress_buf_size = 0;
    }
-   if (have_acl && jcr->acl_ctx) {
-      if (jcr->acl_ctx->content) {
-         free(jcr->acl_ctx->content);
-      }
-      free(jcr->acl_ctx);
-      jcr->acl_ctx = NULL;
-   } 
-   if (have_xattr && jcr->xattr_ctx) {
-      if (jcr->xattr_ctx->content) {
-         free(jcr->xattr_ctx->content);
-      }
-      free(jcr->xattr_ctx);
-      jcr->xattr_ctx = NULL;
-   } 
+
+   if (jcr->xacl) {
+      delete(jcr->xacl);
+      jcr->xacl = NULL;
+   }
 
    /* Free the delayed stream stack list. */
    if (rctx.delayed_streams) {
@@ -1241,7 +1221,7 @@ bool decompress_data(JCR *jcr, int32_t stream, char **data, uint32_t *length)
        * NOTE! We only use uLong and Byte because they are
        *  needed by the zlib routines, they should not otherwise
        *  be used in Bacula.
-       */ 
+       */
       compress_len = jcr->compress_buf_size;
       Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, *length);
       while ((stat=uncompress((Byte *)jcr->compress_buf, &compress_len,
@@ -1395,7 +1375,7 @@ int32_t extract_data(r_ctx &rctx, POOLMEM *buf, int32_t buflen)
        *  and then buffer any remaining data. This should be effecient
        *  as long as Bacula's block size is not significantly smaller than the
        *  encryption block size (extremely unlikely!)
-       */ 
+       */
       unser_crypto_packet_len(cipher_ctx);
       Dmsg1(500, "Crypto unser block size=%d\n", cipher_ctx->packet_len - CRYPTO_LEN_SIZE);
 
@@ -1438,7 +1418,7 @@ int32_t extract_data(r_ctx &rctx, POOLMEM *buf, int32_t buflen)
          memmove(cipher_ctx->buf, &cipher_ctx->buf[cipher_ctx->packet_len],
             cipher_ctx->buf_len);
       }
-      /* The packet was successfully written, reset the length so that 
+      /* The packet was successfully written, reset the length so that
        *  the next packet length may be re-read by unser_crypto_packet_len() */
       cipher_ctx->packet_len = 0;
    }
@@ -1480,7 +1460,7 @@ static bool close_previous_stream(r_ctx &rctx)
 
       /* Now perform the delayed restore of some specific data streams. */
       rtn = pop_delayed_data_streams(rctx);
+
       /* Verify the cryptographic signature, if any */
       rctx.type = rctx.attr->type;
       verify_signature(rctx);
@@ -1497,7 +1477,7 @@ static bool close_previous_stream(r_ctx &rctx)
    }
 
    return rtn;
-} 
+}
 
 /*
  * In the context of jcr, flush any remaining data from the cipher context,
@@ -1672,7 +1652,7 @@ static bool verify_signature(r_ctx &rctx)
             }
          }
          if (jcr->crypto.digest) {
-            /* Use digest computed while writing the file to verify 
+            /* Use digest computed while writing the file to verify
              *  the signature */
             if ((err = crypto_sign_verify(sig, keypair, jcr->crypto.digest)) != CRYPTO_ERROR_NONE) {
                Dmsg1(50, "Bad signature on %s\n", jcr->last_fname);
@@ -1686,7 +1666,7 @@ static bool verify_signature(r_ctx &rctx)
             jcr->crypto.digest = digest;
 
             /* Checksum the entire file
-             * Make sure we don't modify JobBytes by saving and 
+             * Make sure we don't modify JobBytes by saving and
              *  restoring it */
             saved_bytes = jcr->JobBytes;
             if (find_one_file(jcr, jcr->ff, do_file_digest, jcr->last_fname, (dev_t)-1, 1) != 0) {
diff --git a/bacula/src/filed/xacl.c b/bacula/src/filed/xacl.c
new file mode 100644 (file)
index 0000000..5b60d7d
--- /dev/null
@@ -0,0 +1,1332 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ *
+ * A specialized class to handle ACL and XATTR in Bacula Enterprise.
+ * The runtime consist of two parts:
+ * 1. OS independent class: XACL
+ * 2. OS dependent subclass: XACL_*
+ *
+ * OS dependent subclasses are available for the following OS:
+ *   - Darwin (OSX)
+ *   - FreeBSD (POSIX and NFSv4/ZFS acls)
+ *   - Linux
+ *   - Solaris (POSIX and NFSv4/ZFS acls)
+ *
+ * OS dependend subclasses in progress:
+ *   - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface)
+ *   - HPUX
+ *   - IRIX
+ *   - Tru64
+ *
+ * OS independent class support AFS acls using the pioctl interface.
+ *
+ * ACLs are saved in OS native text format provided by acl(3) API and uses
+ * different streams for all different platforms.
+ * XATTRs are saved in OS independent format (Bacula own) and uses different streams
+ * for all different platforms. In theory it is possible to restore XATTRs from
+ * particular OS on different OS platform. But this functionality is not available.
+ * Above behavior is a backward compatibility with previous Bacula implementation
+ * we need to maintain.
+ *
+ * During OS specyfic implementation of XACL you need to implement a following methods:
+ *
+ * [xacl] - indicates xacl function/method to call
+ * [os] - indicates OS specyfic function, which could be different on specyfic OS
+ *        (we use a Linux api calls as an example)
+ *
+ * ::os_get_acl(JCR *jcr, XACL_type xacltype)
+ *
+ *   1. get binary form of the acl - acl_get_file[os]
+ *   2. check if acl is trivial if required - call acl_issimple[xacl]
+ *   3. translate binary form into text representation - acl_to_text[os]
+ *   4. save acl text into content - set_content[xacl]
+ *   5. if acl not supported on filesystem - call clear_flag(XACL_FLAG_NATIVE)[xacl]
+ *
+ * ::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt)
+ *
+ *   1. call os_get_acl[xacl] for all supported ACL_TYPES
+ *   2. call send_acl_stream[xacl] for all supported ACL_STREAMS
+ *
+ * ::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length)
+ *
+ *   1. prepare acl binary form from text representation stored in content - acl_from_text[os]
+ *   2. set acl on file - acl_set_file[os]
+ *   3. if acl not supported on filesystem, clear_flag(XACL_FLAG_NATIVE)
+ *
+ * ::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length)
+ *
+ *   1. call os_set_acl for all supported ACL_TYPES
+ *
+ * ::os_get_xattr_names (JCR *jcr, int namespace, POOLMEM ** pxlist, uint32_t * xlen)
+ *
+ *    1. get a size of the extended attibutes list for the file - llistxattr[os]
+ *       in most os'es it is required to have a sufficient space for attibutes list
+ *       and we wont allocate too much and too low space
+ *    2. allocate the buffer of required space
+ *    3. get an extended attibutes list for file - llistxattr[os]
+ *    4. return allocated space buffer in pxlist and length of the buffer in xlen
+ *
+ * ::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen)
+ *
+ *    1. get a size of the extended attibute value for the file - lgetxattr[os]
+ *       in most os'es it is required to have a sufficient space for attibute value
+ *       and we wont allocate too much and too low space
+ *    2. allocate the buffer of required space
+ *    3. get an extended attibute value for file - lgetxattr[os]
+ *    4. return allocated space buffer in pvalue and length of the buffer in plen
+ *
+ * ::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt)
+ *
+ *    1. get a list of extended attributes (name and value) for a file; in most implementations
+ *       it require to get a separate list of attributes names and separate values for every name,
+ *       so it is:
+ *       1A. get a list of xattr attribute names available on file - os_get_xattr_names[xacl]
+ *       1B. for every attribute name get a value - os_get_xattr_value[xacl]
+ *          You should skip some OS specyfic attributes like ACL attributes or NFS4; you can use
+ *          check_xattr_skiplists[xacl] for this
+ *       1C. build a list [type alist] of name/value pairs stored in XACL_xattr struct
+ *    2. if the xattr list is not empty then serialize the list using serialize_xattr_stream[xacl]
+ *    3. call send_xattr_stream[xacl]
+ *
+ * ::os_set_xattr (JCR *jcr, XACL_xattr *xattr)
+ *
+ *    1. set xattr on file using name/value in xattr - lsetxattr[os]
+ *    2. if xattr not supported on filesystem - call clear_flag(XACL_FLAG_NATIVE)[xacl]
+ *
+ * ::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length)
+ *
+ *    1. unserialize backup stream
+ *    2. for every extended attribute restored call os_set_xattr[xacl] to set this attribute on file
+ */
+
+#include "bacula.h"
+#include "filed.h"
+
+/*
+ * This is a constructor of the base XACL class which is OS independent
+ *
+ * - for initialization it uses ::init()
+ *
+ */
+XACL::XACL (){
+   init();
+};
+
+/*
+ * This is a destructor of the XACL class
+ */
+XACL::~XACL (){
+   free_pool_memory(content);
+};
+
+/*
+ * Initialization routine
+ * - initializes all variables to required status
+ * - allocates required memory
+ */
+void XACL::init(){
+#if defined(HAVE_ACL)
+   acl_ena = TRUE;
+#else
+   acl_ena = FALSE;
+#endif
+
+#if defined(HAVE_XATTR)
+   xattr_ena = TRUE;
+#else
+   xattr_ena = FALSE;
+#endif
+
+   /* generic variables */
+   flags = XACL_FLAG_NONE;
+   current_dev = 0;
+   content = get_pool_memory(PM_BSOCK);   /* it is better to have a 4k buffer */
+   content_len = 0;
+   acl_nr_errors = 0;
+   xattr_nr_errors = 0;
+   acl_streams = NULL;
+   default_acl_streams = NULL;
+   xattr_streams = NULL;
+   xattr_skiplist = NULL;
+   xattr_acl_skiplist = NULL;
+};
+
+/*
+ * Enables ACL handling in runtime, could be disabled with disable_acl
+ *    when ACL is not configured then cannot change status
+ */
+void XACL::enable_acl(){
+#if defined(HAVE_ACL)
+   acl_ena = TRUE;
+#endif
+};
+
+/*
+ * Disables ACL handling in runtime, could be enabled with enable_acl
+ *    when ACL is configured
+ */
+void XACL::disable_acl(){
+   acl_ena = FALSE;
+};
+
+/*
+ * Enables XATTR handling in runtime, could be disabled with disable_xattr
+ *    when XATTR is not configured then cannot change status
+ */
+void XACL::enable_xattr(){
+#ifdef HAVE_XATTR
+   xattr_ena = TRUE;
+#endif
+};
+
+/*
+ * Disables XATTR handling in runtime, could be enabled with enable_xattr
+ *    when XATTR is configured
+ */
+void XACL::disable_xattr(){
+   xattr_ena = FALSE;
+};
+
+/*
+ * Copies a text into a content variable and sets a constent_len respectively
+ *
+ * in:
+ *    text - a standard null terminated string
+ * out:
+ *    pointer to content variable to use externally
+ */
+POOLMEM * XACL::set_content(char *text){
+   content_len = pm_strcpy(&content, text);
+   return content;
+};
+
+/*
+ * Copies a data with length of len into a content variable
+ *
+ * in:
+ *    data - data pointer to copy into content buffer
+ * out:
+ *    pointer to content variable to use externally
+ */
+POOLMEM * XACL::set_content(char *data, int len){
+   content_len = pm_memcpy(&content, data, len);
+   return content;
+};
+
+/*
+ * Check if we changed the device,
+ * if so setup a flags
+ *
+ * in:
+ *    jcr - Job Control Record
+ * out:
+ *    bRC_XACL_ok - change of device checked and finish succesful
+ *    bRC_XACL_error - encountered error
+ *    bRC_XACL_skip - cannot verify device - no file found
+ *    bRC_XACL_inval - invalid input data
+ */
+bRC_XACL XACL::check_dev (JCR *jcr){
+
+   int lst;
+   struct stat st;
+
+   /* sanity check of input variables */
+   if (jcr == NULL || jcr->last_fname == NULL){
+      return bRC_XACL_inval;
+   }
+
+   lst = lstat(jcr->last_fname, &st);
+   switch (lst){
+      case -1: {
+         berrno be;
+         switch (errno){
+         case ENOENT:
+            return bRC_XACL_skip;
+         default:
+            Mmsg2(jcr->errmsg, _("Unable to stat file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+            Dmsg2(100, "Unable to stat file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
+            return bRC_XACL_error;
+         }
+         break;
+      }
+      case 0:
+         break;
+   }
+
+   check_dev(jcr, st.st_dev);
+
+   return bRC_XACL_ok;
+};
+
+/*
+ * Check if we changed the device, if so setup a flags
+ *
+ * in:
+ *    jcr - Job Control Record
+ * out:
+ *    internal flags status set
+ */
+void XACL::check_dev (JCR *jcr, uint32_t dev){
+
+   /* sanity check of input variables */
+   if (jcr == NULL || jcr->last_fname == NULL){
+      return;
+   }
+
+   if (current_dev != dev){
+      flags = XACL_FLAG_NONE;
+#if defined(HAVE_AFS_ACL)
+      /* handle special fs: AFS */
+      if (fstype_equals(jcr->last_fname, "afs")){
+         set_flag(XACL_FLAG_AFS);
+      } else {
+         set_flag(XACL_FLAG_NATIVE);
+      }
+#else
+      set_flag(XACL_FLAG_NATIVE);
+#endif
+      current_dev = dev;
+   }
+};
+
+/*
+ * It sends a stream located in this->content to Storage Daemon, so the main Bacula
+ * backup loop is free from this. It sends a header followed by data.
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    stream - a stream number to save
+ * out:
+ *    bRC_XACL_inval - when supplied variables are incorrect
+ *    bRC_XACL_fatal - when we can't send data to the SD
+ *    bRC_XACL_ok - send finish without errors
+ */
+bRC_XACL XACL::send_acl_stream(JCR *jcr, int stream){
+
+   BSOCK * sd;
+   POOLMEM * msgsave;
+#ifdef FD_NO_SEND_TEST
+   return bRC_XACL_ok;
+#endif
+
+   /* sanity check of input variables */
+   if (jcr == NULL || jcr->store_bsock == NULL){
+      return bRC_XACL_inval;
+   }
+   if (content_len <= 0){
+      return bRC_XACL_ok;
+   }
+
+   sd = jcr->store_bsock;
+   /* send header */
+   if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)){
+      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror());
+      return bRC_XACL_fatal;
+   }
+
+   /* send the buffer to the storage deamon */
+   Dmsg1(400, "Backing up ACL <%s>\n", content);
+   msgsave = sd->msg;
+   sd->msg = content;
+   sd->msglen = content_len + 1;
+   if (!sd->send()){
+      sd->msg = msgsave;
+      sd->msglen = 0;
+      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror());
+      return bRC_XACL_fatal;
+   }
+
+   jcr->JobBytes += sd->msglen;
+   sd->msg = msgsave;
+   if (!sd->signal(BNET_EOD)){
+      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror());
+      return bRC_XACL_fatal;
+   }
+
+   Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
+   return bRC_XACL_ok;
+};
+
+/*
+ * It sends a stream located in this->content to Storage Daemon, so the main Bacula
+ * backup loop is free from this. It sends a header followed by data.
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    stream - a stream number to save
+ * out:
+ *    bRC_XACL_inval - when supplied variables are incorrect
+ *    bRC_XACL_fatal - when we can't send data to the SD
+ *    bRC_XACL_ok - send finish without errors
+ */
+bRC_XACL XACL::send_xattr_stream(JCR *jcr, int stream){
+
+   BSOCK * sd;
+   POOLMEM * msgsave;
+#ifdef FD_NO_SEND_TEST
+   return bRC_XACL_ok;
+#endif
+
+   /* sanity check of input variables */
+   if (jcr == NULL || jcr->store_bsock == NULL){
+      return bRC_XACL_inval;
+   }
+   if (content_len <= 0){
+      return bRC_XACL_ok;
+   }
+
+   sd = jcr->store_bsock;
+   /* send header */
+   if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)){
+      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror());
+      return bRC_XACL_fatal;
+   }
+
+   /* send the buffer to the storage deamon */
+   Dmsg1(400, "Backing up XATTR <%s>\n", content);
+   msgsave = sd->msg;
+   sd->msg = content;
+   sd->msglen = content_len;
+   if (!sd->send()){
+      sd->msg = msgsave;
+      sd->msglen = 0;
+      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror());
+      return bRC_XACL_fatal;
+   }
+
+   jcr->JobBytes += sd->msglen;
+   sd->msg = msgsave;
+   if (!sd->signal(BNET_EOD)){
+      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror());
+      return bRC_XACL_fatal;
+   }
+   Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname);
+   return bRC_XACL_ok;
+};
+
+/*
+ * The main public backup method for ACL
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    ff_pkt - file backup record
+ * out:
+ *    bRC_XACL_fatal - when ACL backup is not compiled in Bacula
+ *    bRC_XACL_ok - backup finish without problems
+ *    bRC_XACL_error - when you can't backup acl data because some error
+ */
+bRC_XACL XACL::backup_acl (JCR *jcr, FF_PKT *ff_pkt){
+
+#if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL)
+   Jmsg(jcr, M_FATAL, 0, "ACL backup requested but not configured in Bacula.\n");
+   return bRC_XACL_fatal;
+#else
+   /* sanity check of input variables and verify if engine is enabled */
+   if (acl_ena && jcr != NULL && ff_pkt != NULL){
+      /* acl engine enabled, proceed */
+      bRC_XACL rc;
+      /*
+       * No acl request for link or plugin
+       *
+       * TODO: it should be possible to handle ACL/XATTR for cmd plugins
+       */
+      if (!(ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK && !ff_pkt->cmd_plugin)){
+         return bRC_XACL_ok;
+      }
+
+      jcr->errmsg[0] = 0;
+      check_dev(jcr, ff_pkt->statp.st_dev);
+
+#if defined(HAVE_AFS_ACL)
+      if (flags & XACL_FLAG_AFS){
+         Dmsg0(400, "make AFS ACL call\n");
+         rc = afs_backup_acl(jcr, ff_pkt);
+         goto bail_out;
+      }
+#endif
+
+#if defined(HAVE_ACL)
+      if (flags & XACL_FLAG_NATIVE){
+         Dmsg0(400, "make Native ACL call\n");
+         rc = os_backup_acl(jcr, ff_pkt);
+      } else {
+         /* skip acl backup */
+         return bRC_XACL_ok;
+      }
+#endif
+
+#if defined(HAVE_AFS_ACL)
+   bail_out:
+#endif
+      if (rc == bRC_XACL_error){
+         if (acl_nr_errors < ACL_MAX_ERROR_PRINT_PER_JOB){
+            if (!jcr->errmsg[0]){
+               Jmsg(jcr, M_WARNING, 0, "No OS ACL configured.\n");
+            } else {
+               Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+            }
+            inc_acl_errors();
+         }
+         return bRC_XACL_ok;
+      }
+      return rc;
+   }
+   return bRC_XACL_ok;
+#endif
+};
+
+/*
+ * The main public restore method for ACL
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    stream - a backup stream type number to restore_acl
+ *    data - a potinter to the data stream to restore
+ *    length - a data stream length
+ * out:
+ *    bRC_XACL_fatal - when ACL restore is not compiled in Bacula
+ *    bRC_XACL_ok - restore finish without problems
+ *    bRC_XACL_error - when you can't restore a stream because some error
+ */
+bRC_XACL XACL::restore_acl (JCR *jcr, int stream, char *data, uint32_t length){
+
+#if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL)
+   Jmsg(jcr, M_FATAL, 0, "ACL retore requested but not configured in Bacula.\n");
+   return bRC_XACL_fatal;
+#else
+   /* sanity check of input variables and verify if engine is enabled */
+   if (acl_ena && jcr != NULL && data != NULL){
+      /* acl engine enabled, proceed */
+      int a;
+      bRC_XACL rc = check_dev(jcr);
+
+      switch (rc){
+         case bRC_XACL_skip:
+            return bRC_XACL_ok;
+         case bRC_XACL_ok:
+            break;
+         default:
+            return rc;
+      }
+
+      /* copy a data into a content buffer */
+      set_content(data, length);
+
+      switch (stream){
+#if defined(HAVE_AFS_ACL)
+         case STREAM_XACL_AFS_TEXT:
+            if (flags & XACL_FLAG_AFS){
+               return afs_restore_acl(jcr, stream);
+            } else {
+               /*
+                * Increment error count but don't log an error again for the same filesystem.
+                */
+               inc_acl_errors();
+               return bRC_XACL_ok;
+            }
+#endif
+#if defined(HAVE_ACL)
+         case STREAM_UNIX_ACCESS_ACL:
+         case STREAM_UNIX_DEFAULT_ACL:
+            if (flags & XACL_FLAG_NATIVE){
+               return os_restore_acl(jcr, stream, content, content_len);
+            } else {
+               inc_acl_errors();
+               return bRC_XACL_ok;
+            }
+            break;
+         default:
+            if (flags & XACL_FLAG_NATIVE){
+               for (a = 0; acl_streams[a] > 0; a++){
+                  if (acl_streams[a] == stream){
+                     return os_restore_acl(jcr, stream, content, content_len);
+                  }
+               }
+               for (a = 0; default_acl_streams[a] > 0; a++){
+                  if (default_acl_streams[a] == stream){
+                     return os_restore_acl(jcr, stream, content, content_len);
+                  }
+               }
+            } else {
+               inc_acl_errors();
+               return bRC_XACL_ok;
+            }
+            break;
+#else
+         default:
+            break;
+#endif
+      }
+      /* cannot find a valid stream to support */
+      Qmsg2(jcr, M_WARNING, 0, _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"), jcr->last_fname, stream);
+      return bRC_XACL_error;
+   }
+   return bRC_XACL_ok;
+#endif
+};
+
+/*
+ * The main public backup method for XATTR
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    ff_pkt - file backup record
+ * out:
+ *    bRC_XACL_fatal - when XATTR backup is not compiled in Bacula
+ *    bRC_XACL_ok - backup finish without problems
+ *    bRC_XACL_error - when you can't backup xattr data because some error
+ */
+bRC_XACL XACL::backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
+
+#if !defined(HAVE_XATTR)
+   Jmsg(jcr, M_FATAL, 0, "XATTR backup requested but not configured in Bacula.\n");
+   return bRC_XACL_fatal;
+#else
+   /* sanity check of input variables and verify if engine is enabled */
+   if (xattr_ena && jcr != NULL && ff_pkt != NULL){
+      /* xattr engine enabled, proceed */
+      bRC_XACL rc;
+      /*
+       * No xattr request for plugin
+       *
+       * TODO: it should be possible to handle ACL/XATTR for cmd plugins
+       */
+      if (!(ff_pkt->flags & FO_XATTR && !ff_pkt->cmd_plugin)){
+         return bRC_XACL_ok;
+      }
+
+      jcr->errmsg[0] = 0;
+      check_dev(jcr, ff_pkt->statp.st_dev);
+
+      if (flags & XACL_FLAG_NATIVE){
+         Dmsg0(400, "make Native XATTR call\n");
+         rc = os_backup_xattr(jcr, ff_pkt);
+      } else {
+         /* skip xattr backup */
+         return bRC_XACL_ok;
+      }
+
+      if (rc == bRC_XACL_error){
+         if (xattr_nr_errors < XATTR_MAX_ERROR_PRINT_PER_JOB){
+            if (!jcr->errmsg[0]){
+               Jmsg(jcr, M_WARNING, 0, "No OS XATTR configured.\n");
+            } else {
+               Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+            }
+            inc_xattr_errors();
+         }
+         return bRC_XACL_ok;
+      }
+      return rc;
+   }
+   return bRC_XACL_ok;
+#endif
+};
+
+/*
+ * The main public restore method for XATTR
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    stream - a backup stream type number to restore_acl
+ *    data - a potinter to the data stream to restore
+ *    length - a data stream length
+ * out:
+ *    bRC_XACL_fatal - when XATTR restore is not compiled in Bacula
+ *    bRC_XACL_ok - restore finish without problems
+ *    bRC_XACL_error - when you can't restore a stream because some error
+ */
+bRC_XACL XACL::restore_xattr (JCR *jcr, int stream, char *data, uint32_t length){
+
+#if !defined(HAVE_XATTR)
+   Jmsg(jcr, M_FATAL, 0, "XATTR retore requested but not configured in Bacula.\n");
+   return bRC_XACL_fatal;
+#else
+   /* sanity check of input variables and verify if engine is enabled */
+   if (xattr_ena && jcr != NULL && data != NULL){
+      /* xattr engine enabled, proceed */
+      int a;
+      bRC_XACL rc = check_dev(jcr);
+
+      switch (rc){
+         case bRC_XACL_skip:
+            return bRC_XACL_ok;
+         case bRC_XACL_ok:
+            break;
+         default:
+            return rc;
+      }
+
+      /* copy a data into a content buffer */
+      set_content(data, length);
+
+      if (flags & XACL_FLAG_NATIVE){
+         for (a = 0; xattr_streams[a] > 0; a++){
+            if (xattr_streams[a] == stream){
+               return os_restore_xattr(jcr, stream, content, content_len);
+            }
+         }
+      } else {
+         inc_xattr_errors();
+         return bRC_XACL_ok;
+      }
+      /* cannot find a valid stream to support */
+      Qmsg2(jcr, M_WARNING, 0, _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"), jcr->last_fname, stream);
+      return bRC_XACL_error;
+   }
+   return bRC_XACL_ok;
+#endif
+};
+
+/*
+ * Performs a generic ACL backup using OS specyfic methods for
+ * getting acl data from file
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    ff_pkt - file to backup control package
+ * out:
+ *    bRC_XACL_ok - backup of acl's was successful
+ *    bRC_XACL_fatal - was an error during acl backup
+ */
+bRC_XACL XACL::generic_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
+
+   /* sanity check of input variables */
+   if (jcr == NULL || ff_pkt == NULL){
+      return bRC_XACL_inval;
+   }
+
+   if (os_get_acl(jcr, XACL_TYPE_ACCESS) == bRC_XACL_fatal){
+      /* XXX: check if os_get_acl return fatal and decide what to do when error is returned */
+      return bRC_XACL_fatal;
+   }
+
+   if (content_len > 0){
+      if (send_acl_stream(jcr, acl_streams[0]) == bRC_XACL_fatal){
+         return bRC_XACL_fatal;
+      }
+   }
+
+   if (ff_pkt->type == FT_DIREND){
+      if (os_get_acl(jcr, XACL_TYPE_DEFAULT) == bRC_XACL_fatal){
+         return bRC_XACL_fatal;
+      }
+      if (content_len > 0){
+         if (send_acl_stream(jcr, default_acl_streams[0]) == bRC_XACL_fatal){
+            return bRC_XACL_fatal;
+         }
+      }
+   }
+   return bRC_XACL_ok;
+};
+
+/*
+ * Performs a generic ACL restore using OS specyfic methods for
+ * setting acl data on file.
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    stream - a stream number to restore
+ * out:
+ *    bRC_XACL_ok - backup of acl's was successful
+ *    bRC_XACL_error - was an error during acl backup
+ *    bRC_XACL_fatal - was a fatal error during acl backup or input data is invalid
+ */
+bRC_XACL XACL::generic_restore_acl (JCR *jcr, int stream){
+
+   unsigned int count;
+
+   /* sanity check of input variables */
+   if (jcr == NULL){
+      return bRC_XACL_inval;
+   }
+
+   switch (stream){
+      case STREAM_UNIX_ACCESS_ACL:
+         return os_set_acl(jcr, XACL_TYPE_ACCESS, content, content_len);
+      case STREAM_UNIX_DEFAULT_ACL:
+         return os_set_acl(jcr, XACL_TYPE_DEFAULT, content, content_len);
+      default:
+         for (count = 0; acl_streams[count] > 0; count++){
+            if (acl_streams[count] == stream){
+               return os_set_acl(jcr, XACL_TYPE_ACCESS, content, content_len);
+            }
+         }
+         for (count = 0; default_acl_streams[count] > 0; count++){
+            if (default_acl_streams[count] == stream){
+               return os_set_acl(jcr, XACL_TYPE_DEFAULT, content, content_len);
+            }
+         }
+         break;
+   }
+   return bRC_XACL_error;
+};
+
+/*
+ * Checks if supplied xattr attribute name is indicated on OS specyfic lists
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    ff_pkt - file to backup control package
+ *    name - a name of the attribute to check
+ * out:
+ *    TRUE - the attribute name is found on OS specyfic skip lists and should be skipped during backup
+ *    FALSE - the attribute should be saved on backup stream
+ */
+bool XACL::check_xattr_skiplists (JCR *jcr, FF_PKT *ff_pkt, char * name){
+
+   bool skip = FALSE;
+   int count;
+
+   /* sanity check of input variables */
+   if (jcr == NULL || ff_pkt == NULL || name ==  NULL){
+      return false;
+   }
+
+   /*
+    * On some OSes you also get the acls in the extented attribute list.
+    * So we check if we are already backing up acls and if we do we
+    * don't store the extended attribute with the same info.
+    */
+   if (ff_pkt->flags & FO_ACL){
+      for (count = 0; xattr_acl_skiplist[count] != NULL; count++){
+         if (bstrcmp(name, xattr_acl_skiplist[count])){
+            skip = true;
+            break;
+         }
+      }
+   }
+   /* on some OSes we want to skip certain xattrs which are in the xattr_skiplist array. */
+   if (!skip){
+      for (count = 0; xattr_skiplist[count] != NULL; count++){
+         if (bstrcmp(name, xattr_skiplist[count])){
+            skip = true;
+            break;
+         }
+      }
+   }
+
+   return skip;
+};
+
+
+/*
+ * Performs generic XATTR backup using OS specyfic methods for
+ * getting xattr data from files - os_get_xattr_names and os_get_xattr_value
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    ff_pkt - file to backup control package
+ * out:
+ *    bRC_XACL_ok - xattr backup ok or no xattr to backup found
+ *    bRC_XACL_error/fatal - an error or fatal error occurred
+ *    bRC_XACL_inval - input variables was invalid
+ */
+bRC_XACL XACL::generic_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
+
+   bRC_XACL rc;
+   POOLMEM *xlist;
+   uint32_t xlen;
+   char *name;
+   uint32_t name_len;
+   POOLMEM *value;
+   uint32_t value_len;
+   bool skip;
+   alist *xattr_list = NULL;
+   int xattr_count = 0;
+   uint32_t len = 0;
+   XACL_xattr *xattr;
+
+   /* sanity check of input variables */
+   if (jcr == NULL || ff_pkt == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* xlist is allocated as POOLMEM by os_get_xattr_names */
+   rc = os_get_xattr_names(jcr, &xlist, &xlen);
+   switch (rc){
+      case bRC_XACL_ok:
+         /* it's ok, so go further */
+         break;
+      case bRC_XACL_skip:
+      case bRC_XACL_cont:
+         /* no xattr available, so skip rest of it */
+         return bRC_XACL_ok;
+      default:
+         return rc;
+   }
+
+   /* follow the list of xattr names and get the values
+    * TODO: change a standard NULL-terminated list of names into alist of structures */
+   for (name = xlist; (name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){
+
+      name_len = strlen(name);
+      skip =  check_xattr_skiplists(jcr, ff_pkt, name);
+      if (skip || name_len == 0){
+         Dmsg1(100, "Skipping xattr named \"%s\"\n", name);
+         continue;
+      }
+
+      /* value is allocated as POOLMEM by os_get_xattr_value */
+      rc = os_get_xattr_value(jcr, name, &value, &value_len);
+      switch (rc){
+         case bRC_XACL_ok:
+            /* it's ok, so go further */
+            break;
+         case bRC_XACL_skip:
+            /* no xattr available, so skip rest of it */
+            free_pool_memory(xlist);
+            return bRC_XACL_ok;
+         default:
+            /* error / fatal */
+            free_pool_memory(xlist);
+            return rc;
+      }
+
+      /*
+       * we have a name of the extended attribute in the name variable
+       * and value of the extended attribute in the value variable
+       * so we need to build a list
+       */
+      xattr = (XACL_xattr*)malloc(sizeof(XACL_xattr));
+      xattr->name_len = name_len;
+      xattr->name = name;
+      xattr->value_len = value_len;
+      xattr->value = value;
+      /*       magic              name_len          name        value_len       value */
+      len += sizeof(uint32_t) + sizeof(uint32_t) + name_len + sizeof(uint32_t) + value_len;
+
+      if (xattr_list == NULL){
+         xattr_list = New(alist(10, not_owned_by_alist));
+      }
+      xattr_list->append(xattr);
+      xattr_count++;
+   }
+   if (xattr_count > 0){
+      /* serialize the stream */
+      rc = serialize_xattr_stream(jcr, len, xattr_list);
+      if (rc != bRC_XACL_ok){
+         Mmsg(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), jcr->last_fname);
+         Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", jcr->last_fname);
+         goto bailout;
+      } else {
+         /* send data to SD */
+         rc = send_xattr_stream(jcr, xattr_streams[0]);
+      }
+   } else {
+      rc = bRC_XACL_ok;
+   }
+
+bailout:
+   /* free allocated data */
+   if (xattr_list != NULL){
+      foreach_alist(xattr, xattr_list){
+         if (xattr == NULL){
+            break;
+         }
+         if (xattr->value){
+            free_pool_memory(xattr->value);
+         }
+         free(xattr);
+      }
+      delete xattr_list;
+   }
+   if (xlist != NULL){
+      free_pool_memory(xlist);
+   }
+
+   return rc;
+};
+
+/*
+ * Performs a generic XATTR restore using OS specyfic methods for
+ * setting XATTR data on file.
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    stream - a stream number to restore
+ * out:
+ *    bRC_XACL_ok - restore of acl's was successful
+ *    bRC_XACL_error - was an error during xattr restore
+ *    bRC_XACL_fatal - was a fatal error during xattr restore
+ *    bRC_XACL_inval - input variables was invalid
+ */
+bRC_XACL XACL::generic_restore_xattr (JCR *jcr, int stream){
+
+   bRC_XACL rc = bRC_XACL_ok;
+   alist *xattr_list;
+   XACL_xattr *xattr;
+
+   /* sanity check of input variables */
+   if (jcr == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* empty list */
+   xattr_list = New(alist(10, not_owned_by_alist));
+
+   /* unserialize data */
+   unserialize_xattr_stream(jcr, content, content_len, xattr_list);
+
+   /* follow the list to set all attributes */
+   foreach_alist(xattr, xattr_list){
+      rc = os_set_xattr(jcr, xattr);
+      if (rc != bRC_XACL_ok){
+         Dmsg2(100, "Failed to set extended attribute %s on file \"%s\"\n", xattr->name, jcr->last_fname);
+         goto bailout;
+      }
+   }
+
+bailout:
+   /* free allocated data */
+   if (xattr_list != NULL){
+      foreach_alist(xattr, xattr_list){
+         if (xattr == NULL){
+            break;
+         }
+         if (xattr->name){
+            free(xattr->name);
+         }
+         if (xattr->value){
+            free(xattr->value);
+         }
+         free(xattr);
+      }
+      delete xattr_list;
+   }
+   return rc;
+};
+
+/*
+ * Initialize variables acl_streams and default_acl_streams for a specified OS.
+ * The rutine should be called from object instance constructor
+ *
+ * in:
+ *    pacl - acl streams supported for specyfic OS
+ *    pacl_def - default (directory) acl streams supported for specyfic OS
+ */
+void XACL::set_acl_streams (const int *pacl, const int *pacl_def){
+
+   acl_streams = pacl;
+   default_acl_streams = pacl_def;
+};
+
+/*
+ * Initialize a variable xattr_streams for a specified OS.
+ * The rutine should be called from object instance constructor
+ *
+ * in:
+ *    pxattr - xattr streams supported for specyfic OS
+ */
+void XACL::set_xattr_streams (const int *pxattr){
+
+   xattr_streams = pxattr;
+};
+
+/*
+ * Initialize variables xattr_skiplist and xattr_acl_skiplist for a specified OS.
+ * The rutine should be called from object instance constructor
+ *
+ * in:
+ *    pxattr - xattr skip list for specyfic OS
+ *    pxattr_acl - xattr acl names skip list for specyfic OS
+ */
+void XACL::set_xattr_skiplists (const char **pxattr, const char **pxattr_acl){
+
+   xattr_skiplist = pxattr;
+   xattr_acl_skiplist = pxattr_acl;
+};
+
+/*
+ * Serialize the XATTR stream which will be saved into archive. Serialization elements cames from
+ * a list and for backward compatibility we produce the same stream as prievous Bacula versions.
+ *
+ * serialized stream consists of the following elements:
+ *    magic - A magic string which makes it easy to detect any binary incompatabilites
+ *             required for backward compatibility
+ *    name_len - The length of the following xattr name
+ *    name - The name of the extended attribute
+ *    value_len - The length of the following xattr data
+ *    value - The actual content of the extended attribute only if value_len is greater then zero
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    len - expected serialize length
+ *    list - a list of xattr elements to serialize
+ * out:
+ *    bRC_XACL_ok - when serialization was perfect
+ *    bRC_XACL_inval - when we have invalid variables
+ *    bRC_XACL_error - illegal attribute name
+ */
+bRC_XACL XACL::serialize_xattr_stream(JCR *jcr, uint32_t len, alist *list){
+
+   ser_declare;
+   XACL_xattr *xattr;
+
+   /* sanity check of input variables */
+   if (jcr == NULL || list == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* we serialize data direct to content buffer, so check if data fits */
+   content = check_pool_memory_size(content, len + 20);
+   ser_begin(content, len + 20);
+
+   foreach_alist(xattr, list){
+      if (xattr == NULL){
+         break;
+      }
+      /*
+       * serialize data
+       *
+       * we have to start with the XATTR_MAGIC for backward compatibility (the magic is silly)
+       */
+      ser_uint32(XATTR_MAGIC);
+      /* attribute name length and name itself */
+      if (xattr->name_len > 0 && xattr->name){
+         ser_uint32(xattr->name_len);
+         ser_bytes(xattr->name, xattr->name_len);
+      } else {
+         /* error - name cannot be empty */
+         Mmsg0(jcr->errmsg, _("Illegal empty xattr attribute name\n"));
+         Dmsg0(100, "Illegal empty xattr attribute name\n");
+         return bRC_XACL_error;
+      }
+      /* attibute value length and value itself */
+      ser_uint32(xattr->value_len);
+      if (xattr->value_len > 0 && xattr->value){
+         ser_bytes(xattr->value, xattr->value_len);
+         Dmsg3(100, "Backup xattr named %s, value %*.s\n", xattr->name, xattr->value_len, xattr->value);
+      } else {
+         Dmsg1(100, "Backup empty xattr named %s\n", xattr->name);
+      }
+   }
+
+   ser_end(content, len + 20);
+   content_len = ser_length(content);
+
+   return bRC_XACL_ok;
+};
+
+/*
+ * Unserialize XATTR stream on *content and produce a xattr *list which contain
+ * key => value pairs
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    content - a stream content to unserialize
+ *    length - a content length
+ *    list - a pointer to the xattr list to populate
+ * out:
+ *    bRC_XACL_ok - when unserialize was perfect
+ *    bRC_XACL_inval - when we have invalid variables
+ *    list - key/value pairs populated xattr list
+ */
+bRC_XACL XACL::unserialize_xattr_stream(JCR *jcr, char *content, uint32_t length, alist *list){
+
+   unser_declare;
+   uint32_t magic;
+   XACL_xattr *xattr;
+
+   /* sanity check of input variables */
+   if (jcr == NULL || content == NULL || list == NULL){
+      return bRC_XACL_inval;
+   }
+
+   unser_begin(content, length);
+   while (unser_length(content) < length){
+      /*
+       * Sanity check of correct stream magic number
+       * Someone was too paranoid to implement this kind of verification in original Bacula code
+       * Unfortunate for backward compatibility we have to follow this insane implementation
+       *
+       * XXX: design a new xattr stream format
+       */
+      unser_uint32(magic);
+      if (magic != XATTR_MAGIC){
+         Mmsg(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"), jcr->last_fname);
+         Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n", jcr->last_fname);
+         return bRC_XACL_error;
+      }
+      /* first attribute name length */
+      xattr = (XACL_xattr *)malloc(sizeof(XACL_xattr));
+      unser_uint32(xattr->name_len);
+      if (xattr->name_len == 0){
+         /* attribute name cannot be empty */
+         Mmsg(jcr->errmsg, _("Illegal xattr stream, xattr name length <= 0 on file \"%s\"\n"), jcr->last_fname);
+         Dmsg1(100, "Illegal xattr stream, xattr name length <= 0 on file \"%s\"\n", jcr->last_fname);
+         free(xattr);
+         return bRC_XACL_error;
+      }
+      /* followed by attribute name itself */
+      xattr->name = (char *)malloc(xattr->name_len + 1);
+      unser_bytes(xattr->name, xattr->name_len);
+      xattr->name[xattr->name_len] = '\0';
+      /* attribute value */
+      unser_uint32(xattr->value_len);
+      if (xattr->value_len > 0){
+         /* we have a value */
+         xattr->value = (char *)malloc(xattr->value_len + 1);
+         unser_bytes(xattr->value, xattr->value_len);
+         xattr->value[xattr->value_len] = '\0';
+         Dmsg3(100, "Restoring xattr named %s, value %.*s\n", xattr->name, xattr->value_len, xattr->value);
+      } else {
+         /* value is empty */
+         xattr->value = NULL;
+         Dmsg1(100, "Restoring empty xattr named %s\n", xattr->name);
+      }
+      list->append(xattr);
+   }
+   unser_end(content, length);
+
+   return bRC_XACL_ok;
+};
+
+#if defined(HAVE_AFS_ACL)
+
+#if defined(HAVE_AFS_AFSINT_H) && defined(HAVE_AFS_VENUS_H)
+#include <afs/afsint.h>
+#include <afs/venus.h>
+#else
+#error "configure failed to detect availability of afs/afsint.h and/or afs/venus.h"
+#endif
+
+/*
+ * External references to functions in the libsys library function not in current include files.
+ */
+extern "C" {
+long pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow);
+}
+
+/*
+ * Backup ACL data of AFS
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    ff_pkt - file backup record
+ * out:
+ *    bRC_XACL_inval - input variables are invalid (NULL)
+ *    bRC_XACL_ok - backup finish without problems
+ *    bRC_XACL_error - when you can't backup acl data because some error
+ */
+bRC_XACL XACL::afs_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
+
+   int rc;
+   struct ViceIoctl vip;
+   char data[BUFSIZ];
+
+   /* sanity check of input variables */
+   if (jcr == NULL || ff_pkt == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* AFS ACLs can only be set on a directory, so no need to try other files */
+   if (ff_pkt->type != FT_DIREND){
+      return bRC_XACL_ok;
+   }
+
+   vip.in = NULL;
+   vip.in_size = 0;
+   vip.out = data;
+   vip.out_size = BUFSIZE;
+   memset(data, 0, BUFSIZE);
+
+   if ((rc = pioctl(jcr->last_fname, VIOCGETAL, &vip, 0)) < 0){
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("pioctl VIOCGETAL error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg2(100, "pioctl VIOCGETAL error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+      return bRC_XACL_error;
+   }
+   set_content(data);
+   return send_acl_stream(jcr, STREAM_XACL_AFS_TEXT);
+};
+
+/*
+ * Restore ACL data of AFS
+ * in:
+ *    jcr - Job Control Record
+ *    stream - a backup stream type number to restore_acl
+ * out:
+ *    bRC_XACL_inval - input variables are invalid (NULL)
+ *    bRC_XACL_ok - backup finish without problems
+ *    bRC_XACL_error - when you can't backup acl data because some error
+ */
+bRC_XACL XACL::afs_restore_acl (JCR *jcr, int stream){
+
+   int rc;
+   struct ViceIoctl vip;
+
+   /* sanity check of input variables */
+   if (jcr == NULL || ff_pkt == NULL){
+      return bRC_XACL_inval;
+   }
+
+   vip.in = content;
+   vip.in_size = content_len;
+   vip.out = NULL;
+   vip.out_size = 0;
+
+   if ((rc = pioctl(jcr->last_fname, VIOCSETAL, &vip, 0)) < 0){
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("pioctl VIOCSETAL error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg2(100, "pioctl VIOCSETAL error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+
+      return bRC_XACL_error;
+   }
+   return bRC_XACL_ok;
+};
+#endif /* HAVE_AFS_ACL */
+
+#include "xacl_osx.h"
+#include "xacl_linux.h"
+#include "xacl_freebsd.h"
+#include "xacl_solaris.h"
+// #include "xacl_aix.h"
+
+/*
+ * Creating the corrent instance of the XACL for a supported OS
+ */
+void *new_xacl(){
+#if   defined(HAVE_DARWIN_OS)
+   return new XACL_OSX();
+#elif defined(HAVE_LINUX_OS)
+   return new XACL_Linux();
+#elif defined(HAVE_FREEBSD_OS)
+   return new XACL_FreeBSD();
+#elif defined(HAVE_HURD_OS)
+   return new XACL_Hurd();
+#elif defined(HAVE_AIX_OS)
+   return new XACL_AIX();
+#elif defined(HAVE_IRIX_OS)
+   return new XACL_IRIX();
+#elif defined(HAVE_OSF1_OS)
+   return new XACL_OSF1();
+#elif defined(HAVE_SUN_OS)
+   return new XACL_Solaris();
+#endif
+};
diff --git a/bacula/src/filed/xacl.h b/bacula/src/filed/xacl.h
new file mode 100644 (file)
index 0000000..19df653
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#ifndef __BXACL_H_
+#define __BXACL_H_
+
+/*
+ * Magic used in the magic field of the xattr struct.
+ * This way we can see if we encounter a valid xattr struct.
+ * Used for backward compatibility only.
+ */
+#define XATTR_MAGIC 0x5C5884
+
+/*
+ * Return value status enumeration
+ * You have an error when value is less then zero.
+ * You have a positive status when value is not negative
+ * (greater or equal to zero).
+ */
+enum bRC_XACL {
+   bRC_XACL_inval = -3,       // input data invalid
+   bRC_XACL_fatal = -2,       // a fatal error
+   bRC_XACL_error = -1,       // standard error
+   bRC_XACL_ok   = 0,        // success
+   bRC_XACL_skip  = 1,       // processing should skip current runtime
+   bRC_XACL_cont  = 2        // processing should skip current element
+                             // and continue with next one
+};
+
+/*
+ * We support the following types of ACLs
+ */
+typedef enum {
+   XACL_TYPE_NONE            = 0,
+   XACL_TYPE_ACCESS          = 1,
+   XACL_TYPE_DEFAULT         = 2,
+   XACL_TYPE_DEFAULT_DIR      = 3,
+   XACL_TYPE_EXTENDED        = 4,
+   XACL_TYPE_NFS4            = 5,
+   XACL_TYPE_PLUGIN          = 6
+} XACL_type;
+
+/*
+ * Flags which control what ACL/XATTR engine
+ * to use for backup/restore
+ */
+#define XACL_FLAG_NONE       0
+#define XACL_FLAG_NATIVE      0x01
+#define XACL_FLAG_AFS        0x02
+#define XACL_FLAG_PLUGIN      0x04
+
+/*
+ * Ensure we have none
+ */
+#ifndef ACL_TYPE_NONE
+#define ACL_TYPE_NONE        0x0
+#endif
+
+/*
+ * Extended attribute (xattr) list element.
+ *
+ * Every xattr consist of a Key=>Value pair where
+ * both could be a binary data.
+ */
+struct XACL_xattr {
+   uint32_t name_len;
+   char *name;
+   uint32_t value_len;
+   char *value;
+};
+
+/*
+ * Basic ACL/XATTR class which is a foundation for any other OS specyfic implementation.
+ *
+ * This class cannot be used directly as it is an abstraction class with a lot of virtual
+ * methods laying around. As a basic class it has all public API available for backup and
+ * restore functionality. As a bonus it handles all ACL/XATTR generic functions and OS
+ * independent API, i.e. for AFS ACL/XATTR or Plugins ACL/XATTR (future functionality).
+ */
+class XACL {
+private:
+   bool acl_ena;
+   bool xattr_ena;
+   uint32_t flags;
+   uint32_t current_dev;
+   POOLMEM *content;
+   uint32_t content_len;
+   uint32_t acl_nr_errors;
+   uint32_t xattr_nr_errors;
+   const int *acl_streams;
+   const int *default_acl_streams;
+   const int *xattr_streams;
+   const char **xattr_skiplist;
+   const char **xattr_acl_skiplist;
+
+   void init();
+
+   /**
+    * Perform OS specyfic ACL backup.
+    * in:
+    *   jcr - Job Control Record
+    *   ff_pkt - file to backup information rector
+    * out:
+    *   bRC_XACL_ok - backup performed without problems
+    *   any other - some error ocurred
+    */
+   virtual bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){return bRC_XACL_fatal;};
+
+   /**
+    * Perform OS specyfic ACL restore. Runtime is called only when stream is supported by OS.
+    * in:
+    *   jcr - Job Control Record
+    *   ff_pkt - file to backup information rector
+    * out:
+    *   bRC_XACL_ok - backup performed without problems
+    *   any other - some error ocurred
+    */
+   virtual bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){return bRC_XACL_fatal;};
+
+   /**
+    * Perform OS specyfic XATTR backup.
+    *
+    * in:
+    *   jcr - Job Control Record
+    *   ff_pkt - file to backup control package
+    * out:
+    *   bRC_XACL_ok - xattr backup ok or no xattr to backup found
+    *   bRC_XACL_error/fatal - an error or fatal error occurred
+    */
+   virtual bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){return bRC_XACL_fatal;};
+
+   /**
+    * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS.
+    *
+    * in:
+    *   jcr - Job Control Record
+    *   stream - backup stream number
+    *   content - a buffer with data to restore
+    *   length - a data restore length
+    * out:
+    *   bRC_XACL_ok - xattr backup ok or no xattr to backup found
+    *   bRC_XACL_error/fatal - an error or fatal error occurred
+    */
+   virtual bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){return bRC_XACL_fatal;};
+
+   /**
+    * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer.
+    *
+    * in:
+    *   jcr - Job Control Record
+    *   xacltype - the acl type to restore
+    * out:
+    *   bRC_XACL_ok -
+    *   bRC_XACL_error/fatal - an error or fatal error occurred
+    */
+   virtual bRC_XACL os_get_acl (JCR *jcr, XACL_type xacltype){return bRC_XACL_fatal;};
+
+   /**
+    * Low level OS specyfic runtime to set ACL data on file.
+    *
+    * in:
+    *   jcr - Job Control Record
+    *   xacltype - the acl type to restore
+    *   content - a buffer with data to restore
+    *   length - a data restore length
+    * out:
+    *   bRC_XACL_ok -
+    *   bRC_XACL_error/fatal - an error or fatal error occurred
+    */
+   virtual bRC_XACL os_set_acl (JCR *jcr, XACL_type xacltype, char *content, uint32_t length){return bRC_XACL_fatal;};
+
+   /**
+    * Returns a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
+    * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
+    * when not needed. The list of xattr names is returned as an unordered array of NULL terminated
+    * character strings (attribute names are separated by NULL characters), like this:
+    *  user.name1\0system.name1\0user.name2\0
+    * The format of the list is based on standard "llistxattr" function call.
+    * TODO: change the format of the list from an array of NULL terminated strings into an alist of structures.
+    *
+    * in:
+    *   jcr - Job Control Record
+    *   xlen - non NULL pointer to the uint32_t variable for storing a length of the xattr names list
+    *   pxlist - non NULL pointer to the char* variable for allocating a memoty data for xattr names list
+    * out:
+    *   bRC_XACL_ok - we've got a xattr data to backup
+    *   bRC_XACL_skip - no xattr data available, no fatal error, skip rest of the runtime
+    *   bRC_XACL_fatal - when required buffers are unallocated
+    *   bRC_XACL_error - in case of any error
+    */
+   virtual bRC_XACL os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){return bRC_XACL_fatal;};
+
+   /**
+    * Returns a value of the requested attribute name and a length of the allocated buffer.
+    * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
+    * when not needed.
+    *
+    * in:
+    *   jcr - Job Control Record
+    *   name - a name of the extended attribute
+    *   pvalue - the pointer for the buffer with value - it is allocated by function and should be freed when no needed
+    *   plen - the pointer for the length of the allocated buffer
+    *
+    * out:
+    *   pxlist - the atributes list
+    *   bRC_XACL_ok - we've got a xattr data which could be empty when xlen=0
+    *   bRC_XACL_skip - no xattr data available, no fatal error, skip rest of the runtime
+    *   bRC_XACL_error - error getting an attribute
+    *   bRC_XACL_fatal - required buffers are unallocated
+    */
+   virtual bRC_XACL os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){return bRC_XACL_fatal;};
+
+   /**
+    * Low level OS specyfic runtime to set extended attribute on file
+    *
+    * in:
+    *   jcr - Job Control Record
+    *   xattr - the struct with attribute/value to set
+    *
+    * out:
+    *   bRC_XACL_ok - setting the attribute was ok
+    *   bRC_XACL_error - error during extattribute set
+    *   bRC_XACL_fatal - required buffers are unallocated
+    */
+   virtual bRC_XACL os_set_xattr (JCR *jcr, XACL_xattr *xattr){return bRC_XACL_fatal;};
+
+   void inc_acl_errors(){ acl_nr_errors++;};
+   void inc_xattr_errors(){ xattr_nr_errors++;};
+   bRC_XACL check_dev (JCR *jcr);
+   void check_dev (JCR *jcr, uint32_t dev);
+
+public:
+   XACL ();
+   virtual ~XACL();
+
+   /* enable/disable functionality */
+   void enable_acl();
+   void disable_acl();
+   void enable_xattr();
+   void disable_xattr();
+
+   /*
+    * public methods used outside the class or derivatives
+    */
+   bRC_XACL backup_acl (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL restore_acl (JCR *jcr, int stream, char *content, uint32_t content_length);
+   bRC_XACL backup_xattr (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL restore_xattr (JCR *jcr, int stream, char *content, uint32_t content_length);
+
+   /* utility functions */
+   inline uint32_t get_acl_nr_errors(){ return acl_nr_errors;};
+   inline uint32_t get_xattr_nr_errors(){ return xattr_nr_errors;};
+   void set_acl_streams (const int *pacl, const int *pacl_def);
+   void set_xattr_streams (const int *pxattr);
+   void set_xattr_skiplists (const char **pxattr, const char **pxattr_acl);
+   inline void clear_flag (uint32_t flag){ flags &= ~flag;};
+   inline void set_flag (uint32_t flag){ flags |= flag;};
+   POOLMEM * set_content (char *text);
+   POOLMEM * set_content(char *data, int len);
+   inline POOLMEM * get_content (void){ return content;};
+   inline uint32_t get_content_size (void){ return sizeof_pool_memory(content);};
+   inline uint32_t get_content_len (void){ return content_len;};
+   bool check_xattr_skiplists (JCR *jcr, FF_PKT *ff_pkt, char * name);
+
+   /* sending data to the storage */
+   bRC_XACL send_acl_stream (JCR *jcr, int stream);
+   bRC_XACL send_xattr_stream (JCR *jcr, int stream);
+
+   /* serialize / unserialize stream */
+   bRC_XACL unserialize_xattr_stream(JCR *jcr, char *content, uint32_t length, alist *list);
+   bRC_XACL serialize_xattr_stream(JCR *jcr, uint32_t len, alist *list);
+
+   /* generic functions */
+   bRC_XACL generic_backup_acl (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL generic_restore_acl (JCR *jcr, int stream);
+   bRC_XACL afs_backup_acl (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL afs_restore_acl (JCR *jcr, int stream);
+   bRC_XACL generic_backup_xattr (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL generic_restore_xattr (JCR *jcr, int stream);
+};
+
+void *new_xacl();
+
+#endif /* __BXACL_H_ */
diff --git a/bacula/src/filed/xacl_freebsd.c b/bacula/src/filed/xacl_freebsd.c
new file mode 100644 (file)
index 0000000..59d1554
--- /dev/null
@@ -0,0 +1,946 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#include "bacula.h"
+#include "filed.h"
+#include "xacl_freebsd.h"
+
+#if defined(HAVE_FREEBSD_OS)
+/*
+ * Define the supported ACL streams for this OS
+ */
+static const int os_acl_streams[] = {
+   STREAM_XACL_FREEBSD_ACCESS,
+   STREAM_XACL_FREEBSD_NFS4,
+   0
+};
+
+static const int os_default_acl_streams[] = {
+   STREAM_XACL_FREEBSD_DEFAULT,
+   0
+};
+
+/*
+ * Define the supported XATTR streams for this OS
+ */
+static const int os_xattr_streams[] = {
+   STREAM_XACL_FREEBSD_XATTR,
+   0
+};
+
+static const int os_xattr_namespaces[] = {
+   EXTATTR_NAMESPACE_USER,
+   EXTATTR_NAMESPACE_SYSTEM,
+   -1
+};
+
+static const char *os_xattr_acl_skiplist[] = {
+   "system.posix1e.acl_access",
+   "system.posix1e.acl_default",
+   "system.nfs4.acl",
+   NULL
+};
+
+static const char *os_xattr_skiplist[] = {
+   NULL
+};
+
+/*
+ * OS Specyfic constructor
+ */
+XACL_FreeBSD::XACL_FreeBSD(){
+
+   set_acl_streams(os_acl_streams, os_default_acl_streams);
+   set_xattr_streams(os_xattr_streams);
+   set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
+};
+
+/*
+ * Translates Bacula internal acl representation into
+ * acl type
+ *
+ * in:
+ *    xacltype - internal Bacula acl type (XACL_type)
+ * out:
+ *    acl_type_t - os dependent acl type
+ *    when failed - ACL_TYPE_NONE is returned
+ */
+acl_type_t XACL_FreeBSD::get_acltype(XACL_type xacltype){
+
+   acl_type_t acltype;
+
+   switch (xacltype){
+#ifdef HAVE_ACL_TYPE_NFS4
+      case XACL_TYPE_NFS4:
+         acltype = ACL_TYPE_NFS4;
+         break;
+#endif
+      case XACL_TYPE_ACCESS:
+         acltype = ACL_TYPE_ACCESS;
+         break;
+      case XACL_TYPE_DEFAULT:
+         acltype = ACL_TYPE_DEFAULT;
+         break;
+      default:
+         /*
+          * sanity check for acl's not supported by OS
+          */
+         acltype = (acl_type_t)ACL_TYPE_NONE;
+         break;
+   }
+   return acltype;
+};
+
+/*
+ * Counts a number of acl entries
+ *
+ * in:
+ *    acl - acl object
+ * out:
+ *    int - number of entries in acl object
+ *    when no acl entry available or any error then return zero '0'
+ */
+int XACL_FreeBSD::acl_nrentries(acl_t acl){
+
+   int nr = 0;
+   acl_entry_t aclentry;
+   int rc;
+
+   rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
+   while (rc == 1){
+      nr++;
+      rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
+   }
+
+   return nr;
+};
+
+/*
+ * Checks if acl is simple.
+ *
+ * acl is simple if it has only the following entries:
+ * "user::",
+ * "group::",
+ * "other::"
+ *
+ * in:
+ *    acl - acl object
+ * out:
+ *    true - when acl object is simple
+ *    false - when acl object is not simple
+ */
+bool XACL_FreeBSD::acl_issimple(acl_t acl){
+
+   acl_entry_t aclentry;
+   acl_tag_t acltag;
+   int rc;
+
+   rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
+   while (rc == 1){
+      if (acl_get_tag_type(aclentry, &acltag) < 0){
+         return true;
+      }
+      /*
+       * Check for ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER to find out.
+       */
+      if (acltag != ACL_USER_OBJ &&
+          acltag != ACL_GROUP_OBJ &&
+          acltag != ACL_OTHER){
+         return false;
+      }
+      rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
+   }
+   return true;
+};
+
+/*
+ * Checks if ACL's are available for a specified file
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    name - specifies the system variable to be queried
+ * out:
+ *    bRC_XACL_ok - check successful, lets setup xacltype variable
+ *    bRC_XACL_error -  in case of error
+ *    bRC_XACL_skip - you should skip all other routine
+ *    bRC_XACL_cont - you need to continue your routine
+ */
+bRC_XACL XACL_FreeBSD::check_xacltype (JCR *jcr, int name){
+
+   int aclrc = 0;
+
+   aclrc = pathconf(jcr->last_fname, name);
+   switch (aclrc){
+      case -1: {
+         /* some error check why */
+         berrno be;
+         if (errno == ENOENT){
+            /* file does not exist skip it */
+            return bRC_XACL_skip;
+         } else {
+            Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+            Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+            return bRC_XACL_error;
+         }
+      }
+      case 0:
+         /* continue the routine */
+         return bRC_XACL_cont;
+      default:
+         break;
+   }
+   return bRC_XACL_ok;
+};
+
+/*
+ * Perform OS specyfic ACL backup
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_FreeBSD::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
+
+   bRC_XACL rc;
+   XACL_type xacltype = XACL_TYPE_NONE;
+
+#if defined(_PC_ACL_NFS4)
+   /*
+    * Check if filesystem supports NFS4 acls.
+    */
+   rc = check_xacltype(jcr, _PC_ACL_NFS4);
+   switch (rc){
+      case bRC_XACL_ok:
+         xacltype = XACL_TYPE_NFS4;
+         break;
+      case bRC_XACL_skip:
+         return bRC_XACL_ok;
+      case bRC_XACL_cont:
+         break;
+      default:
+         /* errors */
+         return rc;
+   }
+#endif
+   if (xacltype == XACL_TYPE_NONE){
+      /*
+       * Check if filesystem supports POSIX acls.
+       */
+      rc = check_xacltype(jcr, _PC_ACL_EXTENDED);
+      switch (rc){
+         case bRC_XACL_ok:
+            xacltype = XACL_TYPE_ACCESS;
+            break;
+         case bRC_XACL_skip:
+            return bRC_XACL_ok;
+         case bRC_XACL_cont:
+            break;
+         default:
+            /* errors */
+            return rc;
+      }
+   }
+
+   /* no ACL's available for file, so skip this filesystem */
+   if (xacltype == XACL_TYPE_NONE){
+      clear_flag(XACL_FLAG_NATIVE);
+      /*
+       * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
+       * but it is working, hehe :)
+       * you may ask why it is working? it is simple, a pm_strcpy function is handling
+       * a null pointer with a substitiution of empty string.
+       */
+      set_content(NULL);
+      return bRC_XACL_ok;
+   }
+
+   switch (xacltype){
+      case XACL_TYPE_NFS4:
+         /*
+          * Read NFS4 ACLs
+          */
+         if (os_get_acl(jcr, XACL_TYPE_NFS4) == bRC_XACL_fatal)
+            return bRC_XACL_fatal;
+
+         if (get_content_len() > 0){
+            if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_NFS4) == bRC_XACL_fatal)
+               return bRC_XACL_fatal;
+         }
+         break;
+      case XACL_TYPE_ACCESS:
+         /*
+          * Read access ACLs
+          */
+         if (os_get_acl(jcr, XACL_TYPE_ACCESS) == bRC_XACL_fatal)
+            return bRC_XACL_fatal;
+
+         if (get_content_len() > 0){
+            if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_ACCESS) == bRC_XACL_fatal)
+               return bRC_XACL_fatal;
+         }
+
+         /*
+          * Directories can have default ACLs too
+          */
+         if (ff_pkt->type == FT_DIREND){
+            if (os_get_acl(jcr, XACL_TYPE_DEFAULT) == bRC_XACL_fatal)
+               return bRC_XACL_fatal;
+            if (get_content_len() > 0){
+               if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_DEFAULT) == bRC_XACL_fatal)
+                  return bRC_XACL_fatal;
+            }
+         }
+         break;
+      default:
+         break;
+   }
+
+   return bRC_XACL_ok;
+};
+
+/*
+ * Perform OS specyfic ACL restore
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_FreeBSD::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
+
+   int aclrc = 0;
+   const char *acl_type_name;
+
+   switch (stream){
+      case STREAM_UNIX_ACCESS_ACL:
+      case STREAM_XACL_FREEBSD_ACCESS:
+      case STREAM_UNIX_DEFAULT_ACL:
+      case STREAM_XACL_FREEBSD_DEFAULT:
+         aclrc = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
+         acl_type_name = "POSIX";
+         break;
+      case STREAM_XACL_FREEBSD_NFS4:
+#if defined(_PC_ACL_NFS4)
+         aclrc = pathconf(jcr->last_fname, _PC_ACL_NFS4);
+#endif
+         acl_type_name = "NFS4";
+         break;
+      default:
+         acl_type_name = "unknown";
+         break;
+   }
+
+   switch (aclrc){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+            case ENOENT:
+               return bRC_XACL_ok;
+            default:
+               Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+               Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+               return bRC_XACL_error;
+         }
+      }
+      case 0:
+         clear_flag(XACL_FLAG_NATIVE);
+         Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"), jcr->last_fname, acl_type_name);
+         return bRC_XACL_error;
+      default:
+         break;
+   }
+
+   switch (stream){
+      case STREAM_UNIX_ACCESS_ACL:
+      case STREAM_XACL_FREEBSD_ACCESS:
+         return os_set_acl(jcr, XACL_TYPE_ACCESS, content, length);
+      case STREAM_UNIX_DEFAULT_ACL:
+      case STREAM_XACL_FREEBSD_DEFAULT:
+         return os_set_acl(jcr, XACL_TYPE_DEFAULT, content, length);
+      case STREAM_XACL_FREEBSD_NFS4:
+         return os_set_acl(jcr, XACL_TYPE_NFS4, content, length);
+      default:
+         break;
+   }
+   return bRC_XACL_error;
+};
+
+/*
+ * Perform OS specyfic extended attribute backup
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_FreeBSD::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
+
+   bRC_XACL rc;
+   POOLMEM *xlist;
+   uint32_t xlen;
+   char *name;
+   uint32_t name_len;
+   POOLMEM *value;
+   uint32_t value_len;
+   POOLMEM *name_gen;
+   uint32_t name_gen_len;
+   char * namespace_str;
+   int namespace_len;
+   bool skip;
+   alist *xattr_list = NULL;
+   int xattr_count = 0;
+   uint32_t len = 0;
+   XACL_xattr *xattr;
+   int a;
+
+   for (a = 0; os_xattr_namespaces[a] != -1; a++){ // loop through all available namespaces
+      /* xlist is allocated as POOLMEM by os_get_xattr_names */
+      rc = os_get_xattr_names(jcr, os_xattr_namespaces[a], &xlist, &xlen);
+      switch (rc){
+         case bRC_XACL_ok:
+            /* it's ok, so go further */
+            break;
+         case bRC_XACL_skip:
+         case bRC_XACL_cont:
+            /* no xattr available, so skip rest of it */
+            return bRC_XACL_ok;
+         default:
+            return rc;
+      }
+
+      /* get a string representation of the namespace */
+      if (extattr_namespace_to_string(os_xattr_namespaces[a], &namespace_str) != 0){
+         Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"), os_xattr_namespaces[a], jcr->last_fname);
+         Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n", os_xattr_namespaces[a], jcr->last_fname);
+         goto bail_out;
+      }
+      namespace_len = strlen(namespace_str);
+
+      /* follow the list of xattr names and get the values */
+      for (name = xlist; (name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){
+         name_len = strlen(name);
+         name_gen = get_pool_memory(PM_FNAME);
+         name_gen = check_pool_memory_size(name_gen, name_len + namespace_len + 2);
+         bsnprintf(name_gen, name_len + namespace_len + 2, "%s.%s", namespace_str, name);
+         name_gen_len = strlen(name_gen);
+
+         skip =  check_xattr_skiplists(jcr, ff_pkt, name_gen);
+         if (skip || name_len == 0){
+            Dmsg1(100, "Skipping xattr named %s\n", name_gen);
+            continue;
+         }
+
+         /* value is allocated as POOLMEM by os_get_xattr_value */
+         rc = os_get_xattr_value(jcr, os_xattr_namespaces[a], name, &value, &value_len);
+         switch (rc){
+            case bRC_XACL_ok:
+               /* it's ok, so go further */
+               break;
+            case bRC_XACL_skip:
+               /* no xattr available, so skip rest of it */
+               rc = bRC_XACL_ok;
+               goto bail_out;
+            default:
+               /* error / fatal */
+               goto bail_out;
+         }
+
+         /*
+          * we have a name of the extended attribute in the name variable
+          * and value of the extended attribute in the value variable
+          * so we need to build a list
+          */
+         xattr = (XACL_xattr*)malloc(sizeof(XACL_xattr));
+         xattr->name_len = name_gen_len;
+         xattr->name = name_gen;
+         xattr->value_len = value_len;
+         xattr->value = value;
+         /*       magic              name_len          name        value_len       value */
+         len += sizeof(uint32_t) + sizeof(uint32_t) + name_gen_len + sizeof(uint32_t) + value_len;
+
+         if (xattr_list == NULL){
+            xattr_list = New(alist(10, not_owned_by_alist));
+         }
+         xattr_list->append(xattr);
+         xattr_count++;
+      }
+      if (xattr_count > 0){
+         /* serialize the stream */
+         rc = serialize_xattr_stream(jcr, len, xattr_list);
+         if (rc != bRC_XACL_ok){
+            Mmsg(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), jcr->last_fname);
+            Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", jcr->last_fname);
+            goto bail_out;
+         } else {
+            /* send data to SD */
+            rc = send_xattr_stream(jcr, STREAM_XACL_FREEBSD_XATTR);
+         }
+      } else {
+         rc = bRC_XACL_ok;
+      }
+   }
+bail_out:
+   /* free allocated data */
+   if (xattr_list != NULL){
+      foreach_alist(xattr, xattr_list){
+         if (xattr == NULL){
+            break;
+         }
+         if (xattr->name){
+            free_pool_memory(name_gen);
+         }
+         if (xattr->value){
+            free(xattr->value);
+         }
+         free(xattr);
+      }
+      delete xattr_list;
+   }
+   if (xlist != NULL){
+      free(xlist);
+   }
+
+   return rc;
+};
+
+/*
+ * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS.
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_FreeBSD::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){
+   return generic_restore_xattr(jcr, stream);
+};
+
+/*
+ * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_FreeBSD::os_get_acl(JCR *jcr, XACL_type xacltype){
+
+   acl_t acl;
+   acl_type_t acltype;
+   char *acltext;
+   bRC_XACL rc = bRC_XACL_ok;
+
+   acltype = get_acltype(xacltype);
+   acl = acl_get_file(jcr->last_fname, acltype);
+
+   if (acl){
+      Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
+      if (acl_nrentries(acl) == 0){
+         goto bail_out;
+      }
+
+      /* check for fimple ACL which correspond to standard permissions only */
+      if (xacltype == XACL_TYPE_ACCESS && acl_issimple(acl)){
+         goto bail_out;
+      }
+
+#if defined(_PC_ACL_NFS4)
+      if (xacltype == XACL_TYPE_NFS4){
+         int trivial;
+         if (acl_is_trivial_np(acl, &trivial) == 0){
+            if (trivial == 1){
+               goto bail_out;
+            }
+         }
+      }
+#endif
+
+      if ((acltext = acl_to_text(acl, NULL)) != NULL){
+         set_content(acltext);
+         acl_free(acl);
+         acl_free(acltext);
+         return bRC_XACL_ok;
+      }
+
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+
+      rc = bRC_XACL_error;
+
+   } else {
+      berrno be;
+
+      switch (errno){
+      case EOPNOTSUPP:
+         /* fs does not support acl, skip it */
+         Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
+         clear_flag(XACL_FLAG_NATIVE);
+         break;
+      case ENOENT:
+         break;
+      default:
+         /* Some real error */
+         Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         rc = bRC_XACL_error;
+         break;
+      }
+   }
+
+bail_out:
+   if (acl){
+      acl_free(acl);
+   }
+   /*
+    * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
+    * but it is working, hehe :)
+    * you may ask why it is working? it is simple, a pm_strcpy function is handling
+    * a null pointer with a substitiution of empty string.
+    */
+   set_content(NULL);
+   return rc;
+};
+
+/*
+ * Low level OS specyfic runtime to set ACL data on file
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_FreeBSD::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length){
+
+   acl_t acl;
+   acl_type_t acltype;
+
+   acltype = get_acltype(xacltype);
+   if (acltype == ACL_TYPE_DEFAULT && length == 0){
+      /* delete ACl from file when no acl data available for default acl's */
+      if (acl_delete_def_file(jcr->last_fname) == 0){
+         return bRC_XACL_ok;
+      }
+
+      berrno be;
+      switch (errno){
+         case ENOENT:
+            return bRC_XACL_ok;
+         case ENOTSUP:
+            /*
+             * If the filesystem reports it doesn't support acl's we clear the
+             * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files
+             * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
+             * when we change from one filesystem to an other.
+             */
+            clear_flag(XACL_FLAG_NATIVE);
+            Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
+            return bRC_XACL_error;
+         default:
+            Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+            return bRC_XACL_error;
+      }
+   }
+
+   acl = acl_from_text(content);
+   if (acl == NULL){
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+      return bRC_XACL_error;
+   }
+
+   /*
+    * Restore the ACLs, but don't complain about links which really should
+    * not have attributes, and the file it is linked to may not yet be restored.
+    * This is only true for the old acl streams as in the new implementation we
+    * don't save acls of symlinks (which cannot have acls anyhow)
+    */
+   if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
+      berrno be;
+      switch (errno){
+      case ENOENT:
+         acl_free(acl);
+         return bRC_XACL_ok;
+      case ENOTSUP:
+         /*
+          * If the filesystem reports it doesn't support ACLs we clear the
+          * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files
+          * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
+          * when we change from one filesystem to an other.
+          */
+         clear_flag(XACL_FLAG_NATIVE);
+         Mmsg(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
+         Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
+         acl_free(acl);
+         return bRC_XACL_error;
+      default:
+         Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+         acl_free(acl);
+         return bRC_XACL_error;
+      }
+   }
+   acl_free(acl);
+   return bRC_XACL_ok;
+};
+
+/*
+ * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
+ * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
+ * when not needed.
+ *
+ * in/out - check API at xacl.h
+ *
+ * As a FreeBSD uses a different attributes name schema/format then this method is a very different
+ * from a standard generic method because it uses a namespace (ns) value for os dependent optimization.
+ */
+bRC_XACL XACL_FreeBSD::os_get_xattr_names (JCR *jcr, int ns, POOLMEM ** pxlist, uint32_t * xlen){
+
+   int len;
+   POOLMEM * list;
+   int a;
+   int stra;
+   POOLMEM * genlist;
+
+   /* check input data */
+   if (jcr == NULL || xlen == NULL || pxlist == NULL){
+      return bRC_XACL_inval;
+   }
+   /* get the length of the extended attributes */
+   len = extattr_list_link(jcr->last_fname, ns, NULL, 0);
+   switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+            case ENOENT:
+               /* no file available, skip it */
+               return bRC_XACL_skip;
+            case EOPNOTSUPP:
+               /* no xattr supported on filesystem, clear a flag and skip it */
+               clear_flag(XACL_FLAG_NATIVE);
+               set_content(NULL);
+               return bRC_XACL_skip;
+            case EPERM:
+               if (ns == EXTATTR_NAMESPACE_SYSTEM){
+                  return bRC_XACL_cont;
+               } /* else show error */
+            default:
+               Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+               Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+               return bRC_XACL_error;
+         }
+         break;
+      }
+      case 0:
+         /* xattr available but empty, skip it */
+         return bRC_XACL_skip;
+      default:
+         break;
+   }
+
+   /*
+    * allocate memory for the extented attribute list
+    * default size is a 4k for PM_BSOCK, which should be sufficient on almost all
+    * Linux system where xattrs a limited in size to single filesystem block ~4kB
+    * so we need to check required size
+    */
+   list = get_pool_memory(PM_BSOCK);
+   list = check_pool_memory_size(list, len + 1);
+   memset(list, 0, len + 1);
+
+   /* get the list of extended attributes names for a file */
+   len = extattr_list_link(jcr->last_fname, ns, list, len);
+   switch (len){
+   case -1: {
+      berrno be;
+
+      switch (errno){
+      case ENOENT:
+         /* no file available, skip it, first release allocated memory */
+         free_pool_memory(list);
+         return bRC_XACL_skip;
+         case EPERM:
+            if (ns == EXTATTR_NAMESPACE_SYSTEM){
+               return bRC_XACL_cont;
+            } /* else show error */
+      default:
+         Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         free_pool_memory(list);
+         return bRC_XACL_error;
+      }
+      break;
+   }
+   default:
+      break;
+   }
+   /* convert FreeBSD list type to the generic one */
+   genlist = get_pool_memory(PM_BSOCK);
+   genlist = check_pool_memory_size(genlist, len + 1);
+   memset(genlist, 0, len + 1);
+   for (a = 0; a < len; a += list[a] + 1){
+      stra = list[a];
+      memcpy(genlist + a, list + a + 1, stra);
+      genlist[a + stra] = '\0';
+   }
+   free_pool_memory(list);
+   /* setup return data */
+   *pxlist = genlist;
+   *xlen = len;
+   return bRC_XACL_ok;
+};
+
+/*
+ * Return a value of the requested attribute name and a length of the allocated buffer.
+ * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
+ * when not needed.
+ *
+ * in/out - check API at xacl.h
+ *
+ * As a FreeBSD uses a different attributes name schema/format then this method is a very different
+ * from a standard generic method because it uses a namespace (ns) value for os dependent optimization.
+ */
+bRC_XACL XACL_FreeBSD::os_get_xattr_value (JCR *jcr, int ns, char * name, char ** pvalue, uint32_t * plen){
+
+   int len;
+   POOLMEM * value;
+
+   /* check input data */
+   if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){
+      return bRC_XACL_inval;
+   }
+   /* get the length of the value for extended attribute */
+   len = extattr_get_link(jcr->last_fname, ns, name, NULL, 0);
+   switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+            case ENOENT:
+               /* no file available, skip it */
+               return bRC_XACL_skip;
+            default:
+               /* XXX: what about ENOATTR error value? */
+               Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+               Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+               return bRC_XACL_error;
+         }
+         break;
+      }
+      default:
+         break;
+   }
+
+   if (len > 0){
+      /*
+       * allocate memory for the extented attribute value
+       * default size is a 256B for PM_MESSAGE, so we need to check required size
+       */
+      value = get_pool_memory(PM_MESSAGE);
+      value = check_pool_memory_size(value, len + 1);
+      memset(value, 0, len + 1);
+      /* value is not empty, get a data */
+      len = extattr_get_link(jcr->last_fname, ns, name, value, len);
+      switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+         case ENOENT:
+            /* no file available, skip it, first release allocated memory */
+            free_pool_memory(value);
+            return bRC_XACL_skip;
+         default:
+            Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+            Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+            free_pool_memory(value);
+            return bRC_XACL_error;
+         }
+         break;
+      }
+      default:
+         break;
+      }
+      /* ensure a value is nul terminated */
+      value[len] = '\0';
+   } else {
+      /* empty value */
+      value = NULL;
+      len = 0;
+   }
+   /* setup return data */
+   *pvalue = value;
+   *plen = len;
+   return bRC_XACL_ok;
+};
+
+/*
+ * Low level OS specyfic runtime to set extended attribute on file
+ *
+ * in/out - check API at xacl.h
+ *
+ * xattr->name should be in '<namespace>.<name>' format which
+ * function handle without problem, otherwise it returns an error
+ * TODO: it is possible to handle a different attributes name format
+ * for os portability where default namespace 'user' can be used
+ */
+bRC_XACL XACL_FreeBSD::os_set_xattr (JCR *jcr, XACL_xattr *xattr){
+
+   char * name;
+   char * nspace;
+   int ns;
+   int rc;
+
+   /* check input data */
+   if (jcr == NULL || xattr == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* search for attribute namespace which is distinguished from attribute name by a dot '.' character */
+   if ((name = strchr(xattr->name, '.')) == (char *)NULL){
+      Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"), xattr->name, jcr->last_fname);
+      Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n", xattr->name, jcr->last_fname);
+      return bRC_XACL_error;
+   }
+
+   /* split namespace and name of the attribute */
+   nspace = xattr->name;
+   *name++ = '\0';
+
+   /* check if namespace is valid on this system */
+   if (extattr_string_to_namespace(nspace, &ns) != 0){
+      Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"), nspace, jcr->last_fname);
+      Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n", nspace, jcr->last_fname);
+      return bRC_XACL_error;
+   }
+
+   /* set extattr on file */
+   rc = extattr_set_link(jcr->last_fname, ns, name, xattr->value, xattr->value_len);
+   if (rc < 0 || rc != (int)xattr->value_len){
+      berrno be;
+
+      switch (errno){
+      case ENOENT:
+         break;
+      default:
+         Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         return bRC_XACL_error;
+      }
+   }
+   return bRC_XACL_ok;
+};
+
+#endif /* HAVE_FREEBSD_OS */
diff --git a/bacula/src/filed/xacl_freebsd.h b/bacula/src/filed/xacl_freebsd.h
new file mode 100644 (file)
index 0000000..b3e1d65
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#ifndef __XACL_FreeBSD_H_
+#define __XACL_FreeBSD_H_
+
+#if defined(HAVE_FREEBSD_OS)
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#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 <sys/types.h>
+#include <sys/extattr.h>
+#else
+#error "Missing sys/extattr.h header file"
+#endif
+
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+
+#if !defined(HAVE_EXTATTR_GET_LINK) && defined(HAVE_EXTATTR_GET_FILE)
+#define extattr_get_link extattr_get_file
+#endif
+#if !defined(HAVE_EXTATTR_SET_LINK) && defined(HAVE_EXTATTR_SET_FILE)
+#define extattr_set_link extattr_set_file
+#endif
+#if !defined(HAVE_EXTATTR_LIST_LINK) && defined(HAVE_EXTATTR_LIST_FILE)
+#define extattr_list_link extattr_list_file
+#endif
+
+/*
+ *
+ *
+ */
+class XACL_FreeBSD : public XACL {
+private:
+   bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_get_acl(JCR *jcr, XACL_type xacltype);
+   bRC_XACL os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length);
+   bRC_XACL os_get_xattr_names (JCR *jcr, const int ns, POOLMEM **list, uint32_t *length);
+   bRC_XACL os_get_xattr_value (JCR *jcr, const int ns, char * name, char ** pvalue, uint32_t * plen);
+   bRC_XACL os_set_xattr (JCR *jcr, XACL_xattr *xattr);
+   /* requires acl.h available */
+   bool acl_issimple(acl_t acl);
+   acl_type_t get_acltype(XACL_type xacltype);
+   int acl_nrentries(acl_t acl);
+   bRC_XACL check_xacltype (JCR *jcr, int name);
+public:
+   XACL_FreeBSD ();
+};
+
+#endif /* HAVE_FREEBSD_OS */
+
+#endif /* __XACL_FreeBSD_H_ */
diff --git a/bacula/src/filed/xacl_linux.c b/bacula/src/filed/xacl_linux.c
new file mode 100644 (file)
index 0000000..409d6b6
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#include "bacula.h"
+#include "filed.h"
+#include "xacl_linux.h"
+
+#if defined(HAVE_LINUX_OS)
+/*
+ * Define the supported ACL streams for this OS
+ */
+static const int os_acl_streams[] = {
+   STREAM_XACL_LINUX_ACCESS,
+   0
+};
+
+static const int os_default_acl_streams[] = {
+   STREAM_XACL_LINUX_DEFAULT,
+   0
+};
+
+/*
+ * Define the supported XATTR streams for this OS
+ */
+static const int os_xattr_streams[] = {
+   STREAM_XACL_LINUX_XATTR,
+   0
+};
+
+static const char *os_xattr_acl_skiplist[] = {
+   "system.posix_acl_access",
+   "system.posix_acl_default",
+   NULL
+};
+
+static const char *os_xattr_skiplist[] = {
+   NULL
+};
+
+/*
+ * OS Specyfic constructor
+ */
+XACL_Linux::XACL_Linux(){
+   set_acl_streams(os_acl_streams, os_default_acl_streams);
+   set_xattr_streams(os_xattr_streams);
+   set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
+};
+
+/*
+ * Translates Bacula internal acl representation into
+ * acl type
+ *
+ * in:
+ *    xacltype - internal Bacula acl type (XACL_type)
+ * out:
+ *    acl_type_t - os dependent acl type
+ *    when failed - ACL_TYPE_NONE is returned
+ */
+acl_type_t XACL_Linux::get_acltype(XACL_type xacltype){
+
+   acl_type_t acltype;
+
+   switch (xacltype){
+      case XACL_TYPE_ACCESS:
+         acltype = ACL_TYPE_ACCESS;
+         break;
+      case XACL_TYPE_DEFAULT:
+         acltype = ACL_TYPE_DEFAULT;
+         break;
+      default:
+         /*
+          * sanity check for acl's not supported by OS
+          */
+         acltype = (acl_type_t)ACL_TYPE_NONE;
+         break;
+   }
+   return acltype;
+};
+
+/*
+ * Counts a number of acl entries
+ *
+ * in:
+ *    acl - acl object
+ * out:
+ *    int - number of entries in acl object
+ *    when no acl entry available or any error then return zero '0'
+ */
+int XACL_Linux::acl_nrentries(acl_t acl){
+
+   int nr = 0;
+   acl_entry_t aclentry;
+   int rc;
+
+   rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
+   while (rc == 1){
+      nr++;
+      rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
+   }
+
+   return nr;
+};
+
+/*
+ * Checks if acl is simple.
+ *
+ * acl is simple if it has only the following entries:
+ * "user::",
+ * "group::",
+ * "other::"
+ *
+ * in:
+ *    acl - acl object
+ * out:
+ *    true - when acl object is simple
+ *    false - when acl object is not simple
+ */
+bool XACL_Linux::acl_issimple(acl_t acl){
+
+   acl_entry_t aclentry;
+   acl_tag_t acltag;
+   int rc;
+
+   rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
+   while (rc == 1){
+      if (acl_get_tag_type(aclentry, &acltag) < 0){
+         return true;
+      }
+      /*
+       * Check for ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER to find out.
+       */
+      if (acltag != ACL_USER_OBJ &&
+          acltag != ACL_GROUP_OBJ &&
+          acltag != ACL_OTHER){
+         return false;
+      }
+      rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
+   }
+   return true;
+};
+
+/*
+ * Perform OS specyfic ACL backup
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
+   return generic_backup_acl(jcr, ff_pkt);
+};
+
+/*
+ * Perform OS specyfic ACL restore
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
+   return generic_restore_acl(jcr, stream);
+};
+
+/*
+ * Perform OS specyfic extended attribute backup
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
+   return generic_backup_xattr(jcr, ff_pkt);
+};
+
+/*
+ * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS.
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){
+   return generic_restore_xattr(jcr, stream);
+};
+
+/*
+ * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer.
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_get_acl(JCR *jcr, XACL_type xacltype){
+
+   acl_t acl;
+   acl_type_t acltype;
+   char *acltext;
+   bRC_XACL rc = bRC_XACL_ok;
+
+   /* check input data */
+   if (jcr == NULL){
+      return bRC_XACL_inval;
+   }
+
+   acltype = get_acltype(xacltype);
+   acl = acl_get_file(jcr->last_fname, acltype);
+
+   if (acl){
+      Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
+      if (acl_nrentries(acl) == 0){
+         goto bail_out;
+      }
+
+      /* check for fimple ACL which correspond to standard permissions only */
+      if (xacltype == XACL_TYPE_ACCESS && acl_issimple(acl)){
+         goto bail_out;
+      }
+
+      if ((acltext = acl_to_text(acl, NULL)) != NULL){
+         set_content(acltext);
+         acl_free(acl);
+         acl_free(acltext);
+         return bRC_XACL_ok;
+      }
+
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+
+      rc = bRC_XACL_error;
+   } else {
+      berrno be;
+
+      switch (errno){
+      case EOPNOTSUPP:
+         /* fs does not support acl, skip it */
+         Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
+         clear_flag(XACL_FLAG_NATIVE);
+         break;
+      case ENOENT:
+         break;
+      default:
+         /* Some real error */
+         Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         rc = bRC_XACL_error;
+         break;
+      }
+   }
+
+bail_out:
+   if (acl){
+      acl_free(acl);
+   }
+   /*
+    * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
+    * but it is working, hehe :)
+    * you may ask why it is working? it is simple, a pm_strcpy function is handling
+    * a null pointer with a substitiution of empty string.
+    */
+   set_content(NULL);
+   return rc;
+};
+
+/*
+ * Low level OS specyfic runtime to set ACL data on file
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length){
+
+   acl_t acl;
+   acl_type_t acltype;
+
+   /* check input data */
+   if (jcr == NULL || content == NULL){
+      return bRC_XACL_inval;
+   }
+
+   acl = acl_from_text(content);
+   if (acl == NULL){
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+      return bRC_XACL_error;
+   }
+
+   if (acl_valid(acl) != 0){
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("acl_valid error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+      acl_free(acl);
+      return bRC_XACL_error;
+   }
+
+   /* handle different acl types for Linux */
+   acltype = get_acltype(xacltype);
+   if (acltype == ACL_TYPE_DEFAULT && length == 0){
+      /* delete ACl from file when no acl data available for default acl's */
+      if (acl_delete_def_file(jcr->last_fname) == 0){
+         return bRC_XACL_ok;
+      }
+
+      berrno be;
+      switch (errno){
+         case ENOENT:
+            return bRC_XACL_ok;
+         case ENOTSUP:
+            /*
+             * If the filesystem reports it doesn't support acl's we clear the
+             * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files
+             * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
+             * when we change from one filesystem to an other.
+             */
+            clear_flag(XACL_FLAG_NATIVE);
+            Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
+            return bRC_XACL_error;
+         default:
+            Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+            return bRC_XACL_error;
+      }
+   }
+
+   /*
+    * Restore the ACLs, but don't complain about links which really should
+    * not have attributes, and the file it is linked to may not yet be restored.
+    * This is only true for the old acl streams as in the new implementation we
+    * don't save acls of symlinks (which cannot have acls anyhow)
+    */
+   if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
+      berrno be;
+      switch (errno){
+      case ENOENT:
+         acl_free(acl);
+         return bRC_XACL_ok;
+      case ENOTSUP:
+         /*
+          * If the filesystem reports it doesn't support ACLs we clear the
+          * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files
+          * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
+          * when we change from one filesystem to an other.
+          */
+         clear_flag(XACL_FLAG_NATIVE);
+         Mmsg(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
+         Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
+         acl_free(acl);
+         return bRC_XACL_error;
+      default:
+         Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+         acl_free(acl);
+         return bRC_XACL_error;
+      }
+   }
+   acl_free(acl);
+   return bRC_XACL_ok;
+};
+
+/*
+ * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
+ * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
+ * when not needed.
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){
+
+   int len;
+   POOLMEM * list;
+
+   /* check input data */
+   if (jcr == NULL || xlen == NULL || pxlist == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* get the length of the extended attributes */
+   len = llistxattr(jcr->last_fname, NULL, 0);
+   switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+            case ENOENT:
+               /* no file available, skip it */
+               return bRC_XACL_skip;
+            case EOPNOTSUPP:
+               /* no xattr supported on filesystem, clear a flag and skip it */
+               clear_flag(XACL_FLAG_NATIVE);
+               set_content(NULL);
+               return bRC_XACL_skip;
+            default:
+               Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+               Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+               return bRC_XACL_error;
+         }
+         break;
+      }
+      case 0:
+         /* xattr available but empty, skip it */
+         return bRC_XACL_skip;
+      default:
+         break;
+   }
+
+   /*
+    * allocate memory for the extented attribute list
+    * default size is a 4k for PM_BSOCK, which should be sufficient on almost all
+    * Linux system where xattrs a limited in size to single filesystem block ~4kB
+    * so we need to check required size
+    */
+   list = get_pool_memory(PM_BSOCK);
+   list = check_pool_memory_size(list, len + 1);
+   memset(list, 0, len + 1);
+
+   /* get the list of extended attributes names for a file */
+   len = llistxattr(jcr->last_fname, list, len);
+   switch (len){
+   case -1: {
+      berrno be;
+
+      switch (errno){
+      case ENOENT:
+         /* no file available, skip it, first release allocated memory */
+         free_pool_memory(list);
+         return bRC_XACL_skip;
+      default:
+         Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         free_pool_memory(list);
+         return bRC_XACL_error;
+      }
+      break;
+   }
+   default:
+      break;
+   }
+   /* ensure a list is nul terminated */
+   list[len] = '\0';
+   /* setup return data */
+   *pxlist = list;
+   *xlen = len;
+   return bRC_XACL_ok;
+};
+
+/*
+ * Return a value of the requested attribute name and a length of the allocated buffer.
+ * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
+ * when not needed.
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){
+
+   int len;
+   POOLMEM * value;
+
+   /* check input data */
+   if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* get the length of the value for extended attribute */
+   len = lgetxattr(jcr->last_fname, name, NULL, 0);
+   switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+            case ENOENT:
+               /* no file available, skip it */
+               return bRC_XACL_skip;
+            default:
+               /* XXX: what about ENOATTR error value? */
+               Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+               Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+               return bRC_XACL_error;
+         }
+         break;
+      }
+      default:
+         break;
+   }
+
+   if (len > 0){
+      /*
+       * allocate memory for the extented attribute value
+       * default size is a 256B for PM_MESSAGE, so we need to check required size
+       */
+      value = get_pool_memory(PM_MESSAGE);
+      value = check_pool_memory_size(value, len + 1);
+      memset(value, 0, len + 1);
+      /* value is not empty, get a data */
+      len = lgetxattr(jcr->last_fname, name, value, len);
+      switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+         case ENOENT:
+            /* no file available, skip it, first release allocated memory */
+            free_pool_memory(value);
+            return bRC_XACL_skip;
+         default:
+            Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+            Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+            free_pool_memory(value);
+            return bRC_XACL_error;
+         }
+         break;
+      }
+      default:
+         break;
+      }
+      /* ensure a value is nul terminated */
+      value[len] = '\0';
+   } else {
+      /* empty value */
+      value = NULL;
+      len = 0;
+   }
+   /* setup return data */
+   *pvalue = value;
+   *plen = len;
+   return bRC_XACL_ok;
+};
+
+/*
+ * Low level OS specyfic runtime to set extended attribute on file
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Linux::os_set_xattr (JCR *jcr, XACL_xattr *xattr){
+
+   /* check input data */
+   if (jcr == NULL || xattr == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* set extattr on file */
+   if (lsetxattr(jcr->last_fname, xattr->name, xattr->value, xattr->value_len, 0) != 0){
+      berrno be;
+
+      switch (errno){
+      case ENOENT:
+         break;
+      case ENOTSUP:
+         /*
+          * If the filesystem reports it doesn't support XATTR we clear the
+          * XACL_FLAG_NATIVE flag so we skip XATTR restores on all other files
+          * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
+          * when we change from one filesystem to an other.
+          */
+         clear_flag(XACL_FLAG_NATIVE);
+         Mmsg(jcr->errmsg, _("setxattr error on file \"%s\": filesystem doesn't support XATTR\n"), jcr->last_fname);
+         Dmsg3(100, "setxattr error name=%s value=%s file=%s filesystem doesn't support XATTR\n", xattr->name, xattr->value, jcr->last_fname);
+         break;
+      default:
+         Mmsg2(jcr->errmsg, _("setxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "setxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         return bRC_XACL_error;
+      }
+   }
+   return bRC_XACL_ok;
+};
+
+#endif /* HAVE_LINUX_OS */
diff --git a/bacula/src/filed/xacl_linux.h b/bacula/src/filed/xacl_linux.h
new file mode 100644 (file)
index 0000000..4f835ca
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#ifndef __XACL_LINUX_H_
+#define __XACL_LINUX_H_
+
+#if defined(HAVE_LINUX_OS)
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#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 <sys/xattr.h>
+#else
+#error "Missing sys/xattr.h header file"
+#endif
+
+/*
+ *
+ *
+ */
+class XACL_Linux : public XACL {
+private:
+   bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_get_acl(JCR *jcr, XACL_type xacltype);
+   bRC_XACL os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length);
+   bRC_XACL os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length);
+   bRC_XACL os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen);
+   bRC_XACL os_set_xattr (JCR *jcr, FF_PKT *ff_pkt, char *list, uint32_t length);
+   bRC_XACL os_set_xattr (JCR *jcr, XACL_xattr *xattr);
+   acl_type_t get_acltype(XACL_type xacltype);
+   int acl_nrentries(acl_t acl);
+   bool acl_issimple(acl_t acl);
+public:
+   XACL_Linux ();
+};
+
+#endif /* HAVE_LINUX_OS */
+
+#endif /* __XACL_LINUX_H_ */
diff --git a/bacula/src/filed/xacl_osx.c b/bacula/src/filed/xacl_osx.c
new file mode 100644 (file)
index 0000000..ef2fc9a
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#include "bacula.h"
+#include "filed.h"
+#include "xacl_osx.h"
+
+#if defined(HAVE_DARWIN_OS)
+/*
+ * Define the supported ACL streams for this OS
+ */
+static const int os_acl_streams[] = {
+   STREAM_XACL_DARWIN_ACCESS,
+   0
+};
+
+static const int os_default_acl_streams[] = {
+   0
+};
+
+/*
+ * Define the supported XATTR streams for this OS
+ */
+static const int os_xattr_streams[] = {
+   STREAM_XACL_DARWIN_XATTR,
+   0
+};
+
+static const char *os_xattr_acl_skiplist[] = {
+   "com.apple.system.Security",
+   NULL
+};
+
+static const char *os_xattr_skiplist[] = {
+   "com.apple.system.extendedsecurity",
+   "com.apple.ResourceFork",
+   NULL
+};
+
+/*
+ * OS Specyfic constructor
+ */
+XACL_OSX::XACL_OSX(){
+
+   set_acl_streams(os_acl_streams, os_default_acl_streams);
+   set_xattr_streams(os_xattr_streams);
+   set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
+};
+
+/*
+ * Translates Bacula internal acl representation into
+ * acl type
+ *
+ * in:
+ *    xacltype - internal Bacula acl type (XACL_type)
+ * out:
+ *    acl_type_t - os dependent acl type
+ *    when failed - ACL_TYPE_NONE is returned
+ */
+acl_type_t XACL_OSX::get_acltype(XACL_type xacltype){
+
+   acl_type_t acltype;
+
+   switch (xacltype){
+      case XACL_TYPE_ACCESS:
+         acltype = ACL_TYPE_ACCESS;
+         break;
+   #ifdef HAVE_ACL_TYPE_EXTENDED
+      case XACL_TYPE_EXTENDED:
+         acltype = ACL_TYPE_EXTENDED;
+         break;
+   #endif
+      default:
+         /*
+          * sanity check for acl's not supported by OS
+          */
+         acltype = (acl_type_t)ACL_TYPE_NONE;
+         break;
+   }
+   return acltype;
+};
+
+/*
+ * Counts a number of acl entries
+ *
+ * in:
+ *    acl - acl object
+ * out:
+ *    int - number of entries in acl object
+ *    when no acl entry available or any error then return zero '0'
+ */
+int XACL_OSX::acl_nrentries(acl_t acl){
+
+   int nr = 0;
+   acl_entry_t entry;
+   int rc;
+
+   rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
+   while (rc == 0){
+      nr++;
+      rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
+   }
+
+   return nr;
+};
+
+/*
+ * Perform OS specyfic ACL backup
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
+
+   /* check input data */
+   if (jcr == NULL || ff_pkt == NULL){
+      return bRC_XACL_inval;
+   }
+
+#if defined(HAVE_ACL_TYPE_EXTENDED)
+   /*
+    * Use XACL_TYPE_EXTENDED only when available
+    */
+   Dmsg0(400, "MacOSX Extended ACL computed\n");
+   if (os_get_acl(jcr, XACL_TYPE_EXTENDED) == bRC_XACL_fatal){
+      return bRC_XACL_fatal;
+   }
+#else
+   Dmsg0(400, "MacOSX standard ACL computed\n");
+   if (os_get_acl(jcr, XACL_TYPE_ACCESS) == bRC_XACL_fatal){
+      return bRC_XACL_fatal;
+   }
+#endif
+
+   return send_acl_stream(jcr, STREAM_XACL_DARWIN_ACCESS);
+};
+
+/*
+ * Perform OS specyfic ACL restore
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
+
+#if defined(HAVE_ACL_TYPE_EXTENDED)
+      return os_set_acl(jcr, XACL_TYPE_EXTENDED, content, length);
+#else
+      return os_set_acl(jcr, XACL_TYPE_ACCESS, content, length);
+#endif
+};
+
+/*
+ * Perform OS specyfic extended attribute backup
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
+   return generic_backup_xattr(jcr, ff_pkt);
+};
+
+/*
+ * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS.
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){
+   return generic_restore_xattr(jcr, stream);
+};
+
+/*
+ * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_get_acl(JCR *jcr, XACL_type xacltype){
+
+   acl_t acl;
+   acl_type_t acltype;
+   char *acltext;
+   bRC_XACL rc = bRC_XACL_ok;
+
+   /* check input data */
+   if (jcr == NULL){
+      return bRC_XACL_inval;
+   }
+
+   acltype = get_acltype(xacltype);
+   acl = acl_get_file(jcr->last_fname, acltype);
+
+   if (acl){
+      Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
+      if (acl_nrentries(acl) == 0){
+         goto bail_out;
+      }
+
+      if ((acltext = acl_to_text(acl, NULL)) != NULL){
+         set_content(acltext);
+         acl_free(acl);
+         acl_free(acltext);
+         return bRC_XACL_ok;
+      }
+
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+
+      rc = bRC_XACL_error;
+   } else {
+      berrno be;
+
+      switch (errno){
+      case EOPNOTSUPP:
+         /* fs does not support acl, skip it */
+         Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
+         clear_flag(XACL_FLAG_NATIVE);
+         break;
+      case ENOENT:
+         break;
+      default:
+         /* Some real error */
+         Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         rc = bRC_XACL_error;
+         break;
+      }
+   }
+
+bail_out:
+   if (acl){
+      acl_free(acl);
+   }
+   /*
+    * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
+    * but it is working, hehe :)
+    * you may ask why it is working? it is simple, a pm_strcpy function is handling
+    * a null pointer with a substitiution of empty string.
+    */
+   set_content(NULL);
+   return rc;
+};
+
+/*
+ * Low level OS specyfic runtime to set ACL data on file
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length){
+
+   acl_t acl;
+   acl_type_t acltype;
+
+   /* check input data */
+   if (jcr == NULL || content == NULL){
+      return bRC_XACL_inval;
+   }
+
+   acl = acl_from_text(content);
+   if (acl == NULL){
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+      return bRC_XACL_error;
+   }
+
+   acltype = get_acltype(xacltype);
+
+   /*
+    * Restore the ACLs, but don't complain about links which really should
+    * not have attributes, and the file it is linked to may not yet be restored.
+    * This is only true for the old acl streams as in the new implementation we
+    * don't save acls of symlinks (which cannot have acls anyhow)
+    */
+   if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
+      berrno be;
+      switch (errno){
+      case ENOENT:
+         acl_free(acl);
+         return bRC_XACL_ok;
+      case EOPNOTSUPP:
+         /*
+          * If the filesystem reports it doesn't support ACLs we clear the
+          * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files
+          * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
+          * when we change from one filesystem to an other.
+          */
+         clear_flag(XACL_FLAG_NATIVE);
+         Mmsg1(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
+         Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
+         acl_free(acl);
+         return bRC_XACL_error;
+      default:
+         Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+         acl_free(acl);
+         return bRC_XACL_error;
+      }
+   }
+   acl_free(acl);
+   return bRC_XACL_ok;
+};
+
+/*
+ * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
+ * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
+ * when not needed.
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){
+
+   int len;
+   POOLMEM * list;
+
+   /* check input data */
+   if (jcr == NULL || xlen == NULL || pxlist == NULL){
+      return bRC_XACL_inval;
+   }
+   /* get the length of the extended attributes */
+   len = listxattr(jcr->last_fname, NULL, 0, XATTR_NOFOLLOW);
+   switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+            case ENOENT:
+               /* no file available, skip it */
+               return bRC_XACL_skip;
+            case ENOTSUP:
+               /* no xattr supported on filesystem, clear a flag and skip it */
+               clear_flag(XACL_FLAG_NATIVE);
+               set_content(NULL);
+               return bRC_XACL_skip;
+            default:
+               Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+               Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+               return bRC_XACL_error;
+         }
+         break;
+      }
+      case 0:
+         /* xattr available but empty, skip it */
+         return bRC_XACL_skip;
+      default:
+         break;
+   }
+
+   /*
+    * allocate memory for the extented attribute list
+    * default size is a 4k for PM_BSOCK, which should be sufficient on almost all
+    * Linux system where xattrs a limited in size to single filesystem block ~4kB
+    * so we need to check required size
+    */
+   list = get_pool_memory(PM_BSOCK);
+   list = check_pool_memory_size(list, len + 1);
+   memset(list, 0, len + 1);
+
+   /* get the list of extended attributes names for a file */
+   len = listxattr(jcr->last_fname, list, len, XATTR_NOFOLLOW);
+   switch (len){
+   case -1: {
+      berrno be;
+
+      switch (errno){
+      case ENOENT:
+         /* no file available, skip it, first release allocated memory */
+         free_pool_memory(list);
+         return bRC_XACL_skip;
+      default:
+         Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         free_pool_memory(list);
+         return bRC_XACL_error;
+      }
+      break;
+   }
+   default:
+      break;
+   }
+   /* ensure a list is nul terminated */
+   list[len] = '\0';
+   /* setup return data */
+   *pxlist = list;
+   *xlen = len;
+   return bRC_XACL_ok;
+};
+
+/*
+ * Return a value of the requested attribute name and a length of the allocated buffer.
+ * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
+ * when not needed.
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){
+
+   int len;
+   POOLMEM * value;
+
+   /* check input data */
+   if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* get the length of the value for extended attribute */
+   len = getxattr(jcr->last_fname, name, NULL, 0, 0, XATTR_NOFOLLOW);
+   switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+            case ENOENT:
+               /* no file available, skip it */
+               return bRC_XACL_skip;
+            default:
+               /* XXX: what about ENOATTR error value? */
+               Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+               Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+               return bRC_XACL_error;
+         }
+         break;
+      }
+      default:
+         break;
+   }
+
+   if (len > 0){
+      /*
+       * allocate memory for the extented attribute value
+       * default size is a 256B for PM_MESSAGE, so we need to check required size
+       */
+      value = get_pool_memory(PM_MESSAGE);
+      value = check_pool_memory_size(value, len + 1);
+      memset(value, 0, len + 1);
+      /* value is not empty, get a data */
+      len = getxattr(jcr->last_fname, name, value, len, 0, XATTR_NOFOLLOW);
+      switch (len){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+         case ENOENT:
+            /* no file available, skip it, first release allocated memory */
+            free_pool_memory(value);
+            return bRC_XACL_skip;
+         default:
+            Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+            Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+            free_pool_memory(value);
+            return bRC_XACL_error;
+         }
+         break;
+      }
+      default:
+         break;
+      }
+      /* ensure a value is nul terminated */
+      value[len] = '\0';
+   } else {
+      /* empty value */
+      value = NULL;
+      len = 0;
+   }
+   /* setup return data */
+   *pvalue = value;
+   *plen = len;
+   return bRC_XACL_ok;
+};
+
+/*
+ * Low level OS specyfic runtime to set extended attribute on file
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_OSX::os_set_xattr (JCR *jcr, XACL_xattr *xattr){
+
+   /* check input data */
+   if (jcr == NULL || xattr == NULL){
+      return bRC_XACL_inval;
+   }
+
+   /* set extattr on file */
+   if (setxattr(jcr->last_fname, xattr->name, xattr->value, xattr->value_len, 0, XATTR_NOFOLLOW) != 0){
+      berrno be;
+
+      switch (errno){
+      case ENOENT:
+         break;
+      case ENOTSUP:
+         /*
+          * If the filesystem reports it doesn't support XATTR we clear the
+          * XACL_FLAG_NATIVE flag so we skip XATTR restores on all other files
+          * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
+          * when we change from one filesystem to an other.
+          */
+         clear_flag(XACL_FLAG_NATIVE);
+         Mmsg1(jcr->errmsg, _("setxattr error on file \"%s\": filesystem doesn't support XATTR\n"), jcr->last_fname);
+         Dmsg3(100, "setxattr error name=%s value=%s file=%s filesystem doesn't support XATTR\n", xattr->name, xattr->value, jcr->last_fname);
+         break;
+      default:
+         Mmsg2(jcr->errmsg, _("setxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+         Dmsg2(100, "setxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+         return bRC_XACL_error;
+      }
+   }
+   return bRC_XACL_ok;
+};
+
+#endif /* HAVE_DARWIN_OS */
diff --git a/bacula/src/filed/xacl_osx.h b/bacula/src/filed/xacl_osx.h
new file mode 100644 (file)
index 0000000..c8d4b2a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#ifndef __XACL_OSX_H_
+#define __XACL_OSX_H_
+
+#if defined(HAVE_DARWIN_OS)
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#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 <sys/xattr.h>
+#else
+#error "Missing sys/xattr.h header file"
+#endif
+
+/*
+ *
+ *
+ */
+class XACL_OSX : public XACL {
+private:
+   bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_get_acl(JCR *jcr, XACL_type xacltype);
+   bRC_XACL os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length);
+   bRC_XACL os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length);
+   bRC_XACL os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen);
+   bRC_XACL os_set_xattr (JCR *jcr, XACL_xattr *xattr);
+   /* requires acl.h available */
+   acl_type_t get_acltype(XACL_type xacltype);
+   int acl_nrentries(acl_t acl);
+public:
+   XACL_OSX ();
+};
+
+#endif /* HAVE_DARWIN_OS */
+
+#endif /* __XACL_OSX_H_ */
diff --git a/bacula/src/filed/xacl_solaris.c b/bacula/src/filed/xacl_solaris.c
new file mode 100644 (file)
index 0000000..c1701d3
--- /dev/null
@@ -0,0 +1,1177 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#include "bacula.h"
+#include "filed.h"
+#include "xacl_solaris.h"
+
+#if defined(HAVE_SUN_OS)
+/*
+ * Define the supported ACL streams for this OS
+ */
+static const int os_acl_streams[] = {
+   STREAM_XACL_SOLARIS_POSIX,
+   STREAM_XACL_SOLARIS_NFS4,
+   0
+};
+
+static const int os_default_acl_streams[] = {
+   0
+};
+
+/*
+ * Define the supported XATTR streams for this OS
+ */
+static const int os_xattr_streams[] = {
+   STREAM_XACL_SOLARIS_XATTR,
+#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
+   STREAM_XACL_SOLARIS_SYS_XATTR,
+#endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
+   0
+};
+
+
+static const char *os_xattr_acl_skiplist[] = {
+   NULL
+};
+
+static const char *os_xattr_skiplist[] = {
+   "..",
+#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
+   VIEW_READONLY,
+#endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
+   NULL
+};
+
+/*
+ * OS Specyfic constructor
+ */
+XACL_Solaris::XACL_Solaris(){
+
+   set_acl_streams(os_acl_streams, os_default_acl_streams);
+   set_xattr_streams(os_xattr_streams);
+   set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
+   cache = NULL;
+};
+
+/*
+ * OS Specyfic destructor
+ */
+XACL_Solaris::~XACL_Solaris(){
+
+   delete_xattr_cache();
+};
+
+/*
+ * Checks if ACL's are available for a specified file
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    name - specifies the system variable to be queried
+ * out:
+ *    bRC_XACL_ok - check successful, lets setup xacltype variable
+ *    bRC_XACL_error -  in case of error
+ *    bRC_XACL_skip - you should skip all other routine
+ */
+bRC_XACL XACL_Solaris::check_xacltype (JCR *jcr, int name){
+
+   int rc = 0;
+
+   rc = pathconf(jcr->last_fname, name);
+   switch (rc){
+      case -1: {
+         /* some error check why */
+         berrno be;
+         if (errno == ENOENT){
+            /* file does not exist skip it */
+            return bRC_XACL_skip;
+         } else {
+            Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+            Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
+            return bRC_XACL_error;
+         }
+      }
+      case 0:
+         /* No support for ACLs */
+         clear_flag(XACL_FLAG_NATIVE);
+         set_content(NULL);
+         return bRC_XACL_skip;
+      default:
+         break;
+   }
+   return bRC_XACL_ok;
+};
+
+/*
+ * Perform OS specyfic ACL backup
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Solaris::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
+
+   bRC_XACL rc;
+   int stream;
+
+   /*
+    * See if filesystem supports acls.
+    */
+   rc = check_xacltype(jcr, _PC_ACL_ENABLED);
+   switch (rc){
+      case bRC_XACL_ok:
+         break;
+      case bRC_XACL_skip:
+         return bRC_XACL_ok;
+      default:
+         /* errors */
+         return rc;
+   }
+
+   rc = os_get_acl(jcr, &stream);
+   switch (rc){
+      case bRC_XACL_ok:
+         if (get_content_len() > 0){
+            if (send_acl_stream(jcr, stream) == bRC_XACL_fatal){
+               return bRC_XACL_fatal;
+            }
+         }
+         break;
+      default:
+         return rc;
+   }
+
+   return bRC_XACL_ok;
+};
+
+/*
+ * Perform OS specyfic ACL restore
+ *
+ * in/out - check API at xacl.h
+ */
+bRC_XACL XACL_Solaris::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
+
+   int aclrc = 0;
+
+   switch (stream){
+      case STREAM_UNIX_ACCESS_ACL:
+      case STREAM_XACL_SOLARIS_POSIX:
+      case STREAM_XACL_SOLARIS_NFS4:
+         aclrc = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
+         break;
+      default:
+         return bRC_XACL_error;
+   }
+
+   switch (aclrc){
+      case -1: {
+         berrno be;
+
+         switch (errno){
+            case ENOENT:
+               return bRC_XACL_ok;
+            default:
+               Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+               Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
+               return bRC_XACL_error;
+         }
+      }
+      case 0:
+         clear_flag(XACL_FLAG_NATIVE);
+         Mmsg(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"), jcr->last_fname);
+         return bRC_XACL_error;
+      default:
+         break;
+   }
+
+   switch (stream){
+      case STREAM_XACL_SOLARIS_POSIX:
+         if ((aclrc & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0){
+            Mmsg(jcr->errmsg, _("Trying to restore POSIX acl on file \"%s\" on filesystem without aclent acl support\n"), jcr->last_fname);
+            return bRC_XACL_error;
+         }
+         break;
+      case STREAM_XACL_SOLARIS_NFS4:
+         if ((aclrc & _ACL_ACE_ENABLED) == 0){
+            Mmsg(jcr->errmsg, _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without ace acl support\n"), jcr->last_fname);
+            return bRC_XACL_error;
+         }
+         break;
+      default:
+         break;
+   }
+
+   return os_set_acl(jcr, stream, content, length);
+};
+
+/*
+ * Perform OS specyfic extended attribute backup
+ *
+ * in/out - check API at xacl.h
+ *
+ * The Solaris implementation of XATTR is very, very different then all other "generic" unix implementations,
+ * so the original author of the Bacula XATTR support for Solaris OS decided to totally change the Xattr Stream
+ * content, and we need to follow this design to support previous behavior. The stream consist of a number of
+ * "files" with STREAM_XACL_SOLARIS_XATTR or STREAM_XACL_SOLARIS_SYS_XATTR stream' id. Every singe stream represents
+ * a single attibute. The content is a NULL-terminated array with a following data:
+ *    <xattr name>\0<encoded stat>\0<acl rendered text>\0<xattr data>
+ * when an attribute file has a hardlinked other attributes then a content stream changes a bit into:
+ *    <xattr name>\0<encoded stat>\0<target xattr name>\0
+ * where:
+ *    <xattr name> is an attribute name - a file name in Solaris
+ *    <encoded stat> is a standard file stat struct encoded by Bacula (the same encoding goes with a regular file)
+ *    <acl rendered text> is a Solaris dependent acltotext data
+ *    <xattr data> is the attribute file raw content
+ *    <target xattr name> 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
+ *    <str> - 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: <xattr name>\0<encoded stat>\0<acl rendered text>\0<xattr data>
+    * LNK:     <xattr name>\0<encoded stat>\0<target xattr name>\0
+    */
+   /* attribute name and length */
+   name = bp;
+   len = strlen (bp);
+   bp += len + 1;
+
+   /* attribute encoded stat */
+   attribs = bp;
+   len = strlen (bp);
+   bp += len + 1;
+   /* decode it */
+   decode_stat(attribs, &st, sizeof(st), &inum);
+
+   /* acltext and link target name goes here */
+   acltext = lntarget = bp;
+   len = strlen (bp);
+   /* now 'bp' should have a xattr data */
+   bp += len + 1;
+
+   /*
+    * Open the xattr on which to restore the xattrs read-only.
+    */
+   if ((attrdirfd = attropen(jcr->last_fname, ".", O_RDONLY)) < 0){
+      berrno be;
+
+      Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
+      Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
+      rc = bRC_XACL_error;
+      goto bail_out;
+   }
+
+   switch (st.st_mode & S_IFMT){
+      case S_IFREG:
+         if (inum != 0){
+            /* it is a linked attribute, perform a link operation */
+            unlinkat(attrdirfd, name, 0);
+            if (link(lntarget, name) < 0){
+               berrno be;
+
+               Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"), name, lntarget, jcr->last_fname, be.bstrerror());
+               Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n", name, lntarget, jcr->last_fname, be.bstrerror());
+               rc = bRC_XACL_error;
+               goto bail_out;
+            }
+            goto bail_out;
+         } else {
+            if (!extended){
+               unlinkat(attrdirfd, name, 0);
+            }
+            if ((attrfd = openat(attrdirfd, name, O_CREAT|O_RDWR)) < 0){
+               berrno be;
+
+               Mmsg3(jcr->errmsg, _("Unable to open attribute \"%s\" at file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
+               Dmsg3(100, "Unable to open attribute \"%s\" at file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
+               rc = bRC_XACL_error;
+               goto bail_out;
+            }
+            /* restore any data if are available */
+            if (st.st_size > 0){
+               cnt = write (attrfd, bp, length - (bp - content) );
+               if (cnt < 0){
+                  berrno be;
+
+                  Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
+                  Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
+                  rc = bRC_XACL_error;
+                  goto bail_out;
+               }
+            }
+         }
+         break;
+      case S_IFDIR:
+         /* if it is a current dir ob file then we can restore acl data only */
+         if (bstrcmp(name, ".")){
+            break;
+         }
+      default:
+         Mmsg2(jcr->errmsg, _("Unsupported xattr type %s on file \"%s\"\n"), name, jcr->last_fname);
+         Dmsg2(100, "Unsupported xattr type %s on file \"%s\"\n", name, jcr->last_fname);
+         goto bail_out;
+   }
+
+   /* file data restored, so setup permissions and acl data */
+   if (!extended){
+      if (fchownat(attrdirfd, name, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0){
+         berrno be;
+
+         switch (errno){
+            case EINVAL:
+            case ENOENT:
+               break;
+            default:
+               Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
+               Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
+               rc = bRC_XACL_error;
+         }
+         goto bail_out;
+      }
+   }
+
+#ifdef HAVE_ACL
+   if (strlen(acltext)){
+      rc = os_set_xattr_acl(jcr, attrfd, name, acltext);
+      if (rc != bRC_XACL_ok){
+         goto bail_out;
+      }
+   }
+#endif /* HAVE_ACL */
+
+   /* now restore a access and modification time - only for standard attribute */
+   if (!extended){
+      times[0].tv_sec = st.st_atime;
+      times[0].tv_usec = 0;
+      times[1].tv_sec = st.st_mtime;
+      times[1].tv_usec = 0;
+
+      if (futimesat(attrdirfd, name, times) < 0){
+         berrno be;
+
+         Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
+         Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
+         rc = bRC_XACL_error;
+         goto bail_out;
+      }
+   }
+
+bail_out:
+   if (attrfd != 0){
+      close(attrfd);
+   }
+   if (attrdirfd != 0){
+      close(attrdirfd);
+   }
+   return rc;
+};
+
+#endif /* HAVE_SUN_OS */
diff --git a/bacula/src/filed/xacl_solaris.h b/bacula/src/filed/xacl_solaris.h
new file mode 100644 (file)
index 0000000..f8a8d5b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2016 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+/**
+ * Major refactoring of ACL and XATTR code written by:
+ *
+ *  RadosÅ‚aw Korzeniewski, MMXVI
+ *  radoslaw@korzeniewski.net, radekk@inteos.pl
+ *  Inteos Sp. z o.o. http://www.inteos.pl/
+ *
+ */
+
+#ifndef __XACL_Solaris_H_
+#define __XACL_Solaris_H_
+
+#if defined(HAVE_SUN_OS)
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#else
+#error "configure failed to detect availability of sys/acl.h"
+#endif
+
+#ifdef HAVE_SYS_ATTR_H
+#include <sys/attr.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
+
+/*
+ * Cache structure in alist
+ */
+struct XACL_Solaris_Cache {
+   ino_t inode;
+   char * name;
+};
+
+/*
+ *
+ *
+ */
+class XACL_Solaris : public XACL {
+private:
+   alist * cache;
+   bRC_XACL os_backup_acl (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt);
+   bRC_XACL os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_get_acl(JCR *jcr, int *stream);
+   bRC_XACL os_set_acl(JCR *jcr, int stream, char *content, uint32_t length);
+   bRC_XACL os_get_xattr_names (JCR *jcr, POOLMEM **list, uint32_t *length);
+   bRC_XACL os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen);
+   bRC_XACL os_set_xattr (JCR *jcr, bool extended, char *content, uint32_t length);
+   bRC_XACL os_get_xattr_acl(JCR *jcr, int fd, char **buffer);
+   bRC_XACL os_set_xattr_acl(JCR *jcr, int fd, char *name, char *acltext);
+   /* requires acl.h available */
+   bRC_XACL check_xacltype (JCR *jcr, int name);
+   inline char * find_xattr_cache(JCR *jcr, ino_t ino, char * name);
+   inline void delete_xattr_cache();
+public:
+   XACL_Solaris ();
+   ~XACL_Solaris ();
+};
+
+#endif /* HAVE_SUN_OS */
+
+#endif /* __XACL_Solaris_H_ */
diff --git a/bacula/src/filed/xattr.c b/bacula/src/filed/xattr.c
deleted file mode 100644 (file)
index 6762348..0000000
+++ /dev/null
@@ -1,3663 +0,0 @@
-/*
-   Bacula(R) - The Network Backup Solution
-
-   Copyright (C) 2000-2015 Kern Sibbald
-   Copyright (C) 2008-2014 Free Software Foundation Europe e.V.
-
-   The original author of Bacula is Kern Sibbald, with contributions
-   from many others, a complete list can be found in the file AUTHORS.
-
-   You may use this file and others of this release according to the
-   license defined in the LICENSE file, which includes the Affero General
-   Public License, v3.0 ("AGPLv3") and some additional permissions and
-   terms pursuant to its AGPLv3 Section 7.
-
-   This notice must be preserved when any source code is 
-   conveyed and/or propagated.
-
-   Bacula(R) is a registered trademark of Kern Sibbald.
-*/
-/* 
- * Functions to handle Extended Attributes for bacula. 
- * 
- * Extended Attributes are so OS specific we only restore Extended Attributes if 
- * they were saved using a filed on the same platform. 
- * 
- * Currently we support the following OSes: 
- *   - AIX (Extended Attributes) 
- *   - Darwin (Extended Attributes) 
- *   - FreeBSD (Extended Attributes) 
- *   - GNU HURD (Extended Attributes) 
- *   - IRIX (Extended Attributes) 
- *   - Linux (Extended Attributes) 
- *   - NetBSD (Extended Attributes) 
- *   - OpenBSD (Extended Attributes) 
- *     (As it seems either they never implemented xattr or they are removed 
- *      the support as it stated it was in version 3.1 but the current syscall 
- *      tabled shows the extattr_ functions are not implemented. So as such we 
- *      might eventually support xattr on OpenBSD when they implemented them using 
- *      the same interface as FreeBSD and NetBSD. 
- *   - Solaris (Extended Attributes and Extensible Attributes) 
- *   - Tru64 (Extended Attributes) 
- *
- *   Written by Marco van Wieringen, November 2008 
- *   Major overhaul January 2012 + June 2012 
- *   Simplfied by Kern Sibbald, June 2015
- */ 
-#include "bacula.h" 
-#include "filed.h"   
-#ifndef HAVE_XATTR 
-/* 
- * Entry points when compiled without support for XATTRs or on an unsupported platform. 
- */ 
-bool backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   Jmsg(jcr, M_FATAL, 0, "XATTR backup requested but not configured in Bacula.\n");
-   return false;
-}
-
-bxattr_rtn_code restore_xattr_streams(JCR *jcr, int stream, char *content,
-                                    uint32_t content_length)
-{
-   return bxattr_rtn_fatal;
-}
-#else 
-/* 
- * Send a XATTR stream to the SD.
- */ 
-static bxattr_rtn_code send_xattr_stream(JCR *jcr, int stream)
-{ 
-   BSOCK *sd = jcr->store_bsock;
-   POOLMEM *msgsave;
-
-#ifdef FD_NO_SEND_TEST 
-   return bxattr_rtn_ok;
-#endif 
-
-   /* 
-    * Sanity check 
-    */ 
-   if (jcr->xattr_ctx->content_length <= 0) {
-      return bxattr_rtn_ok;
-   }
-   /* 
-    * Send header 
-    */ 
-   if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
-      return bxattr_rtn_fatal;
-   } 
-   /* 
-    * Send the buffer to the storage deamon 
-    */ 
-   Dmsg1(400, "Backing up XATTR <%s>\n", jcr->xattr_ctx->content);
-   msgsave = sd->msg;
-   sd->msg = jcr->xattr_ctx->content;
-   sd->msglen = jcr->xattr_ctx->content_length;
-   if (!sd->send()) {
-      sd->msg = msgsave;
-      sd->msglen = 0;
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
-      return bxattr_rtn_fatal;
-   } 
-   jcr->JobBytes += sd->msglen;
-   sd->msg = msgsave;
-   if (!sd->signal(BNET_EOD)) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
-      return bxattr_rtn_fatal;
-   } 
-   Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname);
-   return bxattr_rtn_ok;
-} 
-/* 
- * First some generic functions for OSes that use the same xattr encoding scheme. 
- * Currently for all OSes except for Solaris. 
- */ 
-#ifndef HAVE_SUN_OS
-static void xattr_drop_internal_table(alist *xattr_value_list)
-{
-   xattr_t *current_xattr;
-   
-   if (!xattr_value_list) {
-      return;
-   }
-   /* Walk the list of xattrs and free allocated memory. */
-   foreach_alist(current_xattr, xattr_value_list) {
-      if (current_xattr->magic != XATTR_MAGIC) {
-         continue;
-      }
-      if (current_xattr->name) {
-         free(current_xattr->name);
-      }
-      if (current_xattr->value && current_xattr->value_length > 0) {
-         free(current_xattr->value);
-      }
-      free(current_xattr);
-   }
-   delete xattr_value_list;
-}
-
-/*
- * The xattr stream for OSX, FreeBSD, Linux and NetBSD is a serialized stream of bytes 
- * which encodes one or more xattr_t structures. 
- * 
- * The Serialized stream consists of the following elements: 
- *    magic - A magic string which makes it easy to detect any binary incompatabilites 
- *    name_length - The length of the following xattr name 
- *    name - The name of the extended attribute 
- *    value_length - The length of the following xattr data 
- *    value - The actual content of the extended attribute 
- * 
- * This is repeated 1 or more times. 
- * 
- */ 
-static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len,
-                                       alist *xattr_value_list)
-{ 
-   xattr_t *current_xattr;
-   ser_declare;
-   /* 
-    * Make sure the serialized stream fits in the poolmem buffer. 
-    * We allocate some more to be sure the stream is gonna fit. 
-    */ 
-   jcr->xattr_ctx->content =
-   check_pool_memory_size(jcr->xattr_ctx->content, expected_serialize_len + 10);
-   ser_begin(jcr->xattr_ctx->content, expected_serialize_len + 10);
-   /* 
-    * Walk the list of xattrs and serialize the data. 
-    */ 
-   foreach_alist(current_xattr, xattr_value_list) {
-      if (current_xattr->magic != XATTR_MAGIC) {
-         continue;                 /* Don't write invalid xattr */
-      }
-
-      ser_uint32(current_xattr->magic);
-      ser_uint32(current_xattr->name_length);
-      ser_bytes(current_xattr->name, current_xattr->name_length);
-
-      ser_uint32(current_xattr->value_length);
-      if (current_xattr->value_length > 0 && current_xattr->value) {
-         ser_bytes(current_xattr->value, current_xattr->value_length);
-         Dmsg3(100, "Backup xattr named %s, value %*s\n",
-               current_xattr->name, current_xattr->value, current_xattr->value);
-      } else {
-         Dmsg1(100, "Backup empty xattr named %s\n", current_xattr->name);
-      }
-   }
-
-   ser_end(jcr->xattr_ctx->content, expected_serialize_len + 10);
-   jcr->xattr_ctx->content_length = ser_length(jcr->xattr_ctx->content);
-   return jcr->xattr_ctx->content_length;
-}
-
-static bxattr_rtn_code unserialize_xattr_stream(JCR *jcr,
-                                                 char *content,
-                                                 uint32_t content_length,
-                                                 alist *xattr_value_list)
-{
-   unser_declare;
-   xattr_t *current_xattr;
-
-   /*
-    * Restore the stream and call restore_xattr_on_file for each extended attribute.
-    *
-    * Start unserializing the data. We keep on looping while we have not
-    * unserialized all bytes in the stream.
-    */
-   unser_begin(content, content_length);
-   while (unser_length(content) < content_length) {
-      /* 
-       * First make sure the magic is present. This way we can easily catch corruption. 
-       * Any missing MAGIC is fatal we do NOT try to continue. 
-       */ 
-      current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
-      unser_uint32(current_xattr->magic);
-      if (current_xattr->magic != XATTR_MAGIC) {
-         Mmsg1(jcr->errmsg, _("Invalid xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         free(current_xattr);
-         return bxattr_rtn_error;
-      }
-      /* 
-       * Decode the valuepair. First decode the length of the name. 
-       */ 
-      unser_uint32(current_xattr->name_length);
-      if (current_xattr->name_length == 0) {
-         Mmsg1(jcr->errmsg, _("Invalid xattr stream, xattr name length <= 0 on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         free(current_xattr);
-         return bxattr_rtn_error;
-      }
-      /* 
-       * Allocate room for the name and decode its content. 
-       */ 
-      current_xattr->name = (char *)malloc(current_xattr->name_length + 1);
-      unser_bytes(current_xattr->name, current_xattr->name_length);
-      /* 
-       * The xattr_name needs to be null terminated. 
-       */ 
-      current_xattr->name[current_xattr->name_length] = '\0';
-      /* 
-       * Decode the value length. 
-       */ 
-      unser_uint32(current_xattr->value_length);
-      if (current_xattr->value_length > 0) {
-         /* 
-          * Allocate room for the value and decode its content. 
-          */ 
-         current_xattr->value = (char *)malloc(current_xattr->value_length);
-         unser_bytes(current_xattr->value, current_xattr->value_length);
-         Dmsg3(100, "Restoring xattr named %s, value %*s\n",
-               current_xattr->name, current_xattr->value, current_xattr->value);
-      } else {
-         current_xattr->value = NULL;
-         Dmsg1(100, "Restoring empty xattr named %s\n", current_xattr->name);
-      }
-
-      xattr_value_list->append(current_xattr);
-   }
-
-   unser_end(content, content_length);
-   return bxattr_rtn_ok;
-}
-#endif
-
-/* 
- * This is a supported OS, See what kind of interface we should use.
- */ 
-#if defined(HAVE_AIX_OS)
-#if (!defined(HAVE_LISTEA) && !defined(HAVE_LLISTEA)) || \
-    (!defined(HAVE_GETEA) && !defined(HAVE_LGETEA)) || \
-    (!defined(HAVE_SETEA) && !defined(HAVE_LSETEA))
-#error "Missing full support for the Extended Attributes (EA) functions."
-#endif
-
-#ifdef HAVE_SYS_EA_H
-#include <sys/ea.h>
-#else
-#error "Missing sys/ea.h header file"
-#endif
-
-/*
- * Define the supported XATTR streams for this OS
- */
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_AIX
-};
-
-/*
- * Fallback to the non l-functions when those are not available.
- */
-#if defined(HAVE_GETEA) && !defined(HAVE_LGETEA)
-#define lgetea getea
-#endif
-#if defined(HAVE_SETEA) && !defined(HAVE_LSETEA)
-#define lsetea setea
-#endif
-#if defined(HAVE_LISTEA) && !defined(HAVE_LLISTEA)
-#define llistea listea
-#endif
-
-static bxattr_rtn_code aix_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   char *bp;
-   bool skip_xattr;
-   char *xattr_list = NULL;
-   int cnt, xattr_count = 0;
-   uint32_t name_length;
-   int32_t xattr_list_len, xattr_value_len;
-   uint32_t expected_serialize_len = 0;
-   xattr_t *current_xattr;
-   alist *xattr_value_list = NULL;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   /*
-    * First get the length of the available list with extended attributes.
-    */
-   xattr_list_len = llistea(jcr->last_fname, NULL, 0);
-   if (xattr_list_len < 0) {
-      berrno be;
-      if (errno == ENOENT || errno == EFORMAT) {
-         retval = bxattr_rtn_ok;
-      } else if (errno == ENOTSUP) {
-         /*
-          * If the filesystem reports it doesn't support XATTRs we clear the
-          * BXATTR_FLAG_SAVE_NATIVE flag so we skip XATTR saves on all other files
-          * on the same filesystem. The BXATTR_FLAG_SAVE_NATIVE flags gets sets again
-          * when we change from one filesystem to an other.
-          */
-         jcr->xattr_ctx->flags &= ~BXATTR_FLAG_SAVE_NATIVE;
-         retval = bxattr_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("XATTR llistea error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-      }
-      goto get_out;
-   } else if (xattr_list_len == 0) {
-      retval = bxattr_rtn_ok;
-      goto get_out;
-   }
-
-   /*
-    * Allocate room for the extented attribute list.
-    */
-   xattr_list = (char *)malloc(xattr_list_len + 1);
-   memset(xattr_list, 0, xattr_list_len + 1);
-
-   /*
-    * Get the actual list of extended attributes names for a file.
-    */
-   xattr_list_len = llistea(jcr->last_fname, xattr_list, xattr_list_len);
-   if (xattr_list_len < 0) {
-      berrno be;
-      if (errno == ENOENT || errno == EFORMAT) {
-         retval = bxattr_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("XATTR llistea error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-      }
-      goto get_out;
-   }
-   xattr_list[xattr_list_len] = '\0';
-
-   /*
-    * Walk the list of extended attributes names and retrieve the data.
-    * We already count the bytes needed for serializing the stream later on.
-    */
-   for (bp = xattr_list; (bp - xattr_list) + 1 < xattr_list_len;
-        bp = strchr(bp, '\0') + 1) {
-      skip_xattr = false;
-
-      /*
-       * We want to skip certain xattrs which start with a 0xF8 character on AIX.
-       */
-      if (*bp == 0xF8) {
-         skip_xattr = true;
-      }
-
-      name_length = strlen(bp);
-      if (skip_xattr || name_length == 0) {
-         Dmsg1(100, "Skipping xattr named %s\n", bp);
-         continue;
-      }
-
-      /*
-       * First see how long the value is for the extended attribute.
-       */
-      xattr_value_len = lgetea(jcr->last_fname, bp, NULL, 0);
-      if (xattr_value_len < 0) {
-         berrno be;
-         if (errno == ENOENT || errno == EFORMAT) {
-            retval = bxattr_rtn_ok;
-         } else {
-            Mmsg2(jcr->errmsg, _("XATTR lgetea error on file \"%s\": ERR=%s\n"),
-                  jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-         }
-         goto get_out;
-      }
-
-      /*
-       * Each xattr valuepair starts with a magic so we can restore it easier.
-       */
-      current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
-      memset(current_xattr, 0, sizeof(xattr_t));
-      current_xattr->magic = XATTR_MAGIC;
-      expected_serialize_len += sizeof(current_xattr->magic);
-
-      /*
-       * Allocate space for storing the name.
-       */
-      current_xattr->name_length = name_length;
-      current_xattr->name = (char *)malloc(current_xattr->name_length);
-      memcpy(current_xattr->name, bp, current_xattr->name_length);
-
-      expected_serialize_len += sizeof(current_xattr->name_length) +
-                                current_xattr->name_length;
-
-      if (xattr_value_len == 0) {
-         current_xattr->value = NULL;
-         current_xattr->value_length = 0;
-         expected_serialize_len += sizeof(current_xattr->value_length);
-      } else {
-         /*
-          * Allocate space for storing the value.
-          */
-         current_xattr->value = (char *)malloc(xattr_value_len);
-         memset(current_xattr->value, 0, xattr_value_len);
-
-         xattr_value_len = lgetea(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
-         if (xattr_value_len < 0) {
-            berrno be;
-            if (errno == ENOENT || errno == EFORMAT) {
-               retval = bxattr_rtn_ok;
-            } else {
-               Mmsg2(jcr->errmsg, _("XATTR lgetea error on file \"%s\": ERR=%s\n"),
-                     jcr->last_fname, be.bstrerror());
-               Dmsg1(100, "%s", jcr->errmsg);
-            }
-
-            /*
-             * Default failure path out when retrieval of attr fails.
-             */
-            free(current_xattr->value);
-            free(current_xattr->name);
-            free(current_xattr);
-            goto get_out;
-         }
-         /*
-          * Store the actual length of the value.
-          */
-         current_xattr->value_length = xattr_value_len;
-         expected_serialize_len += sizeof(current_xattr->value_length) +
-                                   current_xattr->value_length;
-      }
-
-      if (xattr_value_list == NULL) {
-         xattr_value_list = New(alist(10, not_owned_by_alist));
-      }
-
-      xattr_value_list->append(current_xattr);
-      xattr_count++;
-
-      /*
-       * Protect ourself against things getting out of hand.
-       */
-      if (expected_serialize_len >= MAX_XATTR_LENGTH) {
-         Mmsg2(jcr->errmsg,
-               _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
-               jcr->last_fname, MAX_XATTR_LENGTH);
-         goto get_out;
-      }
-   }
-
-   free(xattr_list);
-   xattr_list = (char *)NULL;
-
-   /*
-    * If we found any xattr send them to the SD.
-    */
-   if (xattr_count > 0) {
-      /* Serialize the datastream. */
-      if (serialize_xattr_stream(jcr, expected_serialize_len,
-                                 xattr_value_list) < expected_serialize_len) {
-         Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /*
-       * Send the datastream to the SD.
-       */
-      retval = send_xattr_stream(jcr, os_default_xattr_streams[0]);
-   } else {
-      retval = bxattr_rtn_ok;
-   }
-
-get_out:
-   if (xattr_list != NULL) {
-      free(xattr_list);
-   }
-   if (xattr_value_list != NULL) {
-      xattr_drop_internal_table(xattr_value_list);
-   }
-
-   return retval;
-}
-
-static bxattr_rtn_code aix_restore_xattr_streams(JCR *jcr, int stream, char *content,
-                                               uint32_t content_length)
-{
-   xattr_t *current_xattr;
-   alist *xattr_value_list;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   xattr_value_list = New(alist(10, not_owned_by_alist));
-
-   if (unserialize_xattr_stream(jcr, content, content_length,
-                                xattr_value_list) != bxattr_rtn_ok) {
-      goto get_out;
-   }
-
-   foreach_alist(current_xattr, xattr_value_list) {
-      if (lsetea(jcr->last_fname, current_xattr->name, current_xattr->value,
-                 current_xattr->value_length, 0) != 0) {
-         berrno be;
-         switch (errno) {
-         case ENOENT:
-         case EFORMAT:
-            break;
-         case ENOTSUP:
-            /*
-             * If the filesystem reports it doesn't support XATTRs we clear
-             * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
-             * on all other files on the same filesystem. The
-             * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
-             * change from one filesystem to an other.
-             */
-            jcr->xattr_ctx->flags &= ~BXATTR_FLAG_RESTORE_NATIVE;
-            /* Failback wanted */
-         default:
-            MmsgD2(100, jcr->errmsg,
-                   _("XATTR lsetea error on file \"%s\": ERR=%s\n"),
-                   jcr->last_fname, be.bstrerror());
-            break;
-         }
-         goto get_out;
-      }
-   }
-   retval = bxattr_rtn_ok;
-
-get_out:
-   xattr_drop_internal_table(xattr_value_list);
-
-   return retval;
-}
-
-/*
- * Function pointers to the build and restore function to use for these xattrs.
- */
-static bxattr_rtn_code (*os_backup_xattr_streams)
-                        (JCR *jcr, FF_PKT *ff_pkt) =
-                        aix_xattr_build_streams;
-static bxattr_rtn_code (*os_restore_xattr_streams)
-                        (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                        aix_restore_xattr_streams;
-
-#elif defined(HAVE_IRIX_OS)
-
-#include <sys/attributes.h>
-
-/*
- * Define the supported XATTR streams for this OS
- */
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_IRIX
-};
-static const char *xattr_acl_skiplist[1] = {
-   NULL
-};
-static const char *xattr_skiplist[1] = {
-   NULL
-};
-
-struct xattr_naming_space {
-   const char *name;
-   int flags;
-};
-
-static xattr_naming_space xattr_naming_spaces[] = {
-   {
-      "user.",
-      ATTR_DONTFOLLOW
-   }, {
-      "root.",
-      ATTR_ROOT | ATTR_DONTFOLLOW
-   }, {
-      NULL,
-      0
-   }
-};
-
-static bxattr_rtn_code irix_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   char dummy[32];
-   int cnt, length, xattr_count = 0;
-   attrlist_cursor_t cursor;
-   attrlist_t *attrlist;
-   attrlist_ent_t *attrlist_ent;
-   xattr_t *current_xattr;
-   alist *xattr_value_list = NULL;
-   uint32_t expected_serialize_len = 0;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-   POOLMEM *xattrbuf = get_memory(ATTR_MAX_VALUELEN);
-
-   for (cnt = 0; xattr_naming_spaces[cnt].name != NULL; cnt++) {
-      memset(&cursor, 0, sizeof(attrlist_cursor_t));
-      for ( ;; } {
-         if (attr_list(jcr->last_fname, xattrbuf, ATTR_MAX_VALUELEN,
-                       xattr_naming_spaces[cnt].flags, &cursor) != 0) {
-            berrno be;
-            switch (errno) {
-            case ENOENT:
-               retval = bxattr_rtn_ok;
-               goto get_out;
-            default:
-               Mmsg2(jcr->errmsg, _("XATTR attr_list error on file \"%s\": ERR=%s\n"),
-                     jcr->last_fname, be.bstrerror());
-               Dmsg1(100, "%s", jcr->errmsg);
-               goto get_out;
-            }
-         }
-
-         attrlist = (attrlist_t *)xattrbuf;
-
-         /*
-          * Walk the available attributes.
-          */
-         for (cnt = 0; cnt < attrlist->al_count; cnt++) {
-            attrlist_ent = ATTR_ENTRY(xattrbuf, cnt);
-
-            /*
-             * First determine if we can retrieve the xattr and how big it really is.
-             */
-            length = sizeof(dummy);
-            if (attr_get(jcr->last_fname, attrlist_ent->a_name, dummy,
-                         &length, xattr_naming_spaces[cnt].flags) != 0) {
-               berrno be;
-               switch (errno) {
-               case ENOENT:
-               case ENOATTR:
-                  retval = bxattr_rtn_ok;
-                  goto get_out;
-               case E2BIG:
-                  /*
-                   * Size of the xattr is bigger then the 32 bytes dummy which is
-                   * likely. As length now contains its actual length we can allocate
-                   * a properly size buffer for the real retrieval.
-                   */
-                  break;
-               default:
-                  Mmsg2(jcr->errmsg, _("XATTR attr_list error on file \"%s\": ERR=%s\n"),
-                        jcr->last_fname, be.bstrerror());
-                  Dmsg1(100, "%s", jcr->errmsg);
-                  goto get_out;
-               }
-            }
-
-            /*
-             * Each xattr valuepair starts with a magic so we can restore it easier.
-             */
-            current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
-            memset(current_xattr, 0, sizeof(xattr_t));
-            current_xattr->magic = XATTR_MAGIC;
-            expected_serialize_len += sizeof(current_xattr->magic);
-
-            /*
-             * Allocate space for storing the name.
-             * We store the name as <naming_space_name><xattr_name>
-             */
-            current_xattr->name_length = strlen(xattr_naming_spaces[cnt].name) +
-                                         strlen(attrlist_ent->a_name) + 1;
-            current_xattr->name = (char *)malloc(current_xattr->name_length);
-            bsnprintf(current_xattr->name, current_xattr->name_length, "%s%s",
-                      xattr_naming_spaces[cnt].name, attrlist_ent->a_name);
-
-            expected_serialize_len += sizeof(current_xattr->name_length) +
-                                      current_xattr->name_length;
-
-            current_xattr->value_length = length;
-            current_xattr->value = (char *)malloc(current_xattr->value_length);
-
-            /*
-             * Retrieve the actual value of the xattr.
-             */
-            if (attr_get(jcr->last_fname, attrlist_ent->a_name, current_xattr->value,
-                         &length, xattr_naming_spaces[cnt].flags) != 0) {
-               berrno be;
-
-               switch (errno) {
-               case ENOENT:
-               case ENOATTR:
-                  retval = bxattr_rtn_ok;
-                  break;
-               case E2BIG:
-                  /*
-                   * The buffer for the xattr isn't big enough. the value of
-                   * current_xattr->value_length is updated with the actual size
-                   * of the xattr. So we free the old buffer and create a new one
-                   * and try again. Normally this cannot happen as we size the
-                   * buffer using a call to attr_get before but in case of an
-                   * race condition it might happen.
-                   */
-                  free(current_xattr->value);
-                  current_xattr->value = (char *)malloc(length);
-                  if (attr_get(jcr->last_fname, attrlist_ent->a_name, current_xattr->value,
-                               &length, xattr_naming_spaces[cnt].flags) != 0) {
-                     switch (errno) {
-                     case ENOENT:
-                     case ENOATTR:
-                        retval = bxattr_rtn_ok;
-                        break;
-                     default:
-                        Mmsg2(jcr->errmsg,
-                              _("attr_list error on file \"%s\": ERR=%s\n"),
-                              jcr->last_fname, be.bstrerror(errno));
-                        Dmsg2(100, "attr_list error file=%s ERR=%s\n",
-                              jcr->last_fname, be.bstrerror());
-                        break;
-                     }
-                  } else {
-                     goto ok_continue;
-                  }
-                  break;
-               default:
-                  Mmsg2(jcr->errmsg, _("XATTR attr_list error on file \"%s\": ERR=%s\n"),
-                        jcr->last_fname, be.bstrerror());
-                  Dmsg1(100, "%s", jcr->errmsg);
-                  break;
-               }
-
-               /*
-                * Default failure path out when retrieval of attr fails.
-                */
-               free(current_xattr->value);
-               free(current_xattr->name);
-               free(current_xattr);
-               goto get_out;
-            }
-
-ok_continue:
-            current_xattr->value_length = length;
-            expected_serialize_len += sizeof(current_xattr->value_length) +
-                                      current_xattr->value_length;
-
-            if (xattr_value_list == NULL) {
-               xattr_value_list = New(alist(10, not_owned_by_alist));
-            }
-
-            xattr_value_list->append(current_xattr);
-            xattr_count++;
-
-            /*
-             * Protect ourself against things getting out of hand.
-             */
-            if (expected_serialize_len >= MAX_XATTR_LENGTH) {
-               Mmsg2(jcr->errmsg,
-                     _("XATTR stream on file \"%s\" exceeds maximum size of %d bytes\n"),
-                     jcr->last_fname, MAX_XATTR_LENGTH);
-               goto get_out;
-            }
-         }
-
-         /*
-          * See if there are more attributes available for a next run of attr_list.
-          */
-         if (attrlist->al_more == 0) {
-            break;
-         }
-      }
-   }
-
-   /*
-    * If we found any xattr send them to the SD.
-    */
-   if (xattr_count > 0) {
-      /* Serialize the XATTR */
-      if (serialize_xattr_stream(jcr, expected_serialize_len,
-                                 xattr_value_list) < expected_serialize_len) {
-         Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /* Send the XATTR to the SD. */
-      retval = send_xattr_stream(jcr, os_default_xattr_streams[0]);
-   } else {
-      retval = bxattr_rtn_ok;
-   }
-
-get_out:
-   free_pool_memory(xattrbuf);
-
-   if (xattr_value_list != NULL) {
-      xattr_drop_internal_table(xattr_value_list);
-   }
-
-   return retval;
-}
-
-static bxattr_rtn_code irix_restore_xattr_streams(JCR *jcr,
-                                                 int stream,
-                                                 char *content,
-                                                 uint32_t content_length)
-{
-   char *bp;
-   int cnt, cmp_size, name_space_index, flags;
-   xattr_t *current_xattr;
-   alist *xattr_value_list;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   xattr_value_list = New(alist(10, not_owned_by_alist));
-
-   if (unserialize_xattr_stream(jcr,
-                                content,
-                                content_length,
-                                xattr_value_list) != bxattr_rtn_ok) {
-      goto get_out;
-   }
-
-   foreach_alist(current_xattr, xattr_value_list) {
-      /*
-       * See to what namingspace this xattr belongs to.
-       */
-      name_space_index = 0;
-      for (cnt = 0; xattr_naming_spaces[cnt].name != NULL; cnt++) {
-         cmp_size = strlen(xattr_naming_spaces[cnt].name);
-         if (!strncasecmp(current_xattr->name,
-                          xattr_naming_spaces[cnt].name,
-                          cmp_size)) {
-            name_space_index = cnt;
-            break;
-         }
-      }
-
-      /*
-       * If we got a xattr that doesn't belong to an valid namespace complain.
-       */
-      if (name_space_index == 0) {
-         Mmsg2(jcr->errmsg,
-               _("Received invalid xattr named %s on file \"%s\"\n"),
-               current_xattr->name, jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /*
-       * Restore the xattr first try to create the attribute from scratch.
-       */
-      flags = xattr_naming_spaces[name_space_index].flags | ATTR_CREATE;
-      bp = strchr(current_xattr->name, '.');
-      if (attr_set(jcr->last_fname, ++bp, current_xattr->value,
-                   current_xattr->value_length, flags) != 0) {
-         berrno be;
-
-         switch (errno) {
-         case ENOENT:
-            retval = bxattr_rtn_ok;
-            goto get_out;
-         case EEXIST:
-            /*
-             * The xattr already exists we need to replace it.
-             */
-            flags = xattr_naming_spaces[name_space_index].flags | ATTR_REPLACE;
-            if (attr_set(jcr->last_fname, bp, current_xattr->value,
-                         current_xattr->value_length, flags) != 0) {
-               switch (errno) {
-               case ENOENT:
-                  retval = bxattr_rtn_ok;
-                  goto get_out;
-               default:
-                  Mmsg2(jcr->errmsg, _("attr_set error on file \"%s\": ERR=%s\n"),
-                        jcr->last_fname, be.bstrerror(errno));
-                  Dmsg1(100, "%s", jcr->errmsg);
-                  goto get_out;
-               }
-            }
-            break;
-         default:
-            Mmsg2(jcr->errmsg, _("attr_set error on file \"%s\": ERR=%s\n"),
-                  jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      }
-   }
-
-   retval = bxattr_rtn_ok;
-
-get_out:
-   xattr_drop_internal_table(xattr_value_list);
-
-   return retval;
-}
-
-/*
- * Function pointers to the build and restore function to use for these xattrs.
- */
-static bxattr_rtn_code (*os_backup_xattr_streams)
-                        (JCR *jcr, FF_PKT *ff_pkt) =
-                        irix_xattr_build_streams;
-static bxattr_rtn_code (*os_restore_xattr_streams)
-                        (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                        irix_restore_xattr_streams;
-
-#elif defined(HAVE_DARWIN_OS) || \
-      defined(HAVE_LINUX_OS) || \
-      defined(HAVE_HURD_OS)
-
-#if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \
-    (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \
-    (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR))
-#error "Missing full support for the XATTR functions."
-#endif
-
-#ifdef HAVE_SYS_XATTR_H
-#include <sys/xattr.h>
-#else
-#error "Missing sys/xattr.h header file"
-#endif
-
-/*
- * Define the supported XATTR streams for this OS
- */
-#if defined(HAVE_DARWIN_OS)
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_DARWIN
-};
-static const char *xattr_acl_skiplist[2] = {
-   "com.apple.system.Security",
-   NULL
-};
-static const char *xattr_skiplist[3] = {
-   "com.apple.system.extendedsecurity",
-   "com.apple.ResourceFork",
-   NULL
-};
-#elif defined(HAVE_LINUX_OS)
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_LINUX
-};
-static const char *xattr_acl_skiplist[3] = {
-   "system.posix_acl_access",
-   "system.posix_acl_default",
-   NULL
-};
-static const char *xattr_skiplist[1] = {
-   NULL
-};
-#elif defined(HAVE_HURD_OS)
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_HURD
-};
-static const char *xattr_acl_skiplist[1] = {
-   NULL
-};
-static const char *xattr_skiplist[1] = {
-   NULL
-};
-#endif
-
-/*
- * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
- * listxattr, getxattr and setxattr with an extra options argument
- * which mimics the l variants of the functions when we specify
- * XATTR_NOFOLLOW as the options value.
- */
-#if defined(HAVE_DARWIN_OS)
-   #define llistxattr(path, list, size) \
-           listxattr((path), (list), (size), XATTR_NOFOLLOW)
-   #define lgetxattr(path, name, value, size) \
-           getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
-   #define lsetxattr(path, name, value, size, flags) \
-           setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
-#else
-   /*
-    * Fallback to the non l-functions when those are not available.
-    */
-   #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
-   #define lgetxattr getxattr
-   #endif
-   #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
-   #define lsetxattr setxattr
-   #endif
-   #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
-   #define llistxattr listxattr
-   #endif
-#endif
-
-static bxattr_rtn_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   char *bp;
-   bool skip_xattr;
-   char *xattr_list = NULL;
-   int cnt, xattr_count = 0;
-   uint32_t name_length;
-   int32_t xattr_list_len,
-           xattr_value_len;
-   uint32_t expected_serialize_len = 0;
-   xattr_t *current_xattr;
-   alist *xattr_value_list = NULL;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   /*
-    * First get the length of the available list with extended attributes.
-    */
-   xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
-   switch (xattr_list_len) {
-   case -1: {
-      berrno be;
-
-      switch (errno) {
-      case ENOENT:
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      case BXATTR_ENOTSUP:
-         /*
-          * If the filesystem reports it doesn't support XATTRs we clear
-          * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
-          * on all other files on the same filesystem. The
-          * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
-          * change from one filesystem to an other.
-          */
-         jcr->xattr_ctx->flags &= ~BXATTR_FLAG_SAVE_NATIVE;
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      default:
-         Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-      break;
-   }
-   case 0:
-      retval = bxattr_rtn_ok;
-      goto get_out;
-   default:
-      break;
-   }
-
-   /*
-    * Allocate room for the extented attribute list.
-    */
-   xattr_list = (char *)malloc(xattr_list_len + 1);
-   memset(xattr_list, 0, xattr_list_len + 1);
-
-   /*
-    * Get the actual list of extended attributes names for a file.
-    */
-   xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
-   if (xattr_list_len < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         retval = bxattr_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-      }
-     goto get_out;
-   }
-   xattr_list[xattr_list_len] = '\0';
-
-   /*
-    * Walk the list of extended attributes names and retrieve the data.
-    * We already count the bytes needed for serializing the stream later on.
-    */
-   for (bp = xattr_list;
-       (bp - xattr_list) + 1 < xattr_list_len;
-        bp = strchr(bp, '\0') + 1) {
-      skip_xattr = false;
-
-      /*
-       * On some OSes you also get the acls in the extented attribute list.
-       * So we check if we are already backing up acls and if we do we
-       * don't store the extended attribute with the same info.
-       */
-      if (ff_pkt->flags & FO_ACL) {
-         for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
-            if (bstrcmp(bp, xattr_acl_skiplist[cnt])) {
-               skip_xattr = true;
-               break;
-            }
-         }
-      }
-
-      /*
-       * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
-       */
-      if (!skip_xattr) {
-         for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
-            if (bstrcmp(bp, xattr_skiplist[cnt])) {
-               skip_xattr = true;
-               break;
-            }
-         }
-      }
-
-      name_length = strlen(bp);
-      if (skip_xattr || name_length == 0) {
-         Dmsg1(100, "Skipping xattr named %s\n", bp);
-         continue;
-      }
-
-      /*
-       * First see how long the value is for the extended attribute.
-       */
-      xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
-      if (xattr_value_len < 0) {
-         berrno be;
-         if (errno == ENOENT) {
-            retval = bxattr_rtn_ok;
-         } else {
-            Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
-                  jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-         }
-         goto get_out;
-      }
-
-      /*
-       * Each xattr valuepair starts with a magic so we can restore it easier.
-       */
-      current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
-      current_xattr->magic = XATTR_MAGIC;
-      current_xattr->value = NULL;
-      expected_serialize_len += sizeof(current_xattr->magic);
-
-      /*
-       * Allocate space for storing the name.
-       */
-      current_xattr->name_length = name_length;
-      current_xattr->name = (char *)malloc(current_xattr->name_length);
-      memcpy(current_xattr->name, bp, current_xattr->name_length);
-
-      expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
-
-      switch (xattr_value_len) {
-      case 0:
-         current_xattr->value = NULL;
-         current_xattr->value_length = 0;
-         expected_serialize_len += sizeof(current_xattr->value_length);
-         break;
-      default:
-         /*
-          * Allocate space for storing the value.
-          */
-         current_xattr->value = (char *)malloc(xattr_value_len);
-         memset(current_xattr->value, 0, xattr_value_len);
-
-         xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
-         if (xattr_value_len < 0) {
-            berrno be;
-
-            switch (errno) {
-            case ENOENT:
-               retval = bxattr_rtn_ok;
-               break;
-            default:
-               Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
-                     jcr->last_fname, be.bstrerror());
-               Dmsg1(100, "%s", jcr->errmsg);
-               break;
-            }
-
-            /*
-             * Default failure path out when retrieval of attr fails.
-             */
-            free(current_xattr->value);
-            free(current_xattr->name);
-            free(current_xattr);
-            goto get_out;
-         }
-
-         /*
-          * Store the actual length of the value.
-          */
-         current_xattr->value_length = xattr_value_len;
-         expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
-         break;
-      }
-
-      if (xattr_value_list == NULL) {
-         xattr_value_list = New(alist(10, not_owned_by_alist));
-      }
-
-      xattr_value_list->append(current_xattr);
-      xattr_count++;
-
-      /*
-       * Protect ourself against things getting out of hand.
-       */
-      if (expected_serialize_len >= MAX_XATTR_LENGTH) {
-         Mmsg2(jcr->errmsg,
-               _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
-               jcr->last_fname, MAX_XATTR_LENGTH);
-         goto get_out;
-      }
-   }
-
-   free(xattr_list);
-   xattr_list = (char *)NULL;
-
-   /*
-    * If we found any xattr send them to the SD.
-    */
-   if (xattr_count > 0) {
-      /*
-       * Serialize the datastream.
-       */
-      if (serialize_xattr_stream(jcr, expected_serialize_len,
-                                 xattr_value_list) < expected_serialize_len) {
-         Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /*
-       * Send the datastream to the SD.
-       */
-      retval = send_xattr_stream(jcr, os_default_xattr_streams[0]);
-   } else {
-      retval = bxattr_rtn_ok;
-   }
-
-get_out:
-   if (xattr_list != NULL) {
-      free(xattr_list);
-   }
-   if (xattr_value_list != NULL) {
-      xattr_drop_internal_table(xattr_value_list);
-   }
-
-   return retval;
-}
-
-static bxattr_rtn_code generic_restore_xattr_streams(JCR *jcr, int stream,
-                                                    char *content,
-                                                    uint32_t content_length)
-{
-   xattr_t *current_xattr;
-   alist *xattr_value_list;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   xattr_value_list = New(alist(10, not_owned_by_alist));
-
-   if (unserialize_xattr_stream(jcr, content, content_length,
-                                xattr_value_list) != bxattr_rtn_ok) {
-      goto get_out;
-   }
-
-   foreach_alist(current_xattr, xattr_value_list) {
-      if (lsetxattr(jcr->last_fname, current_xattr->name, current_xattr->value, current_xattr->value_length, 0) != 0) {
-         berrno be;
-         if (errno == ENOENT) {
-            goto get_out;
-         } else if (errno == BXATTR_ENOTSUP) {
-            /*
-             * If the filesystem reports it doesn't support XATTRs we clear
-             * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
-             * on all other files on the same filesystem. The
-             * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
-             * change from one filesystem to an other.
-             */
-            jcr->xattr_ctx->flags &= ~BXATTR_FLAG_RESTORE_NATIVE;
-         }
-         MmsgD2(100, jcr->errmsg,
-                _("lsetxattr error on file \"%s\": ERR=%s\n"),
-                jcr->last_fname, be.bstrerror());
-         goto get_out;
-      }
-   }
-
-   retval = bxattr_rtn_ok;
-
-get_out:
-   xattr_drop_internal_table(xattr_value_list);
-   return retval;
-}
-
-/*
- * Function pointers to the build and restore functions to use for these xattrs.
- */
-static bxattr_rtn_code (*os_backup_xattr_streams)
-                        (JCR *jcr, FF_PKT *ff_pkt) =
-                        generic_xattr_build_streams;
-static bxattr_rtn_code (*os_restore_xattr_streams)
-                        (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                        generic_restore_xattr_streams;
-
-#elif defined(HAVE_FREEBSD_OS) || \
-      defined(HAVE_NETBSD_OS) || \
-      defined(HAVE_OPENBSD_OS)
-
-#if (!defined(HAVE_EXTATTR_GET_LINK) && !defined(HAVE_EXTATTR_GET_FILE)) || \
-    (!defined(HAVE_EXTATTR_SET_LINK) && !defined(HAVE_EXTATTR_SET_FILE)) || \
-    (!defined(HAVE_EXTATTR_LIST_LINK) && !defined(HAVE_EXTATTR_LIST_FILE)) || \
-    !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \
-    !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE)
-#error "Missing full support for the extattr functions."
-#endif
-
-#ifdef HAVE_SYS_EXTATTR_H
-#include <sys/extattr.h>
-#else
-#error "Missing sys/extattr.h header file"
-#endif
-
-#ifdef HAVE_LIBUTIL_H
-#include <libutil.h>
-#endif
-
-#if !defined(HAVE_EXTATTR_GET_LINK) && defined(HAVE_EXTATTR_GET_FILE)
-#define extattr_get_link extattr_get_file
-#endif
-#if !defined(HAVE_EXTATTR_SET_LINK) && defined(HAVE_EXTATTR_SET_FILE)
-#define extattr_set_link extattr_set_file
-#endif
-#if !defined(HAVE_EXTATTR_LIST_LINK) && defined(HAVE_EXTATTR_LIST_FILE)
-#define extattr_list_link extattr_list_file
-#endif
-
-#if defined(HAVE_FREEBSD_OS)
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_FREEBSD
-};
-static int os_default_xattr_namespaces[2] = {
-   EXTATTR_NAMESPACE_USER,
-   EXTATTR_NAMESPACE_SYSTEM
-};
-static const char *xattr_acl_skiplist[4] = {
-   "system.posix1e.acl_access",
-   "system.posix1e.acl_default",
-   "system.nfs4.acl",
-   NULL
-};
-static const char *xattr_skiplist[1] = {
-   NULL
-};
-#elif defined(HAVE_NETBSD_OS)
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_NETBSD
-};
-static int os_default_xattr_namespaces[2] = {
-   EXTATTR_NAMESPACE_USER,
-   EXTATTR_NAMESPACE_SYSTEM
-};
-static const char *xattr_acl_skiplist[1] = {
-   NULL
-};
-static const char *xattr_skiplist[1] = {
-   NULL
-};
-#elif defined(HAVE_OPENBSD_OS)
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_OPENBSD
-};
-static int os_default_xattr_namespaces[2] = {
-   EXTATTR_NAMESPACE_USER,
-   EXTATTR_NAMESPACE_SYSTEM
-};
-static const char *xattr_acl_skiplist[1] = {
-   NULL
-};
-static const char *xattr_skiplist[1] = {
-   NULL
-};
-#endif
-
-static bxattr_rtn_code bsd_backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   bool skip_xattr;
-   char *xattr_list = NULL;
-   int cnt, index, xattr_count = 0;
-   int32_t xattr_list_len,
-           xattr_value_len;
-   uint32_t expected_serialize_len = 0;
-   unsigned int namespace_index;
-   int attrnamespace;
-   char *current_attrnamespace = NULL;
-   char current_attrname[XATTR_BUFSIZ], current_attrtuple[XATTR_BUFSIZ];
-   xattr_t *current_xattr;
-   alist *xattr_value_list = NULL;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   /*
-    * Loop over all available xattr namespaces.
-    */
-   for (namespace_index = 0;
-        namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int);
-        namespace_index++) {
-      attrnamespace = os_default_xattr_namespaces[namespace_index];
-
-      /*
-       * First get the length of the available list with extended attributes.
-       * If we get EPERM on system namespace, don't return error.
-       * This is expected for normal users trying to archive the system
-       * namespace on FreeBSD 6.2 and later. On NetBSD 3.1 and later,
-       * they've decided to return EOPNOTSUPP instead.
-       */
-      xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, NULL, 0);
-      switch (xattr_list_len) {
-      case -1: {
-         berrno be;
-
-         switch (errno) {
-         case ENOENT:
-            retval = bxattr_rtn_ok;
-            goto get_out;
-#if defined(EOPNOTSUPP)
-         case EOPNOTSUPP:
-#endif
-         case EPERM:
-            if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM) {
-               continue;
-            }
-            /*
-             * FALLTHROUGH
-             */
-         default:
-            Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
-                  jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-         break;
-      }
-      case 0:
-         continue;
-      default:
-         break;
-      }
-
-      /*
-       * Allocate room for the extented attribute list.
-       */
-      xattr_list = (char *)malloc(xattr_list_len + 1);
-      memset(xattr_list, 0, xattr_list_len + 1);
-
-      /*
-       * Get the actual list of extended attributes names for a file.
-       */
-      xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace,
-                                         xattr_list, xattr_list_len);
-      if (xattr_list_len < 0) {
-         berrno be;
-         if (errno == ENOENT) {
-            retval = bxattr_rtn_ok;
-         } else {
-            Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
-                  jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-         }
-         goto get_out;
-      }
-      xattr_list[xattr_list_len] = '\0';
-
-      /*
-       * Convert the numeric attrnamespace into a string representation and make
-       * a private copy of that string. The extattr_namespace_to_string functions
-       * returns a strdupped string which we need to free.
-       */
-      if (extattr_namespace_to_string(attrnamespace, &current_attrnamespace) != 0) {
-         Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"),
-               attrnamespace, jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /*
-       * Walk the list of extended attributes names and retrieve the data.
-       * We already count the bytes needed for serializing the stream later on.
-       */
-      for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) {
-         skip_xattr = false;
-
-         /*
-          * Print the current name into the buffer as its not null terminated
-          * we need to use the length encoded in the string for copying only
-          * the needed bytes.
-          */
-         cnt = xattr_list[index];
-         if (cnt > ((int)sizeof(current_attrname) - 1)) {
-            cnt = ((int)sizeof(current_attrname) - 1);
-         }
-         strncpy(current_attrname, xattr_list + (index + 1), cnt);
-         current_attrname[cnt] = '\0';
-
-         /*
-          * First make a xattr tuple of the current namespace and the name of
-          * the xattr. e.g. something like user.<attrname> or system.<attrname>
-          */
-         bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s",
-                   current_attrnamespace, current_attrname);
-
-         /*
-          * On some OSes you also get the acls in the extented attribute list.
-          * So we check if we are already backing up acls and if we do we
-          * don't store the extended attribute with the same info.
-          */
-         if (ff_pkt->flags & FO_ACL) {
-            for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
-               if (bstrcmp(current_attrtuple, xattr_acl_skiplist[cnt])) {
-                  skip_xattr = true;
-                  break;
-               }
-            }
-         }
-
-         /*
-          * On some OSes we want to skip certain xattrs which are in the
-          * xattr_skiplist array.
-          */
-         if (!skip_xattr) {
-            for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
-               if (bstrcmp(current_attrtuple, xattr_skiplist[cnt])) {
-                  skip_xattr = true;
-                  break;
-               }
-            }
-         }
-
-         if (skip_xattr) {
-            Dmsg1(100, "Skipping xattr named %s\n", current_attrname);
-            continue;
-         }
-
-         /*
-          * First see how long the value is for the extended attribute.
-          */
-         xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace,
-                                            current_attrname, NULL, 0);
-         switch (xattr_value_len) {
-         case -1: {
-            berrno be;
-
-            switch (errno) {
-            case ENOENT:
-               retval = bxattr_rtn_ok;
-               goto get_out;
-            default:
-               Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
-                     jcr->last_fname, be.bstrerror());
-               Dmsg1(100, "%s", jcr->errmsg);
-               goto get_out;
-            }
-            break;
-         }
-         default:
-            break;
-         }
-
-         /*
-          * Each xattr valuepair starts with a magic so we can restore it easier.
-          */
-         current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
-         memset(current_xattr, 0, sizeof(xattr_t));
-         current_xattr->magic = XATTR_MAGIC;
-         expected_serialize_len += sizeof(current_xattr->magic);
-
-         /*
-          * Allocate space for storing the name.
-          */
-         current_xattr->name_length = strlen(current_attrtuple);
-         current_xattr->name = (char *)malloc(current_xattr->name_length);
-         memcpy(current_xattr->name, current_attrtuple, current_xattr->name_length);
-
-         expected_serialize_len += sizeof(current_xattr->name_length) +
-                                   current_xattr->name_length;
-
-         switch (xattr_value_len) {
-         case 0:
-            current_xattr->value = NULL;
-            current_xattr->value_length = 0;
-            expected_serialize_len += sizeof(current_xattr->value_length);
-            break;
-         default:
-            /*
-             * Allocate space for storing the value.
-             */
-            current_xattr->value = (char *)malloc(xattr_value_len);
-            memset(current_xattr->value, 0, xattr_value_len);
-
-            xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace,
-                                               current_attrname, current_xattr->value,
-                                               xattr_value_len);
-            if (xattr_value_len < 0) {
-               berrno be;
-
-               switch (errno) {
-               case ENOENT:
-                  retval = bxattr_rtn_ok;
-                  break;
-               default:
-                  Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
-                        jcr->last_fname, be.bstrerror());
-                  Dmsg1(100, "%s", jcr->errmsg);
-                  break;
-               }
-
-               /*
-                * Default failure path out when retrieval of attr fails.
-                */
-               free(current_xattr->value);
-               free(current_xattr->name);
-               free(current_xattr);
-               goto get_out;
-            }
-
-            /*
-             * Store the actual length of the value.
-             */
-            current_xattr->value_length = xattr_value_len;
-            expected_serialize_len += sizeof(current_xattr->value_length) +
-                                      current_xattr->value_length;
-            break;
-         }
-
-         if (xattr_value_list == NULL) {
-            xattr_value_list = New(alist(10, not_owned_by_alist));
-         }
-
-         xattr_value_list->append(current_xattr);
-         xattr_count++;
-
-         /*
-          * Protect ourself against things getting out of hand.
-          */
-         if (expected_serialize_len >= MAX_XATTR_LENGTH) {
-            Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
-                  jcr->last_fname, MAX_XATTR_LENGTH);
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      }
-
-      /*
-       * Drop the local copy of the current_attrnamespace.
-       */
-      actuallyfree(current_attrnamespace);
-      current_attrnamespace = NULL;
-
-      /*
-       * We are done with this xattr list.
-       */
-      free(xattr_list);
-      xattr_list = (char *)NULL;
-   }
-
-   /*
-    * If we found any xattr send them to the SD.
-    */
-   if (xattr_count > 0) {
-      /*
-       * Serialize the datastream.
-       */
-      if (serialize_xattr_stream(jcr,
-                                 expected_serialize_len,
-                                 xattr_value_list) < expected_serialize_len) {
-         Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /*
-       * Send the datastream to the SD.
-       */
-      retval = send_xattr_stream(jcr, os_default_xattr_streams[0]);
-   } else {
-      retval = bxattr_rtn_ok;
-   }
-
-get_out:
-   if (current_attrnamespace != NULL) {
-      actuallyfree(current_attrnamespace);
-   }
-   if (xattr_list != NULL) {
-      free(xattr_list);
-   }
-   if (xattr_value_list != NULL) {
-      xattr_drop_internal_table(xattr_value_list);
-   }
-
-   return retval;
-}
-
-static bxattr_rtn_code bsd_restore_xattr_streams(JCR *jcr,
-                                                int stream,
-                                                char *content,
-                                                uint32_t content_length)
-{
-   xattr_t *current_xattr;
-   alist *xattr_value_list;
-   int current_attrnamespace, cnt;
-   char *attrnamespace, *attrname;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   xattr_value_list = New(alist(10, not_owned_by_alist));
-
-   if (unserialize_xattr_stream(jcr,
-                                content,
-                                content_length,
-                                xattr_value_list) != bxattr_rtn_ok) {
-      goto get_out;
-   }
-
-   foreach_alist(current_xattr, xattr_value_list) {
-      /*
-       * Try splitting the xattr_name into a namespace and name part.
-       * The splitting character is a .
-       */
-      attrnamespace = current_xattr->name;
-      if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) {
-         Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"),
-               current_xattr->name, jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-      *attrname++ = '\0';
-
-      /*
-       * Make sure the attrnamespace makes sense.
-       */
-      if (extattr_string_to_namespace(attrnamespace, &current_attrnamespace) != 0) {
-         Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"),
-               attrnamespace, jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /*
-       * Try restoring the extended attribute.
-       */
-      cnt = extattr_set_link(jcr->last_fname, current_attrnamespace,
-                             attrname, current_xattr->value, current_xattr->value_length);
-      if (cnt < 0 || cnt != (int)current_xattr->value_length) {
-         berrno be;
-         if (errno == ENOENT) {
-            goto get_out;
-         } else {
-            Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"),
-                  jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      }
-   }
-
-   retval = bxattr_rtn_ok;
-
-get_out:
-   xattr_drop_internal_table(xattr_value_list);
-   return retval;
-}
-
-/*
- * Function pointers to the build and restore function to use for these xattrs.
- */
-static bxattr_rtn_code (*os_backup_xattr_streams)
-                        (JCR *jcr, FF_PKT *ff_pkt) =
-                        bsd_backup_xattr_streams;
-static bxattr_rtn_code (*os_restore_xattr_streams)
-                        (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                        bsd_restore_xattr_streams;
-
-#elif defined(HAVE_OSF1_OS)
-
-#if !defined(HAVE_GETPROPLIST) || \
-    !defined(HAVE_GET_PROPLIST_ENTRY) || \
-    !defined(HAVE_SIZEOF_PROPLIST_ENTRY) || \
-    !defined(HAVE_ADD_PROPLIST_ENTRY) || \
-    !defined(HAVE_SETPROPLIST)
-#error "Missing full support for the Extended Attributes functions."
-#endif
-
-#ifdef HAVE_SYS_PROPLIST_H
-#include <sys/proplist.h>
-#else
-#error "Missing sys/proplist.h header file"
-#endif
-
-/*
- * Define the supported XATTR streams for this OS
- */
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_TRU64
-};
-static const char *xattr_acl_skiplist[1] = {
-   NULL
-};
-static const char *xattr_skiplist[1] = {
-   NULL
-};
-
-static bxattr_rtn_code tru64_backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   int cnt;
-   char *bp,
-        *xattr_name,
-        *xattr_value;
-   bool skip_xattr;
-   int xattr_count = 0;
-   int32_t *flags,
-           *xattr_value_len;
-   int32_t xattr_list_len,
-           xattrbuf_size,
-           xattrbuf_min_size;
-   uint32_t expected_serialize_len = 0;
-   xattr_t *current_xattr;
-   alist *xattr_value_list = NULL;
-   struct proplistname_args prop_args;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-   POOLMEM *xattrbuf = get_pool_memory(PM_MESSAGE);
-
-   xattrbuf_size = sizeof_pool_memory(xattrbuf);
-   xattrbuf_min_size = 0;
-   xattr_list_len = getproplist(jcr->last_fname, 1, &prop_args, xattrbuf_size,
-                                xattrbuf, &xattrbuf_min_size);
-
-   /*
-    * See what xattr are available.
-    */
-   switch (xattr_list_len) {
-   case -1: {
-      berrno be;
-
-      switch (errno) {
-      case EOPNOTSUPP:
-         /*
-          * If the filesystem reports it doesn't support XATTRs we clear
-          * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
-          * on all other files on the same filesystem. The
-          * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
-          * change from one filesystem to an other.
-          */
-         jcr->xattr_ctx->flags &= ~BXATTR_FLAG_SAVE_NATIVE;
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      default:
-         Mmsg2(jcr->errmsg, _("getproplist error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-      break;
-   }
-   case 0:
-      if (xattrbuf_min_size) {
-         /*
-          * The buffer isn't big enough to hold the xattr data, we now have
-          * a minimum buffersize so we resize the buffer and try again.
-          */
-         xattrbuf = check_pool_memory_size(xattrbuf, xattrbuf_min_size + 1);
-         xattrbuf_size = xattrbuf_min_size + 1;
-         xattr_list_len = getproplist(jcr->last_fname, 1, &prop_args, xattrbuf_size,
-                                   xattrbuf, &xattrbuf_min_size);
-         switch (xattr_list_len) {
-         case -1: {
-            berrno be;
-
-            switch (errno) {
-            default:
-               Mmsg2(jcr->errmsg, _("getproplist error on file \"%s\": ERR=%s\n"),
-                     jcr->last_fname, be.bstrerror());
-               Dmsg1(100, "%s", jcr->errmsg);
-               goto get_out;
-            }
-            break;
-         }
-         case 0:
-            /*
-             * This should never happen as we sized the buffer according to the minimumsize
-             * returned by a previous getproplist call. If it does happen things are fishy and
-             * we are better of forgetting this xattr as it seems its list is changing at this
-             * exact moment so we can never make a good backup copy of it.
-             */
-            retval = bxattr_rtn_ok;
-            goto get_out;
-         default:
-            break;
-         }
-      } else {
-         /*
-          * No xattr on file.
-          */
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      }
-      break;
-   default:
-      break;
-   }
-
-   /*
-    * Walk the list of extended attributes names and retrieve the data.
-    * We already count the bytes needed for serializing the stream later on.
-    */
-   bp = xattrbuf;
-   while (xattrbuf_size > 0) {
-      /*
-       * Call getproplist_entry to initialize name and value
-       * pointers to entries position within buffer.
-       */
-      xattrbuf_size -= get_proplist_entry(&xattr_name, &flags, &xattr_value_len, &xattr_value, &bp);
-
-      /*
-       * On some OSes you also get the acls in the extented attribute list.
-       * So we check if we are already backing up acls and if we do we
-       * don't store the extended attribute with the same info.
-       */
-      if (ff_pkt->flags & FO_ACL) {
-         for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
-            if (bstrcmp(xattr_name, xattr_acl_skiplist[cnt])) {
-               skip_xattr = true;
-               break;
-            }
-         }
-      }
-
-      /*
-       * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
-       */
-      if (!skip_xattr) {
-         for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
-            if (bstrcmp(xattr_name, xattr_skiplist[cnt])) {
-               skip_xattr = true;
-               break;
-            }
-         }
-      }
-
-      if (skip_xattr) {
-         Dmsg1(100, "Skipping xattr named %s\n", xattr_name);
-         continue;
-      }
-
-      /*
-       * Each xattr valuepair starts with a magic so we can restore it easier.
-       */
-      current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
-      memset(current_xattr, 0, sizeof(xattr_t));
-      current_xattr->magic = XATTR_MAGIC;
-      expected_serialize_len += sizeof(current_xattr->magic);
-
-      current_xattr->name_length = strlen(xattr_name);
-      current_xattr->name = bstrdup(xattr_name);
-
-      expected_serialize_len += sizeof(current_xattr->name_length) +
-                                current_xattr->name_length;
-
-      current_xattr->value_length = *xattr_value_len;
-      current_xattr->value = (char *)malloc(current_xattr->value_length);
-      memcpy(current_xattr->value, xattr_value, current_xattr->value_length);
-
-      expected_serialize_len += sizeof(current_xattr->value_length) +
-                                current_xattr->value_length;
-
-      if (xattr_value_list == NULL) {
-         xattr_value_list = New(alist(10, not_owned_by_alist));
-      }
-
-      xattr_value_list->append(current_xattr);
-      xattr_count++;
-
-      /*
-       * Protect ourself against things getting out of hand.
-       */
-      if (expected_serialize_len >= MAX_XATTR_LENGTH) {
-         Mmsg2(jcr->errmsg,
-               _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
-               jcr->last_fname, MAX_XATTR_LENGTH);
-         goto get_out;
-      }
-   }
-
-   /*
-    * If we found any xattr send them to the SD.
-    */
-   if (xattr_count > 0) {
-      /*
-       * Serialize the datastream.
-       */
-      if (serialize_xattr_stream(jcr,
-                                 expected_serialize_len,
-                                 xattr_value_list) < expected_serialize_len) {
-         Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /*
-       * Send the datastream to the SD.
-       */
-      retval = send_xattr_stream(jcr, os_default_xattr_streams[0]);
-   } else {
-      retval = bxattr_rtn_ok;
-   }
-
-get_out:
-   if (xattr_value_list != NULL) {
-      xattr_drop_internal_table(xattr_value_list);
-   }
-   free_pool_memory(xattrbuf);
-
-   return retval;
-}
-
-static bxattr_rtn_code tru64_restore_xattr_streams(JCR *jcr,
-                                                  int stream,
-                                                  char *content,
-                                                  uint32_t content_length)
-{
-   char *bp, *xattrbuf = NULL;
-   int32_t xattrbuf_size, cnt;
-   xattr_t *current_xattr;
-   alist *xattr_value_list;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   xattr_value_list = New(alist(10, not_owned_by_alist));
-
-   if (unserialize_xattr_stream(jcr,
-                                content,
-                                content_length,
-                                xattr_value_list) != bxattr_rtn_ok) {
-      goto get_out;
-   }
-
-   /*
-    * See how big the propertylist must be.
-    */
-   xattrbuf_size = 0;
-   foreach_alist(current_xattr, xattr_value_list) {
-      xattrbuf_size += sizeof_proplist_entry(current_xattr->name, current_xattr->value_length);
-   }
-
-   xattrbuf = (char *)malloc(xattrbuf_size);
-
-   /*
-    * Add all value pairs to the proplist.
-    */
-   cnt = 0;
-   bp = xattrbuf;
-   foreach_alist(current_xattr, xattr_value_list) {
-      cnt = add_proplist_entry(current_xattr->name, 0, current_xattr->value_length,
-                               current_xattr->value, &bp);
-   }
-
-   /*
-    * Sanity check.
-    */
-   if (cnt != xattrbuf_size) {
-      Mmsg1(jcr->errmsg, _("Unable create proper proplist to restore xattrs on file \"%s\"\n"),
-            jcr->last_fname);
-      Dmsg1(100, "%s", jcr->errmsg);
-      goto get_out;
-   }
-
-   /*
-    * Restore the list of extended attributes on the file.
-    */
-   cnt = setproplist(jcr->last_fname, 1, xattrbuf_size, xattrbuf);
-   switch (cnt) {
-   case -1: {
-      berrno be;
-
-      switch (errno) {
-      case EOPNOTSUPP:
-         /*
-          * If the filesystem reports it doesn't support XATTRs we clear
-          * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
-          * on all other files on the same filesystem. The
-          * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
-          * change from one filesystem to an other.
-          */
-         jcr->xattr_ctx->flags &= ~BXATTR_FLAG_RESTORE_NATIVE;
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      default:
-         Mmsg2(jcr->errmsg, _("setproplist error on file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-      break;
-   }
-   default:
-      break;
-   }
-
-   retval = bxattr_rtn_ok;
-
-get_out:
-   if (xattrbuf) {
-      free(xattrbuf);
-   }
-   xattr_drop_internal_table(xattr_value_list);
-
-   return retval;
-}
-
-/*
- * Function pointers to the build and restore function to use for these xattrs.
- */
-static bxattr_rtn_code (*os_backup_xattr_streams)
-                        (JCR *jcr, FF_PKT *ff_pkt) =
-                        tru64_backup_xattr_streams;
-static bxattr_rtn_code (*os_restore_xattr_streams)
-                        (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                        tru64_restore_xattr_streams;
-
-#elif defined(HAVE_SUN_OS)
-/*
- * Solaris extended attributes were introduced in Solaris 9
- * by PSARC 1999/209
- *
- * Solaris extensible attributes were introduced in OpenSolaris
- * by PSARC 2007/315 Solaris extensible attributes are also
- * sometimes called extended system attributes.
- *
- * man fsattr(5) on Solaris gives a wealth of info. The most
- * important bits are:
- *
- * Attributes are logically supported as files within the  file
- * system.   The  file  system  is  therefore augmented with an
- * orthogonal name space of file attributes. Any file  (includ-
- * ing  attribute files) can have an arbitrarily deep attribute
- * tree associated with it. Attribute values  are  accessed  by
- * file descriptors obtained through a special attribute inter-
- * face.  This logical view of "attributes as files" allows the
- * leveraging  of  existing file system interface functionality
- * to support the construction, deletion, and  manipulation  of
- * attributes.
- *
- * The special files  "."  and  ".."  retain  their  accustomed
- * semantics within the attribute hierarchy.  The "." attribute
- * file refers to the current directory and the ".."  attribute
- * file  refers to the parent directory.  The unnamed directory
- * at the head of each attribute tree is considered the "child"
- * of  the  file it is associated with and the ".." file refers
- * to the associated file.  For  any  non-directory  file  with
- * attributes,  the  ".." entry in the unnamed directory refers
- * to a file that is not a directory.
- *
- * Conceptually, the attribute model is fully general. Extended
- * attributes  can  be  any  type of file (doors, links, direc-
- * tories, and so forth) and can even have their own attributes
- * (fully  recursive).   As a result, the attributes associated
- * with a file could be an arbitrarily deep directory hierarchy
- * where each attribute could have an equally complex attribute
- * tree associated with it.  Not all implementations  are  able
- * to,  or  want to, support the full model. Implementation are
- * therefore permitted to reject operations that are  not  sup-
- * ported.   For  example,  the implementation for the UFS file
- * system allows only regular files as attributes (for example,
- * no sub-directories) and rejects attempts to place attributes
- * on attributes.
- *
- * The following list details the operations that are  rejected
- * in the current implementation:
- *
- * link                     Any attempt to create links between
- *                          attribute  and  non-attribute space
- *                          is rejected  to  prevent  security-
- *                          related   or   otherwise  sensitive
- *                          attributes from being exposed,  and
- *                          therefore  manipulable,  as regular
- *                          files.
- *
- * rename                   Any  attempt  to   rename   between
- *                          attribute  and  non-attribute space
- *                          is rejected to prevent  an  already
- *                          linked  file from being renamed and
- *                          thereby circumventing the link res-
- *                          triction above.
- *
- * mkdir, symlink, mknod    Any  attempt  to  create  a   "non-
- *                          regular" file in attribute space is
- *                          rejected to reduce the  functional-
- *                          ity,  and  therefore  exposure  and
- *                          risk, of  the  initial  implementa-
- *                          tion.
- *
- * The entire available name space has been allocated to  "gen-
- * eral use" to bring the implementation in line with the NFSv4
- * draft standard [NFSv4]. That standard defines "named  attri-
- * butes"  (equivalent  to Solaris Extended Attributes) with no
- * naming restrictions.  All Sun  applications  making  use  of
- * opaque extended attributes will use the prefix "SUNW".
- *
- */
-#ifdef HAVE_SYS_ATTR_H
-#include <sys/attr.h>
-#endif
-
-#ifdef HAVE_ATTR_H
-#include <attr.h>
-#endif
-
-#ifdef HAVE_SYS_NVPAIR_H
-#include <sys/nvpair.h>
-#endif
-
-#ifdef HAVE_SYS_ACL_H
-#include <sys/acl.h>
-#endif
-
-#if !defined(HAVE_OPENAT) || \
-    !defined(HAVE_UNLINKAT) || \
-    !defined(HAVE_FCHOWNAT) || \
-    !defined(HAVE_FUTIMESAT)
-#error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function"
-#endif
-
-/*
- * Define the supported XATTR streams for this OS
- */
-#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
-static int os_default_xattr_streams[2] = {
-   STREAM_XATTR_SOLARIS,
-   STREAM_XATTR_SOLARIS_SYS
-};
-#else
-static int os_default_xattr_streams[1] = {
-   STREAM_XATTR_SOLARIS
-};
-#endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
-
-/*
- * This code creates a temporary cache with entries for each xattr which has
- * a link count > 1 (which indicates it has one or more hard linked counterpart(s))
- */
-static inline xattr_link_cache_entry_t *find_xattr_link_cache_entry(JCR *jcr, ino_t inum)
-{
-   xattr_link_cache_entry_t *ptr;
-
-   foreach_alist(ptr, jcr->xattr_ctx->link_cache) {
-      if (ptr && ptr->inum == inum) {
-         return ptr;
-      }
-   }
-   return NULL;
-}
-
-static inline void add_xattr_link_cache_entry(JCR *jcr, ino_t inum, char *target)
-{
-   xattr_link_cache_entry_t *ptr;
-
-   ptr = (xattr_link_cache_entry_t *)malloc(sizeof(xattr_link_cache_entry_t));
-   memset(ptr, 0, sizeof(xattr_link_cache_entry_t));
-   ptr->inum = inum;
-   ptr->target = bstrdup(target);
-
-   if (!jcr->xattr_ctx->link_cache) {
-      jcr->xattr_ctx->link_cache = New(alist(10, not_owned_by_alist));
-   }
-   jcr->xattr_ctx->link_cache->append(ptr);
-}
-
-static inline void drop_xattr_link_cache(JCR *jcr)
-{
-   xattr_link_cache_entry_t *ptr;
-
-   /*
-    * Walk the list of xattr link cache entries and free allocated memory on traversing.
-    */
-   foreach_alist(ptr, jcr->xattr_ctx->link_cache) {
-      free(ptr->target);
-      free(ptr);
-   }
-
-   delete jcr->xattr_ctx->link_cache;
-   jcr->xattr_ctx->link_cache = NULL;
-}
-
-#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
-/*
- * This function returns true if a non default extended system attribute
- * list is associated with fd and returns false when an error has occured
- * or when only extended system attributes other than archive,
- * av_modified or crtime are set.
- *
- * The function returns true for the following cases:
- *
- * - any extended system attribute other than the default attributes
- *   ('archive', 'av_modified' and 'crtime') is set
- * - nvlist has NULL name string
- * - nvpair has data type of 'nvlist'
- * - default data type.
- */
-static bool solaris_has_non_transient_extensible_attributes(int fd)
-{
-   boolean_t value;
-   data_type_t type;
-   nvlist_t *response;
-   nvpair_t *pair;
-   f_attr_t fattr;
-   char *name;
-   bool retval = false;
-
-   if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
-      return false;
-   }
-
-   pair = NULL;
-   while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
-      name = nvpair_name(pair);
-
-      if (name != NULL) {
-         fattr = name_to_attr(name);
-      } else {
-         retval = true;
-         goto get_out;
-      }
-
-      type = nvpair_type(pair);
-      switch (type) {
-      case DATA_TYPE_BOOLEAN_VALUE:
-         if (nvpair_value_boolean_value(pair, &value) != 0) {
-            continue;
-         }
-         if (value && fattr != F_ARCHIVE &&
-                      fattr != F_AV_MODIFIED) {
-            retval = true;
-            goto get_out;
-         }
-         break;
-      case DATA_TYPE_UINT64_ARRAY:
-         if (fattr != F_CRTIME) {
-            retval = true;
-            goto get_out;
-         }
-         break;
-      case DATA_TYPE_NVLIST:
-      default:
-         retval = true;
-         goto get_out;
-      }
-   }
-
-get_out:
-   if (response != NULL) {
-      nvlist_free(response);
-   }
-   return retval;
-}
-#endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
-
-#if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
-/*
- * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
- * There is no need to store those acls as we already store the stat bits too.
- */
-static bool acl_is_trivial(int count, aclent_t *entries)
-{
-   int n;
-   aclent_t *ace;
-
-   for (n = 0; n < count; n++) {
-      ace = &entries[n];
-      if (!(ace->a_type == USER_OBJ ||
-            ace->a_type == GROUP_OBJ ||
-            ace->a_type == OTHER_OBJ ||
-            ace->a_type == CLASS_OBJ))
-        return false;
-   }
-   return true;
-}
-#endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
-
-static bxattr_rtn_code solaris_save_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text)
-{
-   bxattr_rtn_code retval = bxattr_rtn_error;
-#ifdef HAVE_ACL
-#ifdef HAVE_EXTENDED_ACL
-   int flags;
-   acl_t *aclp = NULL;
-
-   /*
-    * See if this attribute has an ACL
-    */
-   if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) ||
-       pathconf(attrname, _PC_ACL_ENABLED) > 0) {
-      /*
-       * See if there is a non trivial acl on the file.
-       */
-      if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) ||
-           acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
-         berrno be;
-
-         switch (errno) {
-         case ENOENT:
-            retval = bxattr_rtn_ok;
-            goto get_out;
-         default:
-            Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
-                  attrname, jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      }
-
-      if (aclp != NULL) {
-#if defined(ACL_SID_FMT)
-         /*
-          * New format flag added in newer Solaris versions.
-          */
-         flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
-#else
-         flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
-#endif /* ACL_SID_FMT */
-
-         *acl_text = acl_totext(aclp, flags);
-         acl_free(aclp);
-      } else {
-         *acl_text = NULL;
-      }
-   } else {
-      *acl_text = NULL;
-   }
-   retval = bxattr_rtn_ok;
-#else /* HAVE_EXTENDED_ACL */
-   int n;
-   aclent_t *acls = NULL;
-
-   /*
-    * See if this attribute has an ACL
-    */
-   if (fd != -1) {
-      n = facl(fd, GETACLCNT, 0, NULL);
-   } else {
-      n = acl(attrname, GETACLCNT, 0, NULL);
-   }
-
-   if (n >= MIN_ACL_ENTRIES) {
-      acls = (aclent_t *)malloc(n * sizeof(aclent_t));
-      if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
-          acl(attrname, GETACL, n, acls) != n) {
-         berrno be;
-
-         switch (errno) {
-         case ENOENT:
-            free(acls);
-            retval = bxattr_rtn_ok;
-            goto get_out;
-         default:
-            Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
-                  attrname, jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            free(acls);
-            goto get_out;
-         }
-      }
-
-      /*
-       * See if there is a non trivial acl on the file.
-       */
-      if (!acl_is_trivial(n, acls)) {
-         if ((*acl_text = acltotext(acls, n)) == NULL) {
-            berrno be;
-
-            Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
-                  attrname, jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            free(acls);
-            goto get_out;
-         }
-      } else {
-         *acl_text = NULL;
-      }
-
-     free(acls);
-   } else {
-      *acl_text = NULL;
-   }
-   retval = bxattr_rtn_ok;
-#endif /* HAVE_EXTENDED_ACL */
-
-#else /* HAVE_ACL */
-   retval = bxattr_rtn_ok;
-#endif /* HAVE_ACL */
-
-get_out:
-   return retval;
-}
-
-/*
- * Forward declaration for recursive function call.
- */
-static bxattr_rtn_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent);
-
-/*
- * Save an extended or extensible attribute.
- * This is stored as an opaque stream of bytes with the following encoding:
- *
- * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_ctx>
- *
- * or for a hardlinked or symlinked attribute
- *
- * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
- *
- * xattr_name can be a subpath relative to the file the xattr is on.
- * stat_buffer is the string representation of the stat struct.
- * acl_string is an acl text when a non trivial acl is set on the xattr.
- * actual_xattr_ctx is the content of the xattr file.
- */
-static bxattr_rtn_code solaris_save_xattr(JCR *jcr, int fd, const char *xattr_namespace,
-                                           const char *attrname, bool toplevel_hidden_dir, int stream)
-{
-   int cnt;
-   int attrfd = -1;
-   struct stat st;
-   xattr_link_cache_entry_t *xlce;
-   char target_attrname[PATH_MAX];
-   char link_source[PATH_MAX];
-   char *acl_text = NULL;
-   char attribs[MAXSTRING];
-   char buffer[XATTR_BUFSIZ];
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname);
-
-   /*
-    * Get the stats of the extended or extensible attribute.
-    */
-   if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-      berrno be;
-
-      switch (errno) {
-      case ENOENT:
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      default:
-         Mmsg3(jcr->errmsg, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
-               target_attrname, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-   }
-
-   /*
-    * Based on the filetype perform the correct action. We support most filetypes here, more
-    * then the actual implementation on Solaris supports so some code may never get executed
-    * due to limitations in the implementation.
-    */
-   switch (st.st_mode & S_IFMT) {
-   case S_IFIFO:
-   case S_IFCHR:
-   case S_IFBLK:
-      /*
-       * Get any acl on the xattr.
-       */
-      if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_rtn_ok)
-         goto get_out;
-
-      /*
-       * The current implementation of xattr on Solaris doesn't support this,
-       * but if it ever does we are prepared.
-       * Encode the stat struct into an ASCII representation.
-       */
-      encode_stat(attribs, &st, sizeof(st), 0, stream);
-      cnt = bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
-                      target_attrname, 0, attribs, 0,
-                      (acl_text) ? acl_text : "", 0);
-      break;
-   case S_IFDIR:
-      /*
-       * Get any acl on the xattr.
-       */
-      if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_rtn_ok)
-         goto get_out;
-
-      /*
-       * See if this is the toplevel_hidden_dir being saved.
-       */
-      if (toplevel_hidden_dir) {
-         /*
-          * Save the data for later storage when we encounter a real xattr.
-          * We store the data in the jcr->xattr_ctx->content buffer
-          * and flush that just before sending out the first real xattr.
-          * Encode the stat struct into an ASCII representation and jump
-          * out of the function.
-          */
-         encode_stat(attribs, &st, sizeof(st), 0, stream);
-         cnt = bsnprintf(buffer, sizeof(buffer),
-                         "%s%c%s%c%s%c",
-                         target_attrname, 0, attribs, 0,
-                         (acl_text) ? acl_text : "", 0);
-         pm_memcpy(jcr->xattr_ctx->content, buffer, cnt);
-         jcr->xattr_ctx->content_length = cnt;
-         goto get_out;
-      } else {
-         /*
-          * The current implementation of xattr on Solaris doesn't support this,
-          * but if it ever does we are prepared.
-          * Encode the stat struct into an ASCII representation.
-          */
-         encode_stat(attribs, &st, sizeof(st), 0, stream);
-         cnt = bsnprintf(buffer, sizeof(buffer),
-                         "%s%c%s%c%s%c",
-                         target_attrname, 0, attribs, 0,
-                         (acl_text) ? acl_text : "", 0);
-      }
-      break;
-   case S_IFREG:
-      /*
-       * If this is a hardlinked file check the inode cache for a hit.
-       */
-      if (st.st_nlink > 1) {
-         /*
-          * See if the cache already knows this inode number.
-          */
-         if ((xlce = find_xattr_link_cache_entry(jcr, st.st_ino)) != NULL) {
-            /*
-             * Generate a xattr encoding with the reference to the target in there.
-             */
-            encode_stat(attribs, &st, sizeof(st), st.st_ino, stream);
-            cnt = bsnprintf(buffer, sizeof(buffer),
-                            "%s%c%s%c%s%c",
-                            target_attrname, 0, attribs, 0, xlce->target, 0);
-            pm_memcpy(jcr->xattr_ctx->content, buffer, cnt);
-            jcr->xattr_ctx->content_length = cnt;
-            retval = send_xattr_stream(jcr, stream);
-
-            /*
-             * For a hard linked file we are ready now, no need to recursively
-             * save the attributes.
-             */
-            goto get_out;
-         }
-
-         /*
-          * Store this hard linked file in the cache.
-          * Store the name relative to the top level xattr space.
-          */
-         add_xattr_link_cache_entry(jcr, st.st_ino, target_attrname + 1);
-      }
-
-      /*
-       * Get any acl on the xattr.
-       */
-      if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_rtn_ok) {
-         goto get_out;
-      }
-
-      /*
-       * Encode the stat struct into an ASCII representation.
-       */
-      encode_stat(attribs, &st, sizeof(st), 0, stream);
-      cnt = bsnprintf(buffer, sizeof(buffer),
-                     "%s%c%s%c%s%c",
-                     target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
-
-      /*
-       * Open the extended or extensible attribute file.
-       */
-      if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
-         berrno be;
-
-         switch (errno) {
-         case ENOENT:
-            retval = bxattr_rtn_ok;
-            goto get_out;
-         default:
-            Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
-                  target_attrname, jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      }
-      break;
-   case S_IFLNK:
-      /*
-       * The current implementation of xattr on Solaris doesn't support this, but if it
-       * ever does we are prepared.
-       * Encode the stat struct into an ASCII representation.
-       */
-      if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
-         berrno be;
-
-         switch (errno) {
-         case ENOENT:
-            retval = bxattr_rtn_ok;
-            goto get_out;
-         default:
-            Mmsg3(jcr->errmsg, _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
-                  target_attrname, jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      }
-
-      /*
-       * Generate a xattr encoding with the reference to the target in there.
-       */
-      encode_stat(attribs, &st, sizeof(st), st.st_ino, stream);
-      cnt = bsnprintf(buffer, sizeof(buffer),
-                      "%s%c%s%c%s%c",
-                      target_attrname, 0, attribs, 0, link_source, 0);
-      pm_memcpy(jcr->xattr_ctx->content, buffer, cnt);
-      jcr->xattr_ctx->content_length = cnt;
-      retval = send_xattr_stream(jcr, stream);
-
-      if (retval == bxattr_rtn_ok) {
-         jcr->xattr_ctx->nr_saved++;
-      }
-
-      /*
-       * For a soft linked file we are ready now, no need to recursively save the attributes.
-       */
-      goto get_out;
-   default:
-      goto get_out;
-   }
-
-   /*
-    * See if this is the first real xattr being saved.
-    * If it is save the toplevel_hidden_dir attributes first.
-    * This is easy as its stored already in the
-    * jcr->xattr_ctx->content buffer.
-    */
-   if (jcr->xattr_ctx->nr_saved == 0) {
-      retval = send_xattr_stream(jcr, STREAM_XATTR_SOLARIS);
-      if (retval != bxattr_rtn_ok) {
-         goto get_out;
-      }
-      jcr->xattr_ctx->nr_saved++;
-   }
-
-   pm_memcpy(jcr->xattr_ctx->content, buffer, cnt);
-   jcr->xattr_ctx->content_length = cnt;
-
-   /*
-    * Only dump the content of regular files.
-    */
-   switch (st.st_mode & S_IFMT) {
-   case S_IFREG:
-      if (st.st_size > 0) {
-         /*
-          * Protect ourself against things getting out of hand.
-          */
-         if (st.st_size >= MAX_XATTR_LENGTH) {
-            Mmsg2(jcr->errmsg,
-                  _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
-                  jcr->last_fname, MAX_XATTR_LENGTH);
-            goto get_out;
-         }
-
-         while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
-            jcr->xattr_ctx->content =
-               check_pool_memory_size(jcr->xattr_ctx->content,
-                  jcr->xattr_ctx->content_length + cnt);
-            memcpy(jcr->xattr_ctx->content +
-                   jcr->xattr_ctx->content_length, buffer, cnt);
-            jcr->xattr_ctx->content_length += cnt;
-         }
-
-         if (cnt < 0) {
-            Mmsg2(jcr->errmsg, _("Unable to read content of xattr %s on file \"%s\"\n"),
-                  target_attrname, jcr->last_fname);
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      }
-      break;
-
-   default:
-      break;
-   }
-
-   /*
-    * We build a new xattr stream send it to the SD.
-    */
-   retval = send_xattr_stream(jcr, stream);
-   if (retval != bxattr_rtn_ok) {
-       goto get_out;
-   }
-   jcr->xattr_ctx->nr_saved++;
-
-   /*
-    * Recursivly call solaris_save_extended_attributes for archiving the attributes
-    * available on this extended attribute.
-    */
-   retval = solaris_save_xattrs(jcr, xattr_namespace, attrname);
-
-   /*
-    * The recursive call could change our working dir so change back to the wanted workdir.
-    */
-   if (fchdir(fd) < 0) {
-      berrno be;
-
-      switch (errno) {
-      case ENOENT:
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      default:
-         Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-   }
-
-get_out:
-   if (acl_text != NULL) {
-      free(acl_text);
-   }
-   if (attrfd != -1) {
-      close(attrfd);
-   }
-   return retval;
-}
-
-static bxattr_rtn_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent)
-{
-   const char *name;
-   int fd, filefd = -1, attrdirfd = -1;
-   DIR *dirp;
-   struct dirent *dp;
-   char current_xattr_namespace[PATH_MAX];
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   /*
-    * Determine what argument to use. Use attr_parent when set
-    * (recursive call) or jcr->last_fname for first call. Also save
-    * the current depth of the xattr_space we are in.
-    */
-   if (attr_parent) {
-      name = attr_parent;
-      if (xattr_namespace) {
-         bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/",
-                   xattr_namespace, attr_parent);
-      } else {
-         bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
-      }
-   } else {
-      name = jcr->last_fname;
-      bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
-   }
-
-   /*
-    * Open the file on which to save the xattrs read-only.
-    */
-   if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
-      berrno be;
-
-      switch (errno) {
-      case ENOENT:
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      default:
-         Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-   }
-
-   /*
-    * Open the xattr naming space.
-    */
-   if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
-      berrno be;
-
-      switch (errno) {
-      case EINVAL:
-         /*
-          * Gentile way of the system saying this type of xattr layering is not supported.
-          * Which is not problem we just forget about this this xattr.
-          * But as this is not an error we return a positive return value.
-          */
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      case ENOENT:
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      default:
-         Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
-               name, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-   }
-
-  /*
-   * We need to change into the attribute directory to determine if each of the
-   * attributes should be saved.
-   */
-   if (fchdir(attrdirfd) < 0) {
-      berrno be;
-
-      Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      goto get_out;
-   }
-
-   /*
-    * Save the data of the toplevel xattr hidden_dir. We save this one before anything
-    * else because the readdir returns "." entry after the extensible attr entry.
-    * And as we want this entry before anything else we better just save its data.
-    */
-   if (!attr_parent)
-      solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, ".",
-                         true, STREAM_XATTR_SOLARIS);
-
-   if ((fd = dup(attrdirfd)) == -1 ||
-       (dirp = fdopendir(fd)) == (DIR *)NULL) {
-      berrno be;
-
-      Mmsg2(jcr->errmsg, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-
-      goto get_out;
-   }
-
-   /*
-    * Walk the namespace.
-    */
-   while ((dp = readdir(dirp)) != NULL) {
-      /*
-       * Skip only the toplevel . dir.
-       */
-      if (!attr_parent && bstrcmp(dp->d_name, "."))
-         continue;
-
-      /*
-       * Skip all .. directories
-       */
-      if (bstrcmp(dp->d_name, ".."))
-         continue;
-
-      Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
-         current_xattr_namespace, dp->d_name, jcr->last_fname);
-
-#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
-      /*
-       * We are not interested in read-only extensible attributes.
-       */
-      if (bstrcmp(dp->d_name, VIEW_READONLY)) {
-         Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
-            current_xattr_namespace, dp->d_name, jcr->last_fname);
-
-         continue;
-      }
-
-      /*
-       * We are only interested in read-write extensible attributes
-       * when they contain non-transient values.
-       */
-      if (bstrcmp(dp->d_name, VIEW_READWRITE)) {
-         /*
-          * Determine if there are non-transient system attributes at the toplevel.
-          * We need to provide a fd to the open file.
-          */
-         if (!solaris_has_non_transient_extensible_attributes(filefd)) {
-            Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n",
-               current_xattr_namespace, dp->d_name, jcr->last_fname);
-            continue;
-         }
-
-         /*
-          * Save the xattr.
-          */
-         solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
-                            false, STREAM_XATTR_SOLARIS_SYS);
-         continue;
-      }
-#endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
-
-      /*
-       * Save the xattr.
-       */
-      solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
-                         false, STREAM_XATTR_SOLARIS);
-   }
-
-   closedir(dirp);
-   retval = bxattr_rtn_ok;
-
-get_out:
-   if (attrdirfd != -1)
-      close(attrdirfd);
-   if (filefd != -1)
-      close(filefd);
-   return retval;
-}
-
-#ifdef HAVE_ACL
-static bxattr_rtn_code solaris_restore_xattr_acl(JCR *jcr,
-                                                  int fd,
-                                                  const char *attrname,
-                                                  char *acl_text)
-{
-#ifdef HAVE_EXTENDED_ACL
-   int error;
-   acl_t *aclp = NULL;
-
-   if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
-      Mmsg1(jcr->errmsg,
-            _("Unable to convert acl from text on file \"%s\"\n"),
-            jcr->last_fname);
-      return bxattr_rtn_error;
-   }
-
-   if ((fd != -1 && facl_set(fd, aclp) != 0) ||
-        acl_set(attrname, aclp) != 0) {
-      berrno be;
-
-      Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
-            attrname, jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      return bxattr_rtn_error;
-   }
-
-   if (aclp) {
-      acl_free(aclp);
-   }
-   return bxattr_rtn_ok;
-
-#else /* HAVE_EXTENDED_ACL */
-   int n;
-   aclent_t *acls = NULL;
-
-   acls = aclfromtext(acl_text, &n);
-   if (!acls) {
-      if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) ||
-           acl(attrname, SETACL, n, acls) != 0) {
-         berrno be;
-
-         Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
-               attrname, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         return bxattr_rtn_error;
-      }
-   }
-
-   if (acls) {
-      free(acls);
-   }
-   return bxattr_rtn_ok;
-
-#endif /* HAVE_EXTENDED_ACL */
-
-}
-#endif /* HAVE_ACL */
-
-static bxattr_rtn_code solaris_restore_xattrs(JCR *jcr,
-                                               bool is_extensible,
-                                               char *content,
-                                               uint32_t content_length)
-
-{
-   int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
-   int used_bytes, cnt;
-   char *bp, *target_attrname, *attribs;
-   char *linked_target = NULL;
-   char *acl_text = NULL;
-   char *data = NULL;
-   int32_t inum;
-   struct stat st;
-   struct timeval times[2];
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   /*
-    * Restore the xattr stream. First the part that is the same for all xattrs.
-    */
-   used_bytes = 0;
-
-   /*
-    * The name of the target xattr has a leading / we are not interested
-    * in that so skip it when decoding the string. We always start a the /
-    * of the xattr space anyway.
-    */
-   target_attrname = content + 1;
-   if ((bp = strchr(target_attrname, '\0')) == (char *)NULL ||
-       (used_bytes = (bp - content)) >= (int32_t)(content_length - 1)) {
-      goto restore_error;
-   }
-   attribs = ++bp;
-
-   /*
-    * Open the file on which to restore the xattrs read-only.
-    */
-   if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
-      berrno be;
-
-      Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      goto get_out;
-   }
-
-   /*
-    * Open the xattr naming space and make it the current working dir.
-    */
-   if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
-      berrno be;
-
-      Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      goto get_out;
-   }
-
-   if (fchdir(attrdirfd) < 0) {
-      berrno be;
-
-      Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
-            jcr->last_fname, be.bstrerror());
-      Dmsg1(100, "%s", jcr->errmsg);
-      goto get_out;
-   }
-
-   /*
-    * Try to open the correct xattr subdir based on the target_attrname given.
-    * e.g. check if its a subdir attrname. Each / in the string makes us go
-    * one level deeper.
-    */
-   while ((bp = strchr(target_attrname, '/')) != (char *)NULL) {
-      *bp = '\0';
-
-      if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
-         berrno be;
-
-         Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
-               target_attrname, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      close(filefd);
-      filefd = fd;
-
-      /*
-       * Open the xattr naming space.
-       */
-      if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
-         berrno be;
-
-         Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
-               target_attrname, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      close(attrdirfd);
-      attrdirfd = fd;
-
-      /*
-       * Make the xattr space our current workingdir.
-       */
-      if (fchdir(attrdirfd) < 0) {
-         berrno be;
-
-         Mmsg3(jcr->errmsg, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
-               target_attrname, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      target_attrname = ++bp;
-   }
-
-   /*
-    * Decode the attributes from the stream.
-    */
-   decode_stat(attribs, &st, sizeof(st), &inum);
-
-   /*
-    * Decode the next field (acl_text).
-    */
-   if ((bp = strchr(attribs, '\0')) == (char *)NULL ||
-       (used_bytes = (bp - content)) >= (int32_t)(content_length - 1)) {
-      goto restore_error;
-   }
-   acl_text = ++bp;
-
-   /*
-    * Based on the filetype perform the correct action. We support most filetypes here, more
-    * then the actual implementation on Solaris supports so some code may never get executed
-    * due to limitations in the implementation.
-    */
-   switch (st.st_mode & S_IFMT) {
-   case S_IFIFO:
-      /*
-       * The current implementation of xattr on Solaris doesn't support this,
-       * but if it ever does we are prepared.
-       */
-      unlinkat(attrdirfd, target_attrname, 0);
-      if (mkfifo(target_attrname, st.st_mode) < 0) {
-         berrno be;
-
-         Mmsg3(jcr->errmsg, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
-               target_attrname, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-      break;
-   case S_IFCHR:
-   case S_IFBLK:
-      /*
-       * The current implementation of xattr on Solaris doesn't support this,
-       * but if it ever does we are prepared.
-       */
-      unlinkat(attrdirfd, target_attrname, 0);
-      if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
-         berrno be;
-
-         Mmsg3(jcr->errmsg, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
-               target_attrname, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-      break;
-   case S_IFDIR:
-      /*
-       * If its not the hidden_dir create the entry.
-       * The current implementation of xattr on Solaris doesn't support this,
-       * but if it ever does we are prepared.
-       */
-      if (!bstrcmp(target_attrname, ".")) {
-         unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
-         if (mkdir(target_attrname, st.st_mode) < 0) {
-            berrno be;
-
-            /* *** FIXME *** why not Mmsg? */
-            Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
-               target_attrname, jcr->last_fname, be.bstrerror());
-            Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
-               target_attrname,  jcr->last_fname, be.bstrerror());
-            goto get_out;
-         }
-      }
-      break;
-   case S_IFREG:
-      /*
-       * See if this is a hard linked file. e.g. inum != 0
-       */
-      if (inum != 0) {
-         linked_target = bp;
-
-         unlinkat(attrdirfd, target_attrname, 0);
-         if (link(linked_target, target_attrname) < 0) {
-            berrno be;
-
-            Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
-                  target_attrname, linked_target, jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-
-         /*
-          * Successfully restored xattr.
-          */
-         retval = bxattr_rtn_ok;
-         goto get_out;
-      } else {
-         if ((bp = strchr(acl_text, '\0')) == (char *)NULL ||
-             (used_bytes = (bp - content)) >= (int32_t)content_length) {
-            goto restore_error;
-         }
-
-         if (used_bytes < (int32_t)(content_length - 1))
-            data = ++bp;
-
-         /*
-          * Restore the actual xattr.
-          */
-         if (!is_extensible) {
-            unlinkat(attrdirfd, target_attrname, 0);
-         }
-
-         if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) {
-            berrno be;
-
-            Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
-                  target_attrname, jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-      }
-
-      /*
-       * Restore the actual data.
-       */
-      if (st.st_size > 0) {
-         used_bytes = (data - content);
-         cnt = content_length - used_bytes;
-
-         /*
-          * Do a sanity check, the st.st_size should be the same as the number of bytes
-          * we have available as data of the stream.
-          */
-         if (cnt != st.st_size) {
-            Mmsg2(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"),
-                  target_attrname, jcr->last_fname);
-            Dmsg1(100, "%s", jcr->errmsg);
-            goto get_out;
-         }
-
-         while (cnt > 0) {
-            cnt = write(attrfd, data, cnt);
-            if (cnt < 0) {
-               berrno be;
-
-               Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"),
-                     target_attrname, jcr->last_fname, be.bstrerror());
-               Dmsg1(100, "%s", jcr->errmsg);
-               goto get_out;
-            }
-
-            used_bytes += cnt;
-            data += cnt;
-            cnt = content_length - used_bytes;
-         }
-      }
-      break;
-   case S_IFLNK:
-      /*
-       * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
-       */
-      linked_target = bp;
-
-      if (symlink(linked_target, target_attrname) < 0) {
-         berrno be;
-
-         Mmsg4(jcr->errmsg, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
-               target_attrname, linked_target, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      /*
-       * Successfully restored xattr.
-       */
-      retval = bxattr_rtn_ok;
-      goto get_out;
-   default:
-      goto get_out;
-   }
-
-   /*
-    * Restore owner and acl for non extensible attributes.
-    */
-   if (!is_extensible) {
-      if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) {
-         berrno be;
-         /* EINVAL means is not supported, no fail */
-         if (errno == EINVAL || errno == ENOENT) {
-            retval = bxattr_rtn_ok;
-         } else {
-            Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
-                  target_attrname, jcr->last_fname, be.bstrerror());
-            Dmsg1(100, "%s", jcr->errmsg);
-         }
-         goto get_out;
-      }
-   }
-
-#ifdef HAVE_ACL
-   if (acl_text && *acl_text)
-      if (solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text) != bxattr_rtn_ok)
-         goto get_out;
-#endif /* HAVE_ACL */
-
-   /*
-    * For a non extensible attribute restore access and modification time on the xattr.
-    */
-   if (!is_extensible) {
-      times[0].tv_sec = st.st_atime;
-      times[0].tv_usec = 0;
-      times[1].tv_sec = st.st_mtime;
-      times[1].tv_usec = 0;
-
-      if (futimesat(attrdirfd, target_attrname, times) < 0) {
-         berrno be;
-         Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
-               target_attrname, jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-   }
-
-   /*
-    * Successfully restored xattr.
-    */
-   retval = bxattr_rtn_ok;
-   goto get_out;
-
-restore_error:
-   Mmsg1(jcr->errmsg, _("Invalid xattr stream, failed to restore xattr stream on file \"%s\"\n"),
-         jcr->last_fname);
-   Dmsg1(100, "%s", jcr->errmsg);
-
-get_out:
-   if (attrfd != -1) {
-      close(attrfd);
-   }
-   if (attrdirfd != -1) {
-      close(attrdirfd);
-   }
-   if (filefd != -1) {
-      close(filefd);
-   }
-   return retval;
-}
-
-static bxattr_rtn_code solaris_backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   char cwd[PATH_MAX];
-   bxattr_rtn_code retval = bxattr_rtn_ok;
-
-   /*
-    * First see if extended attributes or extensible attributes are present.
-    * If not just pretend things went ok.
-    */
-   if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) {
-      jcr->xattr_ctx->nr_saved = 0;
-
-      /*
-       * As we change the cwd in the save function save the current cwd
-       * for restore after return from the solaris_save_xattrs function.
-       */
-      getcwd(cwd, sizeof(cwd));
-      retval = solaris_save_xattrs(jcr, NULL, NULL);
-      chdir(cwd);
-      if (jcr->xattr_ctx->link_cache) {
-         drop_xattr_link_cache(jcr);
-      }
-   }
-   return retval;
-}
-
-static bxattr_rtn_code solaris_restore_xattr_streams(JCR *jcr,
-                                                    int stream,
-                                                    char *content,
-                                                    uint32_t content_length)
-{
-   char cwd[PATH_MAX];
-   bool is_extensible = false;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   /*
-    * First make sure we can restore xattr on the filesystem.
-    */
-   switch (stream) {
-#if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
-   case STREAM_XATTR_SOLARIS_SYS:
-      if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) {
-         Mmsg1(jcr->errmsg, _("Failed to restore extensible attributes on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-
-      is_extensible = true;
-      break;
-#endif
-   case STREAM_XATTR_SOLARIS:
-      if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) {
-         Mmsg1(jcr->errmsg, _("Failed to restore extended attributes on file \"%s\"\n"),
-               jcr->last_fname);
-         Dmsg1(100, "%s", jcr->errmsg);
-         goto get_out;
-      }
-      break;
-   default:
-      goto get_out;
-   }
-
-   /*
-    * As we change the cwd in the restore function save the current cwd
-    * for restore after return from the solaris_restore_xattrs function.
-    */
-   getcwd(cwd, sizeof(cwd));
-   retval = solaris_restore_xattrs(jcr, is_extensible, content, content_length);
-   chdir(cwd);
-
-get_out:
-   return retval;
-}
-
-
-/*
- * Function pointers to the build and restore function to use for these xattrs.
- */
-static bxattr_rtn_code (*os_backup_xattr_streams)
-                        (JCR *jcr, FF_PKT *ff_pkt) =
-                        solaris_backup_xattr_streams;
-static bxattr_rtn_code (*os_restore_xattr_streams)
-                        (JCR *jcr, int stream, char *content, uint32_t content_length) =
-                        solaris_restore_xattr_streams;
-
-#endif /* defined(HAVE_SUN_OS) */
-
-/*
- * Entry points when compiled with support for XATTRs on a supported platform.
- */
-bool backup_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
-{
-   bxattr_rtn_code rtn = bxattr_rtn_ok;
-
-   if (!(ff_pkt->flags & FO_XATTR && !ff_pkt->cmd_plugin)) {
-      return true;
-   }
-
-   jcr->errmsg[0] = 0;
-
-   /*
-    * See if we are changing from one device to an other.
-    * We save the current device we are scanning and compare
-    * it with the current st_dev in the last stat performed on
-    * the file we are currently storing.
-    */
-   if (jcr->xattr_ctx->current_dev != ff_pkt->statp.st_dev) {
-      /*
-       * Reset the acl save flags.
-       */
-      jcr->xattr_ctx->flags = 0;
-      jcr->xattr_ctx->flags |= BXATTR_FLAG_SAVE_NATIVE;
-
-      /*
-       * Save that we started scanning a new filesystem.
-       */
-      jcr->xattr_ctx->current_dev = ff_pkt->statp.st_dev;
-   }
-
-   if ((jcr->xattr_ctx->flags & BXATTR_FLAG_SAVE_NATIVE) && os_restore_xattr_streams) {
-      rtn = os_backup_xattr_streams(jcr, ff_pkt);
-   }
-   switch (rtn) {
-   case bxattr_rtn_fatal:
-      return false;
-   case bxattr_rtn_ok:
-      return true;
-   case bxattr_rtn_error:
-      if (jcr->xattr_ctx->nr_errors < XATTR_MAX_ERROR_PRINT_PER_JOB) {
-         if (jcr->errmsg[0]) {
-            Jmsg(jcr, M_WARNING, 0, "Operating system XATTRs not configured.\n");
-         } else {
-            Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
-         }
-         jcr->xattr_ctx->nr_errors++;
-      }
-      return true;
-   }
-   /* Theoretically we cannot get here */
-   return false;
-
-}
-
-bxattr_rtn_code restore_xattr_streams(JCR *jcr,
-                                     int stream,
-                                     char *content,
-                                     uint32_t content_length)
-{
-   int ret;
-   struct stat st;
-   unsigned int cnt;
-   bxattr_rtn_code retval = bxattr_rtn_error;
-
-   /*
-    * See if we are changing from one device to an other.
-    * We save the current device we are restoring to and compare
-    * it with the current st_dev in the last stat performed on
-    * the file we are currently restoring.
-    */
-   ret = lstat(jcr->last_fname, &st);
-   if (ret < 0) {
-      berrno be;
-      if (errno == ENOENT) {
-         retval = bxattr_rtn_ok;
-      } else {
-         Mmsg2(jcr->errmsg, _("Unable to stat file \"%s\": ERR=%s\n"),
-               jcr->last_fname, be.bstrerror());
-         Dmsg1(100, "%s", jcr->errmsg);
-      }
-      goto get_out;
-   }
-   if (jcr->xattr_ctx->current_dev != st.st_dev) {
-      /*
-       * Reset the acl save flags.
-       */
-      jcr->xattr_ctx->flags = 0;
-      jcr->xattr_ctx->flags |= BXATTR_FLAG_RESTORE_NATIVE;
-
-      /*
-       * Save that we started restoring to a new filesystem.
-       */
-      jcr->xattr_ctx->current_dev = st.st_dev;
-   }
-
-   /*
-    * See if we are still restoring native xattr to this filesystem.
-    */
-   if ((jcr->xattr_ctx->flags & BXATTR_FLAG_RESTORE_NATIVE) && os_restore_xattr_streams) {
-      /*
-       * See if we can restore this stream, and ifso give it a try.
-       */
-      for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) {
-         if (os_default_xattr_streams[cnt] == stream) {
-            retval = os_restore_xattr_streams(jcr, stream, content, content_length);
-            goto get_out;
-         }
-      }
-   } else {
-      /*
-       * Increment error count but don't log an error again for the same filesystem.
-       */
-      jcr->xattr_ctx->nr_errors++;
-      retval = bxattr_rtn_ok;
-      goto get_out;
-   }
-
-   /*
-    * Issue a warning and discard the message. But pretend the restore was ok.
-    */
-   Jmsg2(jcr, M_WARNING, 0,
-         _("Cannot restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
-         jcr->last_fname, stream);
-
-get_out:
-   return retval;
-}
-#endif
diff --git a/bacula/src/filed/xattr.h b/bacula/src/filed/xattr.h
deleted file mode 100644 (file)
index 62742a8..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-   Bacula(R) - The Network Backup Solution
-
-   Copyright (C) 2000-2015 Kern Sibbald
-   Copyright (C) 2004-2014 Free Software Foundation Europe e.V.
-
-   The original author of Bacula is Kern Sibbald, with contributions
-   from many others, a complete list can be found in the file AUTHORS.
-
-   You may use this file and others of this release according to the
-   license defined in the LICENSE file, which includes the Affero General
-   Public License, v3.0 ("AGPLv3") and some additional permissions and
-   terms pursuant to its AGPLv3 Section 7.
-
-   This notice must be preserved when any source code is 
-   conveyed and/or propagated.
-
-   Bacula(R) is a registered trademark of Kern Sibbald.
-*/
-
-#ifndef BACULA_XATTR_H_
-#define BACULA_XATTR_H_
-
-#if defined(HAVE_LINUX_OS)
-#define BXATTR_ENOTSUP EOPNOTSUPP
-#elif defined(HAVE_DARWIN_OS)
-#define BXATTR_ENOTSUP ENOTSUP
-#elif defined(HAVE_HURD_OS)
-#define BXATTR_ENOTSUP ENOTSUP
-#endif
-
-/*
- * Magic used in the magic field of the xattr struct.
- * This way we can see we encounter a valid xattr struct.
- */
-#define XATTR_MAGIC 0x5C5884
-
-/*
- * Internal representation of an extended attribute.
- */
-struct xattr_t {
-   uint32_t magic;
-   uint32_t name_length;
-   char *name;
-   uint32_t value_length;
-   char *value;
-};
-
-/*
- * Internal representation of an extended attribute hardlinked file.
- */
-struct xattr_link_cache_entry_t {
-   uint32_t inum;
-   char *target;
-};
-
-#define BXATTR_FLAG_SAVE_NATIVE    0x01
-#define BXATTR_FLAG_RESTORE_NATIVE 0x02
-
-/*
- * Internal tracking data.
- */
-struct xattr_ctx_t {
-   uint32_t flags;              /* See BXATTR_FLAG_* */
-   uint32_t current_dev;
-   uint32_t nr_errors;
-   uint32_t nr_saved;
-   POOLMEM *content;
-   uint32_t content_length;
-   alist *link_cache;
-};
-
-#define MAX_XATTR_LENGTH  (1 * 1024 * 1024) /* 1 Mb */
-
-#define XATTR_BUFSIZ    1024
-
-#endif /* BACULA_XATTR_H_ */
index f0de4fac67aa4e9c0a32e3691d0998fbeeccc315..d86e66620669fe1b97fefa2c24c35dc4ebe95886 100644 (file)
@@ -150,65 +150,65 @@ const char *stream_to_ascii(int stream)
          return _("Plugin Data");
       case STREAM_RESTORE_OBJECT:
          return _("Restore Object");
-      case STREAM_ACL_AIX_TEXT:
+      case STREAM_XACL_AIX_TEXT:
          return _("AIX ACL attribs");
-      case STREAM_ACL_DARWIN_ACCESS:
+      case STREAM_XACL_DARWIN_ACCESS:
          return _("Darwin ACL attribs");
-      case STREAM_ACL_FREEBSD_DEFAULT:
+      case STREAM_XACL_FREEBSD_DEFAULT:
          return _("FreeBSD Default ACL attribs");
-      case STREAM_ACL_FREEBSD_ACCESS:
+      case STREAM_XACL_FREEBSD_ACCESS:
          return _("FreeBSD Access ACL attribs");
-      case STREAM_ACL_HPUX_ACL_ENTRY:
+      case STREAM_XACL_HPUX_ACL_ENTRY:
          return _("HPUX ACL attribs");
-      case STREAM_ACL_IRIX_DEFAULT:
+      case STREAM_XACL_IRIX_DEFAULT:
          return _("Irix Default ACL attribs");
-      case STREAM_ACL_IRIX_ACCESS:
+      case STREAM_XACL_IRIX_ACCESS:
          return _("Irix Access ACL attribs");
-      case STREAM_ACL_LINUX_DEFAULT:
+      case STREAM_XACL_LINUX_DEFAULT:
          return _("Linux Default ACL attribs");
-      case STREAM_ACL_LINUX_ACCESS:
+      case STREAM_XACL_LINUX_ACCESS:
          return _("Linux Access ACL attribs");
-      case STREAM_ACL_TRU64_DEFAULT:
+      case STREAM_XACL_TRU64_DEFAULT:
          return _("TRU64 Default ACL attribs");
-      case STREAM_ACL_TRU64_ACCESS:
+      case STREAM_XACL_TRU64_ACCESS:
          return _("TRU64 Access ACL attribs");
-      case STREAM_ACL_SOLARIS_POSIX:
+      case STREAM_XACL_SOLARIS_POSIX:
          return _("Solaris POSIX ACL attribs");
-      case STREAM_ACL_SOLARIS_NFS4:
+      case STREAM_XACL_SOLARIS_NFS4:
          return _("Solaris NFSv4/ZFS ACL attribs");
-      case STREAM_ACL_AFS_TEXT:
+      case STREAM_XACL_AFS_TEXT:
          return _("AFS ACL attribs");
-      case STREAM_ACL_AIX_AIXC:
+      case STREAM_XACL_AIX_AIXC:
          return _("AIX POSIX ACL attribs");
-      case STREAM_ACL_AIX_NFS4:
+      case STREAM_XACL_AIX_NFS4:
          return _("AIX NFSv4 ACL attribs");
-      case STREAM_ACL_FREEBSD_NFS4:
+      case STREAM_XACL_FREEBSD_NFS4:
          return _("FreeBSD NFSv4/ZFS ACL attribs");
-      case STREAM_ACL_HURD_DEFAULT:
+      case STREAM_XACL_HURD_DEFAULT:
          return _("GNU Hurd Default ACL attribs");
-      case STREAM_ACL_HURD_ACCESS:
+      case STREAM_XACL_HURD_ACCESS:
          return _("GNU Hurd Access ACL attribs");
-      case STREAM_XATTR_HURD:
+      case STREAM_XACL_HURD_XATTR:
          return _("GNU Hurd Extended attribs");
-      case STREAM_XATTR_IRIX:
+      case STREAM_XACL_IRIX_XATTR:
          return _("IRIX Extended attribs");
-      case STREAM_XATTR_TRU64:
+      case STREAM_XACL_TRU64_XATTR:
          return _("TRU64 Extended attribs");
-      case STREAM_XATTR_AIX:
+      case STREAM_XACL_AIX_XATTR:
          return _("AIX Extended attribs");
-      case STREAM_XATTR_OPENBSD:
+      case STREAM_XACL_OPENBSD_XATTR:
          return _("OpenBSD Extended attribs");
-      case STREAM_XATTR_SOLARIS_SYS:
+      case STREAM_XACL_SOLARIS_SYS_XATTR:
          return _("Solaris Extensible attribs or System Extended attribs");
-      case STREAM_XATTR_SOLARIS:
+      case STREAM_XACL_SOLARIS_XATTR:
          return _("Solaris Extended attribs");
-      case STREAM_XATTR_DARWIN:
+      case STREAM_XACL_DARWIN_XATTR:
          return _("Darwin Extended attribs");
-      case STREAM_XATTR_FREEBSD:
+      case STREAM_XACL_FREEBSD_XATTR:
          return _("FreeBSD Extended attribs");
-      case STREAM_XATTR_LINUX:
+      case STREAM_XACL_LINUX_XATTR:
          return _("Linux Extended attribs");
-      case STREAM_XATTR_NETBSD:
+      case STREAM_XACL_NETBSD_XATTR:
          return _("NetBSD Extended attribs");
       default:
          sprintf(buf, "%d", stream);
index d37a31f0fe6b523bf4d756bdb9867797bf3d2205..c2b85f7ea5118330dda01d28357297ead43c5ad8 100644 (file)
@@ -1,8 +1,7 @@
 /*
    Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2000-2015 Kern Sibbald
-   Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2016 Kern Sibbald
 
    The original author of Bacula is Kern Sibbald, with contributions
    from many others, a complete list can be found in the file AUTHORS.
@@ -12,7 +11,7 @@
    Public License, v3.0 ("AGPLv3") and some additional permissions and
    terms pursuant to its AGPLv3 Section 7.
 
-   This notice must be preserved when any source code is 
+   This notice must be preserved when any source code is
    conveyed and/or propagated.
 
    Bacula(R) is a registered trademark of Kern Sibbald.
@@ -147,8 +146,7 @@ struct bpContext;
 
 #ifdef FILE_DAEMON
 class htable;
-struct acl_ctx_t;
-struct xattr_ctx_t;
+class XACL;
 class snapshot_manager;
 
 struct CRYPTO_CTX {
@@ -414,8 +412,7 @@ public:
    POOLMEM *last_fname;               /* last file saved/verified */
    POOLMEM *job_metadata;             /* VSS job metadata */
    pthread_cond_t job_start_wait;     /* Wait for SD to start Job */
-   acl_ctx_t *acl_ctx;                /* ACLs for backup/restore */
-   xattr_ctx_t *xattr_ctx;            /* Extended Attributes for backup/restore */
+   XACL *xacl;                        /* ACLs and Extended Attributes for backup/restore */
    int32_t last_type;                 /* type of last file saved/verified */
    int incremental;                   /* set if incremental for SINCE */
    time_t last_stat_time;             /* Last time stats sent to Dir */
index ca3653a21b4f53184d7a103d47371e15f58ae65a..e9c6678107c5bd0d37eaddaa1130c5a96900c1f1 100644 (file)
@@ -835,40 +835,40 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec)
    case  STREAM_UNIX_ACCESS_ACL:          /* Deprecated Standard ACL attributes on UNIX */
    case  STREAM_UNIX_DEFAULT_ACL:         /* Deprecated Default ACL attributes on UNIX */
    case  STREAM_HFSPLUS_ATTRIBUTES:
-   case  STREAM_ACL_AIX_TEXT:
-   case  STREAM_ACL_DARWIN_ACCESS:
-   case  STREAM_ACL_FREEBSD_DEFAULT:
-   case  STREAM_ACL_FREEBSD_ACCESS:
-   case  STREAM_ACL_HPUX_ACL_ENTRY:
-   case  STREAM_ACL_IRIX_DEFAULT:
-   case  STREAM_ACL_IRIX_ACCESS:
-   case  STREAM_ACL_LINUX_DEFAULT:
-   case  STREAM_ACL_LINUX_ACCESS:
-   case  STREAM_ACL_TRU64_DEFAULT:
-   case  STREAM_ACL_TRU64_DEFAULT_DIR:
-   case  STREAM_ACL_TRU64_ACCESS:
-   case  STREAM_ACL_SOLARIS_POSIX:
-   case  STREAM_ACL_SOLARIS_NFS4:
-   case  STREAM_ACL_AFS_TEXT:
-   case  STREAM_ACL_AIX_AIXC:
-   case  STREAM_ACL_AIX_NFS4:
-   case  STREAM_ACL_FREEBSD_NFS4:
-   case  STREAM_ACL_HURD_DEFAULT:
-   case  STREAM_ACL_HURD_ACCESS:
+   case  STREAM_XACL_AIX_TEXT:
+   case  STREAM_XACL_DARWIN_ACCESS:
+   case  STREAM_XACL_FREEBSD_DEFAULT:
+   case  STREAM_XACL_FREEBSD_ACCESS:
+   case  STREAM_XACL_HPUX_ACL_ENTRY:
+   case  STREAM_XACL_IRIX_DEFAULT:
+   case  STREAM_XACL_IRIX_ACCESS:
+   case  STREAM_XACL_LINUX_DEFAULT:
+   case  STREAM_XACL_LINUX_ACCESS:
+   case  STREAM_XACL_TRU64_DEFAULT:
+   case  STREAM_XACL_TRU64_DEFAULT_DIR:
+   case  STREAM_XACL_TRU64_ACCESS:
+   case  STREAM_XACL_SOLARIS_POSIX:
+   case  STREAM_XACL_SOLARIS_NFS4:
+   case  STREAM_XACL_AFS_TEXT:
+   case  STREAM_XACL_AIX_AIXC:
+   case  STREAM_XACL_AIX_NFS4:
+   case  STREAM_XACL_FREEBSD_NFS4:
+   case  STREAM_XACL_HURD_DEFAULT:
+   case  STREAM_XACL_HURD_ACCESS:
       /* Ignore Unix ACL attributes */
       break;
 
-   case  STREAM_XATTR_HURD:
-   case  STREAM_XATTR_IRIX:
-   case  STREAM_XATTR_TRU64:
-   case  STREAM_XATTR_AIX:
-   case  STREAM_XATTR_OPENBSD:
-   case  STREAM_XATTR_SOLARIS_SYS:
-   case  STREAM_XATTR_SOLARIS:
-   case  STREAM_XATTR_DARWIN:
-   case  STREAM_XATTR_FREEBSD:
-   case  STREAM_XATTR_LINUX:
-   case  STREAM_XATTR_NETBSD:
+   case  STREAM_XACL_HURD_XATTR:
+   case  STREAM_XACL_IRIX_XATTR:
+   case  STREAM_XACL_TRU64_XATTR:
+   case  STREAM_XACL_AIX_XATTR:
+   case  STREAM_XACL_OPENBSD_XATTR:
+   case  STREAM_XACL_SOLARIS_SYS_XATTR:
+   case  STREAM_XACL_SOLARIS_XATTR:
+   case  STREAM_XACL_DARWIN_XATTR:
+   case  STREAM_XACL_FREEBSD_XATTR:
+   case  STREAM_XACL_LINUX_XATTR:
+   case  STREAM_XACL_NETBSD_XATTR:
       /* Ignore Unix Extended attributes */
       break;
 
index d1db5b41205d39be1e17197b88a2a4dbbf7fa67a..e1f7dc72364f2c5e22a2733cf4ee545da422ef29 100644 (file)
  * from the stream dispatch function. Currently in this reserved space we allocate the
  * different acl streams from 1000 on and the different extended attributes streams from
  * 1999 down. So the two naming spaces grows towards each other.
+ *
+ * Rationalize names a bit to correspond to the new XACL classes
+ *
  */
-#define STREAM_ACL_AIX_TEXT          1000    /* AIX string of acl_get */
-#define STREAM_ACL_DARWIN_ACCESS     1001    /* Darwin (OSX) acl_t string of acl_to_text (POSIX acl) */
-#define STREAM_ACL_FREEBSD_DEFAULT   1002    /* FreeBSD acl_t string of acl_to_text (POSIX acl) for default acls */
-#define STREAM_ACL_FREEBSD_ACCESS    1003    /* FreeBSD acl_t string of acl_to_text (POSIX acl) for access acls */
-#define STREAM_ACL_HPUX_ACL_ENTRY    1004    /* HPUX acl_entry string of acltostr (POSIX acl) */
-#define STREAM_ACL_IRIX_DEFAULT      1005    /* IRIX acl_t string of acl_to_text (POSIX acl) for default acls */
-#define STREAM_ACL_IRIX_ACCESS       1006    /* IRIX acl_t string of acl_to_text (POSIX acl) for access acls */
-#define STREAM_ACL_LINUX_DEFAULT     1007    /* Linux acl_t string of acl_to_text (POSIX acl) for default acls */
-#define STREAM_ACL_LINUX_ACCESS      1008    /* Linux acl_t string of acl_to_text (POSIX acl) for access acls */
-#define STREAM_ACL_TRU64_DEFAULT     1009    /* Tru64 acl_t string of acl_to_text (POSIX acl) for default acls */
-#define STREAM_ACL_TRU64_DEFAULT_DIR 1010    /* Tru64 acl_t string of acl_to_text (POSIX acl) for default acls */
-#define STREAM_ACL_TRU64_ACCESS      1011    /* Tru64 acl_t string of acl_to_text (POSIX acl) for access acls */
-#define STREAM_ACL_SOLARIS_POSIX     1012    /* Solaris aclent_t string of acltotext or acl_totext (POSIX acl) */
-#define STREAM_ACL_SOLARIS_NFS4      1013    /* Solaris ace_t string of of acl_totext (NFSv4 or ZFS acl) */
-#define STREAM_ACL_AFS_TEXT          1014    /* AFS string of pioctl */
-#define STREAM_ACL_AIX_AIXC          1015    /* AIX string of aclx_printStr (POSIX acl) */
-#define STREAM_ACL_AIX_NFS4          1016    /* AIX string of aclx_printStr (NFSv4 acl) */
-#define STREAM_ACL_FREEBSD_NFS4      1017    /* FreeBSD acl_t string of acl_to_text (NFSv4 or ZFS acl) */
-#define STREAM_ACL_HURD_DEFAULT      1018    /* GNU HURD acl_t string of acl_to_text (POSIX acl) for default acls */
-#define STREAM_ACL_HURD_ACCESS       1019    /* GNU HURD acl_t string of acl_to_text (POSIX acl) for access acls */
-#define STREAM_XATTR_HURD            1989    /* GNU HURD extended attributes */
-#define STREAM_XATTR_IRIX            1990    /* IRIX extended attributes */
-#define STREAM_XATTR_TRU64           1991    /* TRU64 extended attributes */
-#define STREAM_XATTR_AIX             1992    /* AIX extended attributes */
-#define STREAM_XATTR_OPENBSD         1993    /* OpenBSD extended attributes */
-#define STREAM_XATTR_SOLARIS_SYS     1994    /* Solaris extensible attributes or
-                                              * otherwise named extended system attributes.
-                                              */
-#define STREAM_XATTR_SOLARIS         1995    /* Solaris extented attributes */
-#define STREAM_XATTR_DARWIN          1996    /* Darwin (OSX) extended attributes */
-#define STREAM_XATTR_FREEBSD         1997    /* FreeBSD extended attributes */
-#define STREAM_XATTR_LINUX           1998    /* Linux specific attributes */
-#define STREAM_XATTR_NETBSD          1999    /* NetBSD extended attributes */
+#define STREAM_XACL_AIX_TEXT          1000    /* AIX string of acl_get */
+#define STREAM_XACL_DARWIN_ACCESS     1001    /* Darwin (OSX) acl_t string of acl_to_text (POSIX acl) */
+#define STREAM_XACL_FREEBSD_DEFAULT   1002    /* FreeBSD acl_t string of acl_to_text (POSIX acl) for default acls */
+#define STREAM_XACL_FREEBSD_ACCESS    1003    /* FreeBSD acl_t string of acl_to_text (POSIX acl) for access acls */
+#define STREAM_XACL_HPUX_ACL_ENTRY    1004    /* HPUX acl_entry string of acltostr (POSIX acl) */
+#define STREAM_XACL_IRIX_DEFAULT      1005    /* IRIX acl_t string of acl_to_text (POSIX acl) for default acls */
+#define STREAM_XACL_IRIX_ACCESS       1006    /* IRIX acl_t string of acl_to_text (POSIX acl) for access acls */
+#define STREAM_XACL_LINUX_DEFAULT     1007    /* Linux acl_t string of acl_to_text (POSIX acl) for default acls */
+#define STREAM_XACL_LINUX_ACCESS      1008    /* Linux acl_t string of acl_to_text (POSIX acl) for access acls */
+#define STREAM_XACL_TRU64_DEFAULT     1009    /* Tru64 acl_t string of acl_to_text (POSIX acl) for default acls */
+#define STREAM_XACL_TRU64_DEFAULT_DIR 1010    /* Tru64 acl_t string of acl_to_text (POSIX acl) for default acls */
+#define STREAM_XACL_TRU64_ACCESS      1011    /* Tru64 acl_t string of acl_to_text (POSIX acl) for access acls */
+#define STREAM_XACL_SOLARIS_POSIX     1012    /* Solaris aclent_t string of acltotext or acl_totext (POSIX acl) */
+#define STREAM_XACL_SOLARIS_NFS4      1013    /* Solaris ace_t string of of acl_totext (NFSv4 or ZFS acl) */
+#define STREAM_XACL_AFS_TEXT          1014    /* AFS string of pioctl */
+#define STREAM_XACL_AIX_AIXC          1015    /* AIX string of aclx_printStr (POSIX acl) */
+#define STREAM_XACL_AIX_NFS4          1016    /* AIX string of aclx_printStr (NFSv4 acl) */
+#define STREAM_XACL_FREEBSD_NFS4      1017    /* FreeBSD acl_t string of acl_to_text (NFSv4 or ZFS acl) */
+#define STREAM_XACL_HURD_DEFAULT      1018    /* GNU HURD acl_t string of acl_to_text (POSIX acl) for default acls */
+#define STREAM_XACL_HURD_ACCESS       1019    /* GNU HURD acl_t string of acl_to_text (POSIX acl) for access acls */
+#define STREAM_XACL_HURD_XATTR        1989    /* GNU HURD extended attributes */
+#define STREAM_XACL_IRIX_XATTR        1990    /* IRIX extended attributes */
+#define STREAM_XACL_TRU64_XATTR       1991    /* TRU64 extended attributes */
+#define STREAM_XACL_AIX_XATTR         1992    /* AIX extended attributes */
+#define STREAM_XACL_OPENBSD_XATTR     1993    /* OpenBSD extended attributes */
+#define STREAM_XACL_SOLARIS_SYS_XATTR 1994    /* Solaris extensible attributes or
+                                               * otherwise named extended system attributes.
+                                               */
+#define STREAM_XACL_SOLARIS_XATTR     1995    /* Solaris extented attributes */
+#define STREAM_XACL_DARWIN_XATTR      1996    /* Darwin (OSX) extended attributes */
+#define STREAM_XACL_FREEBSD_XATTR     1997    /* FreeBSD extended attributes */
+#define STREAM_XACL_LINUX_XATTR       1998    /* Linux specific attributes */
+#define STREAM_XACL_NETBSD_XATTR      1999    /* NetBSD extended attributes */
 
 /* WARNING!!! do not define more than 2047 of these old types */