]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/backup.c
Tweak restart incomplete jobs
[bacula/bacula] / bacula / src / filed / backup.c
index a8303798bfac11859c2462ab78913efa607fb8fa..7330fde8781a4af34ecf505588420b5cf5db91de 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2011 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.
    This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
@@ -15,7 +15,7 @@
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
+   You should have received a copy of the GNU Affero General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
@@ -61,6 +61,7 @@ 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);
+static void close_vss_backup_session(JCR *jcr);
 
 /**
  * Find all the requested files and send them
@@ -138,7 +139,7 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
 
    set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime);
 
-   /** in accurate mode, we overwrite the find_one check function */
+   /** in accurate mode, we overload the find_one check function */
    if (jcr->accurate) {
       set_find_changed_function((FF_PKT *)jcr->ff, accurate_check_file);
    } 
@@ -172,6 +173,8 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
            jcr->xattr_data->nr_errors);
    }
 
+   close_vss_backup_session(jcr);
+
    accurate_finish(jcr);              /* send deleted or base file list to SD */
 
    stop_heartbeat_monitor(jcr);
@@ -299,6 +302,7 @@ static bool crypto_session_send(JCR *jcr, BSOCK *sd)
 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
 {
    bool do_read = false;
+   bool plugin_started = false;
    int stat, data_stream; 
    int rtnstat = 0;
    DIGEST *digest = NULL;
@@ -314,7 +318,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
 #endif
    BSOCK *sd = jcr->store_bsock;
 
-   if (jcr->is_job_canceled()) {
+   if (jcr->is_canceled() || jcr->is_incomplete()) {
       return 0;
    }
 
@@ -336,7 +340,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       Dmsg2(130, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link);
       break;
    case FT_RESTORE_FIRST:
-      Dmsg1(00, "FT_RESTORE_FIRST saving: %s\n", ff_pkt->fname);
+      Dmsg1(100, "FT_RESTORE_FIRST saving: %s\n", ff_pkt->fname);
       break;
    case FT_DIRBEGIN:
       jcr->num_files_examined--;      /* correct file count */
@@ -349,8 +353,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    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 %s\n"),
-              ff_pkt->fname, ff_pkt->top_fname, ff_pkt->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;
@@ -364,6 +368,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
            ff_pkt->fname);
       break;
    case FT_REPARSE:
+   case FT_JUNCTION:
    case FT_DIREND:
       Dmsg1(130, "FT_DIREND: %s\n", ff_pkt->link);
       break;
@@ -497,12 +502,17 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          goto bail_out;
       }
       send_plugin_name(jcr, sd, true);      /* signal start of plugin data */
+      plugin_started = true;
    }
 
    /** Send attributes -- must be done after binit() */
    if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) {
       goto bail_out;
    }
+   /** Meta data only for restore object */
+   if (ff_pkt->type == FT_RESTORE_FIRST) {
+      goto good_rtn;
+   }
 
    /** Set up the encryption context and send the session data to the SD */
    if (has_file_data && jcr->crypto.pki_encrypt) {
@@ -524,15 +534,16 @@ 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_REPARSE || ff_pkt->type == FT_JUNCTION ||
          (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) {
       do_read = true;
    }
+
    if (ff_pkt->cmd_plugin) {
       do_read = true;
    }
 
-   Dmsg1(400, "do_read=%d\n", do_read);
+   Dmsg2(150, "type=%d do_read=%d\n", ff_pkt->type, do_read);
    if (do_read) {
       btimer_t *tid;
 
@@ -542,7 +553,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          tid = NULL;
       }
       int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0;
-      ff_pkt->bfd.reparse_point = ff_pkt->type == FT_REPARSE;
+      ff_pkt->bfd.reparse_point = (ff_pkt->type == FT_REPARSE || 
+                                   ff_pkt->type == FT_JUNCTION);
       if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0) < 0) {
          ff_pkt->ff_errno = errno;
          berrno be;
@@ -592,7 +604,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
                goto good_rtn;
             }
             flags = ff_pkt->flags;
-            ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE);
+            ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE|FO_OFFSETS);
             if (flags & FO_ENCRYPT) {
                rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA;
             } else {
@@ -733,14 +745,17 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       sd->send();
       sd->signal(BNET_EOD);              /* end of checksum */
    }
-   if (ff_pkt->cmd_plugin) {
-      send_plugin_name(jcr, sd, false); /* signal end of plugin data */
-   }
 
 good_rtn:
-   rtnstat = 1;                       /* good return */
+   rtnstat = jcr->is_canceled() ? 0 : 1; /* good return if not canceled */
 
 bail_out:
+   if (jcr->is_incomplete()) {
+      rtnstat = 0;
+   }
+   if (ff_pkt->cmd_plugin && plugin_started) {
+      send_plugin_name(jcr, sd, false); /* signal end of plugin data */
+   }
    if (digest) {
       crypto_digest_free(digest);
    }
@@ -795,9 +810,9 @@ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
    int zstat;
 
    if (ff_pkt->flags & FO_GZIP) {
-      if (ff_pkt->flags & FO_SPARSE) {
-         cbuf = (Bytef *)jcr->compress_buf + SPARSE_FADDR_SIZE;
-         max_compress_len = jcr->compress_buf_size - SPARSE_FADDR_SIZE;
+      if ((ff_pkt->flags & FO_SPARSE) || (ff_pkt->flags & FO_OFFSETS)) {
+         cbuf = (Bytef *)jcr->compress_buf + OFFSET_FADDR_SIZE;
+         max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE;
       } else {
          cbuf = (Bytef *)jcr->compress_buf;
          max_compress_len = jcr->compress_buf_size; /* set max length */
@@ -826,8 +841,8 @@ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
 #endif
 
    if (ff_pkt->flags & FO_ENCRYPT) {
-      if (ff_pkt->flags & FO_SPARSE) {
-         Jmsg0(jcr, M_FATAL, 0, _("Encrypting sparse data not supported.\n"));
+      if ((ff_pkt->flags & FO_SPARSE) || (ff_pkt->flags & FO_OFFSETS)) {
+         Jmsg0(jcr, M_FATAL, 0, _("Encrypting sparse or offset data not supported.\n"));
          goto err;
       }
       /** Allocate the cipher context */
@@ -869,9 +884,9 @@ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
     * Make space at beginning of buffer for fileAddr because this
     *   same buffer will be used for writing if compression is off.
     */
-   if (ff_pkt->flags & FO_SPARSE) {
-      rbuf += SPARSE_FADDR_SIZE;
-      rsize -= SPARSE_FADDR_SIZE;
+   if ((ff_pkt->flags & FO_SPARSE) || (ff_pkt->flags & FO_OFFSETS)) {
+      rbuf += OFFSET_FADDR_SIZE;
+      rsize -= OFFSET_FADDR_SIZE;
 #ifdef HAVE_FREEBSD_OS
       /**
        * To read FreeBSD partitions, the read size must be
@@ -904,7 +919,7 @@ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
          }
          if (!allZeros) {
             /** Put file address as first data in buffer */
-            ser_begin(wbuf, SPARSE_FADDR_SIZE);
+            ser_begin(wbuf, OFFSET_FADDR_SIZE);
             ser_uint64(fileAddr);     /* store fileAddr in begin of buffer */
          }
          fileAddr += sd->msglen;      /* update file address */
@@ -912,6 +927,10 @@ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
          if (allZeros) {
             continue;                 /* skip block of zeros */
          }
+      } else if (ff_pkt->flags & FO_OFFSETS) {
+         ser_declare;
+         ser_begin(wbuf, OFFSET_FADDR_SIZE);
+         ser_uint64(ff_pkt->bfd.offset);     /* store offset in begin of buffer */
       }
 
       jcr->ReadBytes += sd->msglen;         /* count bytes read */
@@ -976,8 +995,8 @@ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
          uint32_t initial_len = 0;
          ser_declare;
 
-         if (ff_pkt->flags & FO_SPARSE) {
-            cipher_input_len += SPARSE_FADDR_SIZE;
+         if ((ff_pkt->flags & FO_SPARSE) || (ff_pkt->flags & FO_OFFSETS)) {
+            cipher_input_len += OFFSET_FADDR_SIZE;
          }
 
          /** Encrypt the length of the input block */
@@ -1012,8 +1031,8 @@ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest,
       }
 
       /* Send the buffer to the Storage daemon */
-      if (ff_pkt->flags & FO_SPARSE) {
-         sd->msglen += SPARSE_FADDR_SIZE; /* include fileAddr in size */
+      if ((ff_pkt->flags & FO_SPARSE) || (ff_pkt->flags & FO_OFFSETS)) {
+         sd->msglen += OFFSET_FADDR_SIZE; /* include fileAddr in size */
       }
       sd->msg = wbuf;              /* set correct write buffer */
       if (!sd->send()) {
@@ -1096,9 +1115,11 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream)
    BSOCK *sd = jcr->store_bsock;
    char attribs[MAXSTRING];
    char attribsExBuf[MAXSTRING];
-   char *attribsEx;
+   char *attribsEx = NULL;
    int attr_stream;
+   int comp_len;
    bool stat;
+   int hangup = get_hangup();
 #ifdef FD_NO_SEND_TEST
    return true;
 #endif
@@ -1113,8 +1134,12 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream)
    encode_stat(attribs, &ff_pkt->statp, ff_pkt->LinkFI, data_stream);
 
    /** Now possibly extend the attributes */
-   attribsEx = attribsExBuf;
-   attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
+   if (ff_pkt->type == FT_RESTORE_FIRST) {
+      attr_stream = STREAM_RESTORE_OBJECT;
+   } else {
+      attribsEx = attribsExBuf;
+      attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
+   }
 
    Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx);
 
@@ -1124,12 +1149,20 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream)
    pm_strcpy(jcr->last_fname, ff_pkt->fname);
    jcr->unlock();
 
+   /* Debug code: check if we must hangup */
+   if (hangup && (jcr->JobFiles > (uint32_t)hangup)) {
+      jcr->setJobStatus(JS_Incomplete);
+      Jmsg1(jcr, M_FATAL, 0, "Debug hangup requested after %d files.\n", hangup);
+      set_hangup(0);
+      return false;
+   }
+
    /**
     * Send Attributes header to Storage daemon
     *    <file-index> <stream> <info>
     */
    if (!sd->fsend("%ld %d 0", jcr->JobFiles, attr_stream)) {
-      if (!jcr->is_job_canceled()) {
+      if (!jcr->is_canceled() && !jcr->is_incomplete()) {
          Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
                sd->bstrerror());
       }
@@ -1146,6 +1179,17 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream)
     *   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
+    *
     * For a directory, link is the same as fname, but with trailing
     * slash. For a linked file, link is the link.
     */
@@ -1156,22 +1200,58 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream)
    case FT_LNK:
    case FT_LNKSAVED:
       Dmsg2(300, "Link %s to %s\n", ff_pkt->fname, ff_pkt->link);
-      stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c", jcr->JobFiles,
-               ff_pkt->type, ff_pkt->fname, 0, attribs, 0, ff_pkt->link, 0,
-               attribsEx, 0);
+      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);
       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", jcr->JobFiles,
-               ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, attribsEx, 0);
+      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);
       break;
    case FT_RESTORE_FIRST:
-      attribsEx = ff_pkt->object;        /* put object as extended attributes */
-      /* Fall through wanted */
+      comp_len = ff_pkt->object_len;
+      ff_pkt->object_compression = 0;
+      if (ff_pkt->object_len > 1000) {
+         /* Big object, compress it */
+         int stat;
+         comp_len = ff_pkt->object_len + 1000;
+         POOLMEM *comp_obj = get_memory(comp_len);
+         stat = 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);
+      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);
+      }
+      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);
+      break;
    default:
-      stat = sd->fsend("%ld %d %s%c%s%c%c%s%c", jcr->JobFiles,
-               ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0);
+      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);
       break;
    }
    if (ff_pkt->type != FT_DELETED) {
@@ -1288,3 +1368,36 @@ void unstrip_path(FF_PKT *ff_pkt)
       sm_check(__FILE__, __LINE__, true);
    }
 }
+
+static void close_vss_backup_session(JCR *jcr)
+{
+#if defined(WIN32_VSS)
+   /* STOP VSS ON WIN32 */
+   /* tell vss to close the backup session */
+   if (jcr->VSS) {
+      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));
+         }
+      }
+      WCHAR *metadata = g_pVSSClient->GetMetadata();
+      if (metadata) {
+         FF_PKT *ff_pkt = jcr->ff;
+         ff_pkt->fname = (char *)"job";
+         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
+}