]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/backup.c
Fix bug #1246 Sometimes access denied with VSS enabled. UCS
[bacula/bacula] / bacula / src / filed / backup.c
index 0f86e2247c1819a12a715961dd26ebe2ac1e9fc7..e0c9ba9f46a37d3e9bf894487514b201ae07f6d5 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"
 
 /* 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;
-   char *lstat;
-   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. 
- * 
- */
-/* TODO: tweak verify code to use the same function ?? */
-bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
-{
-   char *p;
-   int stat=false;
-   struct stat statc;                 /* catalog stat */
-   char *Opts_Digest;
-   char *fname;
-   CurFile *elt;
-
-   int32_t LinkFIc;
-
-   if (*ff_pkt->VerifyOpts) {  /* use mtime + ctime checks by default */
-      Opts_Digest = ff_pkt->VerifyOpts;
-   } else {
-      Opts_Digest = "cm"; 
-   }
-
-   if (jcr->accurate == false || 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);
-      stat=false;
-      goto bail_out;
-   }
-
-   decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
-//   *do_Digest = CRYPTO_DIGEST_NONE;
-
-   for (p=Opts_Digest; *p; p++) {
-      char ed1[30], ed2[30];
-      switch (*p) {
-      case 'i':                /* compare INODEs */
-         if (statc.st_ino != ff_pkt->statp.st_ino) {
-            Jmsg(jcr, M_SAVED, 0, _("%s      st_ino   differ. Cat: %s File: %s\n"), fname,
-                 edit_uint64((uint64_t)statc.st_ino, ed1),
-                 edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
-            stat = true;
-         }
-         break;
-      case 'p':                /* permissions bits */
-         if (statc.st_mode != ff_pkt->statp.st_mode) {
-            Jmsg(jcr, M_SAVED, 0, _("%s      st_mode  differ. Cat: %x File: %x\n"), fname,
-                 (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
-            stat = true;
-         }
-         break;
-//      case 'n':                /* number of links */
-//         if (statc.st_nlink != ff_pkt->statp.st_nlink) {
-//            Jmsg(jcr, M_SAVED, 0, _("%s      st_nlink differ. Cat: %d File: %d\n"), fname,
-//                 (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
-//            stat = true;
-//         }
-//         break;
-      case 'u':                /* user id */
-         if (statc.st_uid != ff_pkt->statp.st_uid) {
-            Jmsg(jcr, M_SAVED, 0, _("%s      st_uid   differ. Cat: %u File: %u\n"), fname,
-                 (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
-            stat = true;
-         }
-         break;
-      case 'g':                /* group id */
-         if (statc.st_gid != ff_pkt->statp.st_gid) {
-            Jmsg(jcr, M_SAVED, 0, _("%s      st_gid   differ. Cat: %u File: %u\n"), fname,
-                 (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
-            stat = true;
-         }
-         break;
-      case 's':                /* size */
-         if (statc.st_size != ff_pkt->statp.st_size) {
-            Jmsg(jcr, M_SAVED, 0, _("%s      st_size  differ. Cat: %s File: %s\n"), fname,
-                 edit_uint64((uint64_t)statc.st_size, ed1),
-                 edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
-            stat = true;
-         }
-         break;
-//      case 'a':                /* access time */
-//         if (statc.st_atime != ff_pkt->statp.st_atime) {
-//            Jmsg(jcr, M_SAVED, 0, _("%s      st_atime differs\n"), fname);
-//            stat = true;
-//         }
-//         break;
-      case 'm':
-         if (statc.st_mtime != ff_pkt->statp.st_mtime) {
-            Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
-            stat = true;
-         }
-         break;
-      case 'c':                /* ctime */
-         if (statc.st_ctime != ff_pkt->statp.st_ctime) {
-            Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
-            stat = true;
-         }
-         break;
-      case 'd':                /* file size decrease */
-         if (statc.st_size > ff_pkt->statp.st_size) {
-            Jmsg(jcr, M_SAVED, 0, _("%s      st_size  decrease. Cat: %s File: %s\n"), fname,
-                 edit_uint64((uint64_t)statc.st_size, ed1),
-                 edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
-            stat = true;
-         }
-         break;
-      case '5':                /* compare MD5 */
-         Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname);
-//       *do_Digest = CRYPTO_DIGEST_MD5;
-         break;
-      case '1':                 /* compare SHA1 */
-//       *do_Digest = CRYPTO_DIGEST_SHA1;
-         break;
-      case ':':
-      case 'V':
-      default:
-         break;
-      }
-   }
-   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;
-   uint64_t nb;
-   CurFile *elt=NULL;
-
-   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 + 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 lstat in the same chunk */
-         elt = (CurFile *)malloc(sizeof(CurFile)+dir->msglen+1);
-         elt->fname  = (char *) elt+sizeof(CurFile);
-         memcpy(elt->fname, dir->msg, dir->msglen);
-         elt->fname[dir->msglen]='\0';
-         elt->lstat = elt->fname + len + 1;
-        elt->seen=0;
-         jcr->file_list->insert(elt->fname, elt); 
-         Dmsg2(500, "add fname=%s lstat=%s\n", elt->fname, elt->lstat);
-      }
-   }
-
-//   jcr->file_list->stats();
-   /* TODO: send a EOM ?
-   dir->fsend("2000 OK accurate\n");
-    */
-   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 */
-         Dmsg3(500, "deleted fname=%s lstat=%s seen=%i\n", elt->fname, elt->lstat, elt->seen);
-         ff_pkt->fname = elt->fname;
-         decode_stat(elt->lstat, &ff_pkt->statp, &ff_pkt->LinkFI); /* decode catalog stat */
-         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;
-}
-
 /*
  * Find all the requested files and send them
  * to the Storage daemon.
@@ -331,7 +79,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;
@@ -339,13 +87,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);
@@ -379,7 +128,8 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
    
    start_heartbeat_monitor(jcr);
 
-   jcr->acl_text = get_pool_memory(PM_MESSAGE);
+   jcr->acl_data = get_pool_memory(PM_MESSAGE);
+   jcr->xattr_data = 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)) {
@@ -389,12 +139,18 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
 
    accurate_send_deleted_list(jcr);              /* send deleted list to SD  */
 
-   free_pool_memory(jcr->acl_text);
-
    stop_heartbeat_monitor(jcr);
 
    sd->signal(BNET_EOD);            /* end of sending data */
 
+   if (jcr->acl_data) {
+      free_pool_memory(jcr->acl_data);
+      jcr->acl_data = NULL;
+   }
+   if (jcr->xattr_data) {
+      free_pool_memory(jcr->xattr_data);
+      jcr->xattr_data = NULL;
+   }
    if (jcr->big_buf) {
       free(jcr->big_buf);
       jcr->big_buf = NULL;
@@ -552,7 +308,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       break;
    case FT_NOFSCHG:
       /* Suppress message for /dev filesystems */
-      if (strncmp(ff_pkt->fname, "/dev/", 5) != 0) {
+      if (!is_in_fileset(ff_pkt)) {
          Jmsg(jcr, M_INFO, 1, _("     %s is a different filesystem. Will not descend from %s into %s\n"),
               ff_pkt->fname, ff_pkt->top_fname, ff_pkt->fname);
       }
@@ -587,23 +343,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:
@@ -615,15 +371,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;
    }
 
@@ -677,7 +433,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;
          }
       }
@@ -733,7 +489,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;
 
@@ -747,9 +503,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;
@@ -784,9 +540,9 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          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"), 
+            Jmsg(jcr, M_NOTSAVED, -1, _("     Cannot open resource fork for \"%s\": ERR=%s.\n"), 
                  ff_pkt->fname, be.bstrerror());
-            jcr->Errors++;
+            jcr->JobErrors++;
             if (is_bopen(&ff_pkt->bfd)) {
                bclose(&ff_pkt->bfd);
             }
@@ -810,7 +566,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       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);
+      pm_memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32);
       sd->msglen = 32;
       if (digest) {
          crypto_digest_update(digest, (uint8_t *)sd->msg, sd->msglen);
@@ -823,17 +579,20 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    }
 #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)) {
+   /*
+    * Save ACLs for anything not being a symlink.
+    */
+   if (ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK) {
+      if (!build_acl_streams(jcr, ff_pkt))
+         goto bail_out;
+   }
+
+   /*
+    * Save Extended Attributes for all files.
+    */
+   if (ff_pkt->flags & FO_XATTR) {
+      if (!build_xattr_streams(jcr, ff_pkt))
          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)) {
-            goto bail_out;
-         }
-      }
    }
 
    /* Terminate the signing digest and send it to the Storage daemon */
@@ -862,7 +621,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 */
@@ -930,8 +689,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 */
@@ -1059,19 +818,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 */
          }
       }
@@ -1194,7 +955,7 @@ 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 */
+      if (jcr->JobErrors++ > 1000) {       /* insanity check */
          Jmsg(jcr, M_FATAL, 0, _("Too many errors.\n"));
       }
    } else if (ff_pkt->flags & FO_ENCRYPT) {
@@ -1247,62 +1008,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];
@@ -1320,7 +1026,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);
@@ -1397,9 +1103,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)) {
@@ -1424,9 +1130,13 @@ static bool do_strip(int count, char *in)
 }
 
 /*
- * If requested strip leading components of the path
+ * If requested strip leading components of the path so that we can
+ *   save file as if it came from a subdirectory.  This is most useful
+ *   for dealing with snapshots, by removing the snapshot directory, or
+ *   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);
@@ -1437,6 +1147,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
@@ -1446,28 +1162,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);
    }
 }