]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/backup.c
Fix FD crash when plugin running and cancel given
[bacula/bacula] / bacula / src / filed / backup.c
index 7db7d620b24c5449ef6239458fad6108be3b1175..d9753d00c8576cb13fb768e88104340b0c2444e4 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
@@ -20,7 +20,7 @@
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   Bacula® is a registered trademark of John Walker.
+   Bacula® is a registered trademark of Kern Sibbald.
    The licensor of Bacula is the Free Software Foundation Europe
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 
 #include "bacula.h"
 #include "filed.h"
-#include "lib/htable.h"
+
+#ifdef HAVE_DARWIN_OS
+const bool have_darwin_os = true;
+#else
+const bool have_darwin_os = false;
+#endif
+
+#if defined(HAVE_ACL)
+const bool have_acl = true;
+#else
+const bool have_acl = false;
+#endif
+
+#if defined(HAVE_XATTR)
+const bool have_xattr = true;
+#else
+const bool have_xattr = false;
+#endif
 
 /* Forward referenced functions */
 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
-static void strip_path(FF_PKT *ff_pkt);
-static void unstrip_path(FF_PKT *ff_pkt);
 static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *signature_digest);
-static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream);
-static bool read_and_send_acl(JCR *jcr, int acltype, int stream);
+bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream);
 static bool crypto_session_start(JCR *jcr);
 static void crypto_session_end(JCR *jcr);
 static bool crypto_session_send(JCR *jcr, BSOCK *sd);
 
-typedef struct CurFile {
-   hlink link;
-   char *fname;
-   time_t ctime;
-   time_t mtime;
-   bool seen;
-} CurFile;
-
-#define accurate_mark_file_as_seen(elt) ((elt)->seen = 1)
-#define accurate_file_has_been_seen(elt) ((elt)->seen)
-
-/*
- * This function is called for each file seen in fileset.
- * We check in file_list hash if fname have been backuped
- * the last time. After we can compare Lstat field. 
- * Full Lstat usage have been removed on 6612 
- */
-bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
-{
-   bool stat = false;
-   char *fname;
-   CurFile *elt;
-
-   if (!jcr->accurate || jcr->JobLevel == L_FULL) {
-      return true;
-   }
-
-   strip_path(ff_pkt);
-   if (S_ISDIR(ff_pkt->statp.st_mode)) {
-      fname = ff_pkt->link;
-   } else {
-      fname = ff_pkt->fname;
-   } 
-
-   elt = (CurFile *)jcr->file_list->lookup(fname);
-
-   if (!elt) {
-      Dmsg1(500, "accurate %s = yes (not found)\n", fname);
-      stat = true;
-      goto bail_out;
-   }
-
-   if (accurate_file_has_been_seen(elt)) {
-      Dmsg1(500, "accurate %s = no (already seen)\n", fname);
-      goto bail_out;
-   }
-
-   if (elt->mtime != ff_pkt->statp.st_mtime) {
-     Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
-     stat = true;
-   } else if (elt->ctime != ff_pkt->statp.st_ctime) {
-     Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
-     stat = true;
-   }
-
-   accurate_mark_file_as_seen(elt);
-   Dmsg2(500, "accurate %s = %i\n", fname, stat);
-
-bail_out:
-   unstrip_path(ff_pkt);
-   return stat;
-}
-
-/* 
- * This function doesn't work very well with smartalloc
- * TODO: use bigbuffer from htable
- */
-int accurate_cmd(JCR *jcr)
-{
-   BSOCK *dir = jcr->dir_bsock;
-   int len;
-   struct stat statp;
-   int32_t LinkFIc;
-   uint64_t nb;
-   CurFile *elt=NULL;
-   char *lstat;
-
-   if (jcr->accurate==false || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
-      return true;
-   }
-
-   if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
-      dir->fsend(_("2991 Bad accurate command\n"));
-      return false;
-   }
-
-   jcr->file_list = (htable *)malloc(sizeof(htable));
-   jcr->file_list->init(elt, &elt->link, nb);
-
-   /*
-    * buffer = sizeof(CurFile) + dirmsg
-    * dirmsg = fname + \0 + lstat
-    */
-   /* get current files */
-   while (dir->recv() >= 0) {
-      len = strlen(dir->msg);
-      if ((len+1) < dir->msglen) {
-//       elt = (CurFile *)malloc(sizeof(CurFile));
-//       elt->fname  = (char *) malloc(dir->msglen+1);
-
-         /* we store CurFile, fname and ctime/mtime in the same chunk */
-         elt = (CurFile *)malloc(sizeof(CurFile)+len+1);
-         elt->fname  = (char *) elt+sizeof(CurFile);
-         strcpy(elt->fname, dir->msg);
-         lstat = dir->msg + len + 1;
-         decode_stat(lstat, &statp, &LinkFIc); /* decode catalog stat */
-         elt->ctime = statp.st_ctime;
-         elt->mtime = statp.st_mtime;
-         elt->seen = 0;
-         jcr->file_list->insert(elt->fname, elt); 
-         Dmsg2(500, "add fname=%s lstat=%s\n", elt->fname, lstat);
-      }
-   }
-   extern void *start_heap;
-
-   char b1[50], b2[50], b3[50], b4[50], b5[50];
-   Dmsg5(1," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
-        edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
-        edit_uint64_with_commas(sm_bytes, b2),
-        edit_uint64_with_commas(sm_max_bytes, b3),
-        edit_uint64_with_commas(sm_buffers, b4),
-        edit_uint64_with_commas(sm_max_buffers, b5));
-
-//   jcr->file_list->stats();
-
-   return true;
-}
-
-bool accurate_send_deleted_list(JCR *jcr)
-{
-   CurFile *elt;
-   FF_PKT *ff_pkt;
-
-   int stream = STREAM_UNIX_ATTRIBUTES;
-
-   if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
-      goto bail_out;
-   }
-
-   if (jcr->file_list == NULL) {
-      goto bail_out;
-   }
-
-   ff_pkt = init_find_files();
-   ff_pkt->type = FT_DELETED;
-
-   foreach_htable (elt, jcr->file_list) {
-      if (!accurate_file_has_been_seen(elt)) { /* already seen */
-         Dmsg2(500, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
-         ff_pkt->fname = elt->fname;
-         ff_pkt->statp.st_mtime = elt->mtime;
-         ff_pkt->statp.st_ctime = elt->ctime;
-         encode_and_send_attributes(jcr, ff_pkt, stream);
-      }
-//      Free(elt->fname);
-   }
-   term_find_files(ff_pkt);
-bail_out:
-   /* TODO: clean htable when this function is not reached ? */
-   if (jcr->file_list) {
-      jcr->file_list->destroy();
-      free(jcr->file_list);
-      jcr->file_list = NULL;
-   }
-   return true;
-}
-
-/*
- * check for BSD nodump flag
- */
-static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
-{
-#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
-   if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
-        (ff_pkt->statp.st_flags & UF_NODUMP) ) {
-      Jmsg(jcr, M_INFO, 1, _("     NODUMP flag set - will not process %s\n"),
-           ff_pkt->fname);
-      return true;                    /* do not backup this file */
-   }
-#endif
-   return false;                      /* do backup */
-}
-
 /*
  * Find all the requested files and send them
  * to the Storage daemon.
@@ -267,7 +97,7 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
    } else {
       buf_size = 0;                   /* use default */
    }
-   if (!bnet_set_buffer_size(sd, buf_size, BNET_SETBUF_WRITE)) {
+   if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) {
       set_jcr_job_status(jcr, JS_ErrorTerminated);
       Jmsg(jcr, M_FATAL, 0, _("Cannot set buffer size FD->SD.\n"));
       return false;
@@ -275,13 +105,14 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
 
    jcr->buf_size = sd->msglen;
    /* Adjust for compression so that output buffer is
-    * 12 bytes + 0.1% larger than input buffer plus 18 bytes.
-    * This gives a bit extra plus room for the sparse addr if any.
-    * Note, we adjust the read size to be smaller so that the
-    * same output buffer can be used without growing it.
+    *  12 bytes + 0.1% larger than input buffer plus 18 bytes.
+    *  This gives a bit extra plus room for the sparse addr if any.
+    *  Note, we adjust the read size to be smaller so that the
+    *  same output buffer can be used without growing it.
     *
-    * The zlib compression workset is initialized here to minimise
-    * the "per file" load. The jcr member is only set, if the init was successful.
+    * The zlib compression workset is initialized here to minimize
+    *  the "per file" load. The jcr member is only set, if the init 
+    *  was successful.
     */
    jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30;
    jcr->compress_buf = get_memory(jcr->compress_buf_size);
@@ -315,7 +146,17 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
    
    start_heartbeat_monitor(jcr);
 
-   jcr->acl_text = get_pool_memory(PM_MESSAGE);
+   if (have_acl) {
+      jcr->acl_data = (acl_data_t *)malloc(sizeof(acl_data_t));
+      memset((caddr_t)jcr->acl_data, 0, sizeof(acl_data_t));
+      jcr->acl_data->content = get_pool_memory(PM_MESSAGE);
+   }
+
+   if (have_xattr) {
+      jcr->xattr_data = (xattr_data_t *)malloc(sizeof(xattr_data_t));
+      memset((caddr_t)jcr->xattr_data, 0, sizeof(xattr_data_t));
+      jcr->xattr_data->content = get_pool_memory(PM_MESSAGE);
+   }
 
    /* Subroutine save_file() is called for each file */
    if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, plugin_save)) {
@@ -323,14 +164,31 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
       set_jcr_job_status(jcr, JS_ErrorTerminated);
    }
 
-   accurate_send_deleted_list(jcr);              /* send deleted list to SD  */
+   if (have_acl && jcr->acl_data->nr_errors > 0) {
+      Jmsg(jcr, M_ERROR, 0, _("Encountered %ld acl errors while doing backup\n"),
+           jcr->acl_data->nr_errors);
+   }
+   if (have_xattr && jcr->xattr_data->nr_errors > 0) {
+      Jmsg(jcr, M_ERROR, 0, _("Encountered %ld xattr errors while doing backup\n"),
+           jcr->xattr_data->nr_errors);
+   }
 
-   free_pool_memory(jcr->acl_text);
+   accurate_finish(jcr);              /* send deleted or base file list to SD */
 
    stop_heartbeat_monitor(jcr);
 
    sd->signal(BNET_EOD);            /* end of sending data */
 
+   if (have_acl && jcr->acl_data) {
+      free_pool_memory(jcr->acl_data->content);
+      free(jcr->acl_data);
+      jcr->acl_data = NULL;
+   }
+   if (have_xattr && jcr->xattr_data) {
+      free_pool_memory(jcr->xattr_data->content);
+      free(jcr->xattr_data);
+      jcr->xattr_data = NULL;
+   }
    if (jcr->big_buf) {
       free(jcr->big_buf);
       jcr->big_buf = NULL;
@@ -469,14 +327,10 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       break;
    case FT_REGE:
       Dmsg1(130, "FT_REGE saving: %s\n", ff_pkt->fname);
-      if (no_dump(jcr, ff_pkt))
-          return 1;
       has_file_data = true;
       break;
    case FT_REG:
       Dmsg1(130, "FT_REG saving: %s\n", ff_pkt->fname);
-      if (no_dump(jcr, ff_pkt))
-          return 1;
       has_file_data = true;
       break;
    case FT_LNK:
@@ -484,8 +338,6 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       break;
    case FT_DIRBEGIN:
       jcr->num_files_examined--;      /* correct file count */
-      if (no_dump(jcr, ff_pkt))       /* disable recursion on nodump directories */
-          ff_pkt->flags |= FO_NO_RECURSION;
       return 1;                       /* not used */
    case FT_NORECURSE:
       Jmsg(jcr, M_INFO, 1, _("     Recursion turned off. Will not descend from %s into %s\n"),
@@ -529,23 +381,23 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       break;
    case FT_NOACCESS: {
       berrno be;
-      Jmsg(jcr, M_NOTSAVED, 0, _("     Could not access %s: ERR=%s\n"), ff_pkt->fname,
+      Jmsg(jcr, M_NOTSAVED, 0, _("     Could not access \"%s\": ERR=%s\n"), ff_pkt->fname,
          be.bstrerror(ff_pkt->ff_errno));
-      jcr->Errors++;
+      jcr->JobErrors++;
       return 1;
    }
    case FT_NOFOLLOW: {
       berrno be;
-      Jmsg(jcr, M_NOTSAVED, 0, _("     Could not follow link %s: ERR=%s\n"), 
+      Jmsg(jcr, M_NOTSAVED, 0, _("     Could not follow link \"%s\": ERR=%s\n"), 
            ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno));
-      jcr->Errors++;
+      jcr->JobErrors++;
       return 1;
    }
    case FT_NOSTAT: {
       berrno be;
-      Jmsg(jcr, M_NOTSAVED, 0, _("     Could not stat %s: ERR=%s\n"), ff_pkt->fname,
+      Jmsg(jcr, M_NOTSAVED, 0, _("     Could not stat \"%s\": ERR=%s\n"), ff_pkt->fname,
          be.bstrerror(ff_pkt->ff_errno));
-      jcr->Errors++;
+      jcr->JobErrors++;
       return 1;
    }
    case FT_DIRNOCHG:
@@ -557,15 +409,15 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       return 1;
    case FT_NOOPEN: {
       berrno be;
-      Jmsg(jcr, M_NOTSAVED, 0, _("     Could not open directory %s: ERR=%s\n"), 
+      Jmsg(jcr, M_NOTSAVED, 0, _("     Could not open directory \"%s\": ERR=%s\n"), 
            ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno));
-      jcr->Errors++;
+      jcr->JobErrors++;
       return 1;
    }
    default:
       Jmsg(jcr, M_NOTSAVED, 0,  _("     Unknown file type %d; not saved: %s\n"), 
            ff_pkt->type, ff_pkt->fname);
-      jcr->Errors++;
+      jcr->JobErrors++;
       return 1;
    }
 
@@ -608,10 +460,12 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       }
 
       /*
-       * Set up signature digest handling. If this fails, the signature digest will be set to
-       * NULL and not used.
+       * Set up signature digest handling. If this fails, the signature digest
+       * will be set to NULL and not used.
+       */
+      /* TODO landonf: We should really only calculate the digest once, for
+       * both verification and signing.
        */
-      // TODO landonf: We should really only calculate the digest once, for both verification and signing.
       if (jcr->crypto.pki_sign) {
          signing_digest = crypto_digest_new(jcr, signing_algorithm);
 
@@ -619,7 +473,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          if (signing_digest == NULL) {
             Jmsg(jcr, M_NOTSAVED, 0, _("%s signature digest initialization failed\n"),
                stream_to_ascii(signing_algorithm));
-            jcr->Errors++;
+            jcr->JobErrors++;
             goto good_rtn;
          }
       }
@@ -675,7 +529,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       do_read = true;
    }
 
-   Dmsg1(100, "do_read=%d\n", do_read);
+   Dmsg1(400, "do_read=%d\n", do_read);
    if (do_read) {
       btimer_t *tid;
 
@@ -689,9 +543,9 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       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,
+         Jmsg(jcr, M_NOTSAVED, 0, _("     Cannot open \"%s\": ERR=%s.\n"), ff_pkt->fname,
               be.bstrerror());
-         jcr->Errors++;
+         jcr->JobErrors++;
          if (tid) {
             stop_thread_timer(tid);
             tid = NULL;
@@ -716,64 +570,99 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       }
    }
 
-#ifdef HAVE_DARWIN_OS
-   /* Regular files can have resource forks and Finder Info */
-   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->Errors++;
-            if (is_bopen(&ff_pkt->bfd)) {
-               bclose(&ff_pkt->bfd);
+   if (have_darwin_os) {
+      /* Regular files can have resource forks and Finder Info */
+      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);
+               }
+               goto good_rtn;
+            }
+            flags = ff_pkt->flags;
+            ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE);
+            if (flags & FO_ENCRYPT) {
+               rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA;
+            } else {
+               rsrc_stream = STREAM_MACOS_FORK_DATA;
+            }
+            stat = send_data(jcr, rsrc_stream, ff_pkt, digest, signing_digest);
+            ff_pkt->flags = flags;
+            bclose(&ff_pkt->bfd);
+            if (!stat) {
+               goto bail_out;
             }
-            goto good_rtn;
          }
-         flags = ff_pkt->flags;
-         ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE);
-         if (flags & FO_ENCRYPT) {
-            rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA;
-         } else {
-            rsrc_stream = STREAM_MACOS_FORK_DATA;
+
+         Dmsg1(300, "Saving Finder Info for \"%s\"\n", ff_pkt->fname);
+         sd->fsend("%ld %d 0", jcr->JobFiles, STREAM_HFSPLUS_ATTRIBUTES);
+         Dmsg1(300, "bfiled>stored:header %s\n", sd->msg);
+         pm_memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32);
+         sd->msglen = 32;
+         if (digest) {
+            crypto_digest_update(digest, (uint8_t *)sd->msg, sd->msglen);
          }
-         stat = send_data(jcr, rsrc_stream, ff_pkt, digest, signing_digest);
-         ff_pkt->flags = flags;
-         bclose(&ff_pkt->bfd);
-         if (!stat) {
-            goto bail_out;
+         if (signing_digest) {
+            crypto_digest_update(signing_digest, (uint8_t *)sd->msg, sd->msglen);
          }
+         sd->send();
+         sd->signal(BNET_EOD);
       }
+   }
 
-      Dmsg1(300, "Saving Finder Info for \"%s\"\n", ff_pkt->fname);
-      sd->fsend("%ld %d 0", jcr->JobFiles, STREAM_HFSPLUS_ATTRIBUTES);
-      Dmsg1(300, "bfiled>stored:header %s\n", sd->msg);
-      memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32);
-      sd->msglen = 32;
-      if (digest) {
-         crypto_digest_update(digest, (uint8_t *)sd->msg, sd->msglen);
-      }
-      if (signing_digest) {
-         crypto_digest_update(signing_digest, (uint8_t *)sd->msg, sd->msglen);
+   /*
+    * Save ACLs when requested and available for anything not being a symlink and not being a plugin.
+    */
+   if (have_acl) {
+      if (ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK && !ff_pkt->cmd_plugin) {
+         switch (build_acl_streams(jcr, ff_pkt)) {
+         case bacl_exit_fatal:
+            goto bail_out;
+         case bacl_exit_error:
+            /*
+             * Non-fatal errors, count them and when the number is under ACL_REPORT_ERR_MAX_PER_JOB
+             * print the error message set by the lower level routine in jcr->errmsg.
+             */
+            if (jcr->acl_data->nr_errors < ACL_REPORT_ERR_MAX_PER_JOB) {
+               Jmsg(jcr, M_ERROR, 0, "%s", jcr->errmsg);
+            }
+            jcr->acl_data->nr_errors++;
+            break;
+         case bacl_exit_ok:
+            break;
+         }
       }
-      sd->send();
-      sd->signal(BNET_EOD);
    }
-#endif
 
-   if (ff_pkt->flags & FO_ACL) {
-      /* Read access ACLs for files, dirs and links */
-      if (!read_and_send_acl(jcr, BACL_TYPE_ACCESS, STREAM_UNIX_ACCESS_ACL)) {
-         goto bail_out;
-      }
-      /* Directories can have default ACLs too */
-      if (ff_pkt->type == FT_DIREND && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
-         if (!read_and_send_acl(jcr, BACL_TYPE_DEFAULT, STREAM_UNIX_DEFAULT_ACL)) {
+   /*
+    * Save Extended Attributes when requested and available for all files not being a plugin.
+    */
+   if (have_xattr) {
+      if (ff_pkt->flags & FO_XATTR && !ff_pkt->cmd_plugin) {
+         switch (build_xattr_streams(jcr, ff_pkt)) {
+         case bxattr_exit_fatal:
             goto bail_out;
+         case bxattr_exit_error:
+            /*
+             * Non-fatal errors, count them and when the number is under XATTR_REPORT_ERR_MAX_PER_JOB
+             * print the error message set by the lower level routine in jcr->errmsg.
+             */
+            if (jcr->xattr_data->nr_errors < XATTR_REPORT_ERR_MAX_PER_JOB) {
+               Jmsg(jcr, M_ERROR, 0, "%s", jcr->errmsg);
+            }
+            jcr->xattr_data->nr_errors++;
+            break;
+         case bxattr_exit_ok:
+            break;
          }
       }
    }
@@ -804,7 +693,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       }
 
       /* Send our header */
-      sd->fsend("%ld %d 0", jcr->JobFiles, STREAM_SIGNED_DIGEST);
+      sd->fsend("%ld %ld 0", jcr->JobFiles, STREAM_SIGNED_DIGEST);
       Dmsg1(300, "bfiled>stored:header %s\n", sd->msg);
 
       /* Encode signature data */
@@ -872,8 +761,8 @@ bail_out:
  * Currently this is not a problem as the only other stream, resource forks,
  * are not handled as sparse files.
  */
-int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, 
-              DIGEST *signing_digest)
+static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, 
+                     DIGEST *signing_digest)
 {
    BSOCK *sd = jcr->store_bsock;
    uint64_t fileAddr = 0;             /* file address */
@@ -965,8 +854,10 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
     *    <file-index> <stream> <info>
     */
    if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
+      if (!job_canceled(jcr)) {
+         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+               sd->bstrerror());
+      }
       goto err;
    }
    Dmsg1(300, ">stored: datahdr %s\n", sd->msg);
@@ -1001,19 +892,21 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
       /* Check for sparse blocks */
       if (ff_pkt->flags & FO_SPARSE) {
          ser_declare;
-         bool haveBlock = true;
-         if (sd->msglen == rsize &&
-             fileAddr+sd->msglen < (uint64_t)ff_pkt->statp.st_size ||
+         bool allZeros = false;
+         if ((sd->msglen == rsize &&
+              fileAddr+sd->msglen < (uint64_t)ff_pkt->statp.st_size) ||
              ((ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO) &&
                (uint64_t)ff_pkt->statp.st_size == 0)) {
-            haveBlock = !is_buf_zero(rbuf, rsize);
+            allZeros = is_buf_zero(rbuf, rsize);
          }
-         if (haveBlock) {
+         if (!allZeros) {
+            /* Put file address as first data in buffer */
             ser_begin(wbuf, SPARSE_FADDR_SIZE);
             ser_uint64(fileAddr);     /* store fileAddr in begin of buffer */
          }
          fileAddr += sd->msglen;      /* update file address */
-         if (!haveBlock) {
+         /* Skip block of all zeros */
+         if (allZeros) {
             continue;                 /* skip block of zeros */
          }
       }
@@ -1121,8 +1014,10 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
       }
       sd->msg = wbuf;              /* set correct write buffer */
       if (!sd->send()) {
-         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-               sd->bstrerror());
+         if (!job_canceled(jcr)) {
+            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);
@@ -1136,8 +1031,8 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
       berrno be;
       Jmsg(jcr, M_ERROR, 0, _("Read error on file %s. ERR=%s\n"),
          ff_pkt->fname, be.bstrerror(ff_pkt->bfd.berrno));
-      if (jcr->Errors++ > 1000) {       /* insanity check */
-         Jmsg(jcr, M_FATAL, 0, _("Too many errors.\n"));
+      if (jcr->JobErrors++ > 1000) {       /* insanity check */
+         Jmsg(jcr, M_FATAL, 0, _("Too many errors. JobErrors=%d.\n"), jcr->JobErrors);
       }
    } else if (ff_pkt->flags & FO_ENCRYPT) {
       /* 
@@ -1156,8 +1051,10 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
          sd->msglen = encrypted_len;      /* set encrypted length */
          sd->msg = jcr->crypto.crypto_buf;       /* set correct write buffer */
          if (!sd->send()) {
-            Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-                  sd->bstrerror());
+            if (!job_canceled(jcr)) {
+               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);
@@ -1167,8 +1064,10 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
    }
 
    if (!sd->signal(BNET_EOD)) {        /* indicate end of file data */
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
+      if (!job_canceled(jcr)) {
+         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+               sd->bstrerror());
+      }
       goto err;
    }
 
@@ -1189,62 +1088,7 @@ err:
    return 0;
 }
 
-/*
- * Read and send an ACL for the last encountered file.
- */
-static bool read_and_send_acl(JCR *jcr, int acltype, int stream)
-{
-#ifdef HAVE_ACL
-   BSOCK *sd = jcr->store_bsock;
-   POOLMEM *msgsave;
-   int len;
-#ifdef FD_NO_SEND_TEST
-   return true;
-#endif
-
-   len = bacl_get(jcr, acltype);
-   if (len < 0) {
-      Jmsg1(jcr, M_WARNING, 0, _("Error reading ACL of %s\n"), jcr->last_fname);
-      return true; 
-   }
-   if (len == 0) {
-      return true;                    /* no ACL */
-   }
-
-   /* 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 false;
-   }
-
-   /* Send the buffer to the storage deamon */
-   Dmsg2(400, "Backing up ACL type 0x%2x <%s>\n", acltype, jcr->acl_text);
-   msgsave = sd->msg;
-   sd->msg = jcr->acl_text;
-   sd->msglen = 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 false;
-   }
-
-   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 false;
-   }
-
-   Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
-#endif
-   return true;
-}
-
-static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) 
+bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) 
 {
    BSOCK *sd = jcr->store_bsock;
    char attribs[MAXSTRING];
@@ -1262,7 +1106,7 @@ static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_strea
       Jmsg0(jcr, M_FATAL, 0, _("Invalid file flags, no supported data stream type.\n"));
       return false;
    }
-   encode_stat(attribs, ff_pkt, data_stream);
+   encode_stat(attribs, &ff_pkt->statp, ff_pkt->LinkFI, data_stream);
 
    /* Now possibly extend the attributes */
    attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
@@ -1280,8 +1124,10 @@ static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_strea
     *    <file-index> <stream> <info>
     */
    if (!sd->fsend("%ld %d 0", jcr->JobFiles, attr_stream)) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
+      if (!job_canceled(jcr)) {
+         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+               sd->bstrerror());
+      }
       return false;
    }
    Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
@@ -1320,8 +1166,10 @@ static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_strea
 
    Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
    if (!stat) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-            sd->bstrerror());
+      if (!job_canceled(jcr)) {
+         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+               sd->bstrerror());
+      }
       return false;
    }
    sd->signal(BNET_EOD);            /* indicate end of attributes data */
@@ -1339,9 +1187,9 @@ static bool do_strip(int count, char *in)
 
    /* Copy to first path separator -- Win32 might have c: ... */
    while (*in && !IsPathSeparator(*in)) {    
-      *out++ = *in++;
+      out++; in++;
    }
-   *out++ = *in++;
+   out++; in++;
    numsep++;                     /* one separator seen */
    for (stripped=0; stripped<count && *in; stripped++) {
       while (*in && !IsPathSeparator(*in)) {
@@ -1372,7 +1220,7 @@ static bool do_strip(int count, char *in)
  *   in handling vendor migrations where files have been restored with
  *   a vendor product into a subdirectory.
  */
-static void strip_path(FF_PKT *ff_pkt)
+void strip_path(FF_PKT *ff_pkt)
 {
    if (!(ff_pkt->flags & FO_STRIPPATH) || ff_pkt->strip_path <= 0) {
       Dmsg1(200, "No strip for %s\n", ff_pkt->fname);
@@ -1383,6 +1231,12 @@ static void strip_path(FF_PKT *ff_pkt)
      ff_pkt->link_save = get_pool_memory(PM_FNAME);
    }
    pm_strcpy(ff_pkt->fname_save, ff_pkt->fname);
+   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));
+      sm_check(__FILE__, __LINE__, true);
+   }
 
    /* 
     * Strip path.  If it doesn't succeed put it back.  If
@@ -1392,28 +1246,34 @@ static void strip_path(FF_PKT *ff_pkt)
     * Do not strip symlinks.
     * I.e. if either stripping fails don't strip anything.
     */
-   if (do_strip(ff_pkt->strip_path, ff_pkt->fname)) {
-      /* Strip links but not symlinks */
-      if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) {
-         pm_strcpy(ff_pkt->link_save, ff_pkt->link);
-         if (!do_strip(ff_pkt->strip_path, ff_pkt->link)) {
-            strcpy(ff_pkt->link, ff_pkt->link_save);
-            strcpy(ff_pkt->fname, ff_pkt->fname_save);
-         }
-      }
-   } else {
-      strcpy(ff_pkt->fname, ff_pkt->fname_save);
+   if (!do_strip(ff_pkt->strip_path, ff_pkt->fname)) {
+      unstrip_path(ff_pkt);
+      goto rtn;
    } 
-   Dmsg2(200, "fname=%s stripped=%s\n", ff_pkt->fname_save, ff_pkt->fname);
+   /* 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);
+      }
+   }
+
+rtn:
+   Dmsg3(100, "fname=%s stripped=%s link=%s\n", ff_pkt->fname_save, ff_pkt->fname, 
+       ff_pkt->link);
 }
 
-static void unstrip_path(FF_PKT *ff_pkt)
+void unstrip_path(FF_PKT *ff_pkt)
 {
    if (!(ff_pkt->flags & FO_STRIPPATH) || ff_pkt->strip_path <= 0) {
       return;
    }
    strcpy(ff_pkt->fname, ff_pkt->fname_save);
    if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) {
+      Dmsg2(500, "strcpy link=%s link_save=%s\n", ff_pkt->link,
+          ff_pkt->link_save);
       strcpy(ff_pkt->link, ff_pkt->link_save);
+      Dmsg2(500, "strcpy link=%d link_save=%d\n", strlen(ff_pkt->link),
+          strlen(ff_pkt->link_save));
+      sm_check(__FILE__, __LINE__, true);
    }
 }