2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula File Daemon backup.c send file attributes and data
21 * to the Storage daemon.
23 * Kern Sibbald, March MM
32 const bool have_lzo = true;
34 const bool have_lzo = false;
38 const bool have_libz = true;
40 const bool have_libz = false;
43 /* Forward referenced functions */
44 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
45 static int send_data(bctx_t &bctx, int stream);
46 static void close_vss_backup_session(JCR *jcr);
48 static bool send_resource_fork(bctx_t &bctx);
50 static bool setup_compression(bctx_t &bctx);
51 static bool do_lzo_compression(bctx_t &bctx);
52 static bool do_libz_compression(bctx_t &bctx);
55 * Find all the requested files and send them
56 * to the Storage daemon.
58 * Note, we normally carry on a one-way
59 * conversation from this point on with the SD, simply blasting
60 * data to him. To properly know what is going on, we
61 * also run a "heartbeat" monitor which reads the socket and
62 * reacts accordingly (at the moment it has nothing to do
63 * except echo the heartbeat to the Director).
66 bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
70 // TODO landonf: Allow user to specify encryption algorithm
72 sd = jcr->store_bsock;
74 jcr->setJobStatus(JS_Running);
76 Dmsg1(300, "bfiled: opened data connection %d to stored\n", sd->m_fd);
79 CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
83 buf_size = client->max_network_buffer_size;
85 buf_size = 0; /* use default */
87 if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) {
88 jcr->setJobStatus(JS_ErrorTerminated);
89 Jmsg(jcr, M_FATAL, 0, _("Cannot set buffer size FD->SD.\n"));
93 jcr->buf_size = sd->msglen;
95 * Adjust for compression so that output buffer is
96 * 12 bytes + 0.1% larger than input buffer plus 18 bytes.
97 * This gives a bit extra plus room for the sparse addr if any.
98 * Note, we adjust the read size to be smaller so that the
99 * same output buffer can be used without growing it.
101 * For LZO1X compression the recommended value is :
102 * output_block_size = input_block_size + (input_block_size / 16) + 64 + 3 + sizeof(comp_stream_header)
104 * The zlib compression workset is initialized here to minimize
105 * the "per file" load. The jcr member is only set, if the init
108 * For the same reason, lzo compression is initialized here.
111 jcr->compress_buf_size = MAX(jcr->buf_size + (jcr->buf_size / 16) + 67 + (int)sizeof(comp_stream_header), jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30);
112 jcr->compress_buf = get_memory(jcr->compress_buf_size);
114 jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30;
115 jcr->compress_buf = get_memory(jcr->compress_buf_size);
119 z_stream *pZlibStream = (z_stream*)malloc(sizeof(z_stream));
121 pZlibStream->zalloc = Z_NULL;
122 pZlibStream->zfree = Z_NULL;
123 pZlibStream->opaque = Z_NULL;
124 pZlibStream->state = Z_NULL;
126 if (deflateInit(pZlibStream, Z_DEFAULT_COMPRESSION) == Z_OK) {
127 jcr->pZLIB_compress_workset = pZlibStream;
135 lzo_voidp pLzoMem = (lzo_voidp) malloc(LZO1X_1_MEM_COMPRESS);
137 if (lzo_init() == LZO_E_OK) {
138 jcr->LZO_compress_workset = pLzoMem;
145 if (!crypto_session_start(jcr)) {
149 set_find_options(jcr->ff, jcr->incremental, jcr->mtime);
150 set_find_snapshot_function(jcr->ff, snapshot_convert_path);
152 /** in accurate mode, we overload the find_one check function */
154 set_find_changed_function((FF_PKT *)jcr->ff, accurate_check_file);
156 start_heartbeat_monitor(jcr);
159 jcr->bacl = (BACL*)new_bacl();
162 jcr->bxattr = (BXATTR*)new_bxattr();
165 /* Subroutine save_file() is called for each file */
166 if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, plugin_save)) {
167 ok = false; /* error */
168 jcr->setJobStatus(JS_ErrorTerminated);
171 if (jcr->bacl && jcr->bacl->get_acl_nr_errors() > 0) {
172 Jmsg(jcr, M_WARNING, 0, _("Had %ld acl errors while doing backup\n"),
173 jcr->bacl->get_acl_nr_errors());
177 if (jcr->bxattr && jcr->bxattr->get_xattr_nr_errors() > 0) {
178 Jmsg(jcr, M_WARNING, 0, _("Had %ld xattr errors while doing backup\n"),
179 jcr->bxattr->get_xattr_nr_errors());
183 /* Delete or keep snapshots */
184 close_snapshot_backup_session(jcr);
185 close_vss_backup_session(jcr);
187 accurate_finish(jcr); /* send deleted or base file list to SD */
189 stop_heartbeat_monitor(jcr);
191 sd->signal(BNET_EOD); /* end of sending data */
206 bfree_and_null(jcr->big_buf);
208 if (jcr->compress_buf) {
209 free_and_null_pool_memory(jcr->compress_buf);
211 if (jcr->pZLIB_compress_workset) {
212 /* Free the zlib stream */
214 deflateEnd((z_stream *)jcr->pZLIB_compress_workset);
216 bfree_and_null(jcr->pZLIB_compress_workset);
218 if (jcr->LZO_compress_workset) {
219 bfree_and_null(jcr->LZO_compress_workset);
222 crypto_session_end(jcr);
225 Dmsg1(100, "end blast_data ok=%d\n", ok);
231 * Called here by find() for each file included.
232 * This is a callback. The original is find_files() above.
234 * Send the file and its data to the Storage daemon.
238 * -1 to ignore file/directory (not used here)
240 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
242 bool do_read = false;
243 bool plugin_started = false;
244 bool do_plugin_set = false;
247 bool has_file_data = false;
248 struct save_pkt sp; /* used by option plugin */
249 BSOCK *sd = jcr->store_bsock;
250 bctx_t bctx; /* backup context */
252 memset(&bctx, 0, sizeof(bctx));
254 bctx.ff_pkt = ff_pkt;
258 time_t now = time(NULL);
259 if (jcr->last_stat_time == 0) {
260 jcr->last_stat_time = now;
261 jcr->stat_interval = 30; /* Default 30 seconds */
262 } else if (now >= jcr->last_stat_time + jcr->stat_interval) {
263 jcr->dir_bsock->fsend("Progress JobId=%ld files=%ld bytes=%lld bps=%ld\n",
264 jcr->JobId, jcr->JobFiles, jcr->JobBytes, jcr->LastRate);
265 jcr->last_stat_time = now;
268 if (jcr->is_canceled() || jcr->is_incomplete()) {
269 Dmsg0(100, "Job canceled by user or marked incomplete.\n");
273 jcr->num_files_examined++; /* bump total file count */
275 switch (ff_pkt->type) {
276 case FT_LNKSAVED: /* Hard linked, file already saved */
277 Dmsg2(130, "FT_LNKSAVED hard link: %s => %s\n", ff_pkt->fname, ff_pkt->link);
280 Dmsg1(130, "FT_REGE saving: %s\n", ff_pkt->fname);
281 has_file_data = true;
284 Dmsg1(130, "FT_REG saving: %s\n", ff_pkt->fname);
285 has_file_data = true;
288 Dmsg2(130, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link);
290 case FT_RESTORE_FIRST:
291 Dmsg1(100, "FT_RESTORE_FIRST saving: %s\n", ff_pkt->fname);
293 case FT_PLUGIN_CONFIG:
294 Dmsg1(100, "FT_PLUGIN_CONFIG saving: %s\n", ff_pkt->fname);
297 jcr->num_files_examined--; /* correct file count */
298 return 1; /* not used */
300 Jmsg(jcr, M_INFO, 1, _(" Recursion turned off. Will not descend from %s into %s\n"),
301 ff_pkt->top_fname, ff_pkt->fname);
302 ff_pkt->type = FT_DIREND; /* Backup only the directory entry */
305 /* Suppress message for /dev filesystems */
306 if (!is_in_fileset(ff_pkt)) {
307 Jmsg(jcr, M_INFO, 1, _(" %s is a different filesystem. Will not descend from %s into it.\n"),
308 ff_pkt->fname, ff_pkt->top_fname);
310 ff_pkt->type = FT_DIREND; /* Backup only the directory entry */
313 Jmsg(jcr, M_INFO, 1, _(" Disallowed filesystem. Will not descend from %s into %s\n"),
314 ff_pkt->top_fname, ff_pkt->fname);
315 ff_pkt->type = FT_DIREND; /* Backup only the directory entry */
318 Jmsg(jcr, M_INFO, 1, _(" Disallowed drive type. Will not descend into %s\n"),
324 Dmsg1(130, "FT_DIREND: %s\n", ff_pkt->link);
327 Dmsg1(130, "FT_SPEC saving: %s\n", ff_pkt->fname);
328 if (S_ISSOCK(ff_pkt->statp.st_mode)) {
329 Jmsg(jcr, M_SKIPPED, 1, _(" Socket file skipped: %s\n"), ff_pkt->fname);
334 Dmsg1(130, "FT_RAW saving: %s\n", ff_pkt->fname);
335 has_file_data = true;
338 Dmsg1(130, "FT_FIFO saving: %s\n", ff_pkt->fname);
342 Jmsg(jcr, M_NOTSAVED, 0, _(" Could not access \"%s\": ERR=%s\n"), ff_pkt->fname,
343 be.bstrerror(ff_pkt->ff_errno));
349 Jmsg(jcr, M_NOTSAVED, 0, _(" Could not follow link \"%s\": ERR=%s\n"),
350 ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno));
356 Jmsg(jcr, M_NOTSAVED, 0, _(" Could not stat \"%s\": ERR=%s\n"), ff_pkt->fname,
357 be.bstrerror(ff_pkt->ff_errno));
363 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
366 Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
370 Jmsg(jcr, M_NOTSAVED, 0, _(" Could not open directory \"%s\": ERR=%s\n"),
371 ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno));
376 Dmsg1(130, "FT_DELETED: %s\n", ff_pkt->fname);
379 Jmsg(jcr, M_NOTSAVED, 0, _(" Unknown file type %d; not saved: %s\n"),
380 ff_pkt->type, ff_pkt->fname);
385 Dmsg1(130, "bfiled: sending %s to stored\n", ff_pkt->fname);
387 /** Digests and encryption are only useful if there's file data */
388 if (has_file_data && !crypto_setup_digests(bctx)) {
392 /** Initialize the file descriptor we use for data and other streams. */
394 if (ff_pkt->flags & FO_PORTABLE) {
395 set_portable_backup(&ff_pkt->bfd); /* disable Win32 BackupRead() */
398 if (ff_pkt->cmd_plugin) {
399 do_plugin_set = true;
401 /* option and cmd plugin are not compatible together */
402 } else if (ff_pkt->opt_plugin) {
404 /* ask the option plugin what to do with this file */
405 switch (plugin_option_handle_file(jcr, ff_pkt, &sp)) {
407 Dmsg2(10, "Option plugin %s will be used to backup %s\n",
408 ff_pkt->plugin, ff_pkt->fname);
409 do_plugin_set = true;
412 Dmsg2(10, "Option plugin %s decided to skip %s\n",
413 ff_pkt->plugin, ff_pkt->fname);
416 Dmsg2(10, "Option plugin %s decided to let bacula handle %s\n",
417 ff_pkt->plugin, ff_pkt->fname);
423 /* Tell bfile that it needs to call plugin */
424 if (!set_cmd_plugin(&ff_pkt->bfd, jcr)) {
427 send_plugin_name(jcr, sd, true); /* signal start of plugin data */
428 plugin_started = true;
431 /** Send attributes -- must be done after binit() */
432 if (!encode_and_send_attributes(bctx)) {
435 /** Meta data only for restore object */
436 if (IS_FT_OBJECT(ff_pkt->type)) {
439 /** Meta data only for deleted files */
440 if (ff_pkt->type == FT_DELETED) {
443 /** Set up the encryption context and send the session data to the SD */
444 if (has_file_data && jcr->crypto.pki_encrypt) {
445 if (!crypto_session_send(jcr, sd)) {
451 * Open any file with data that we intend to save, then save it.
453 * Note, if is_win32_backup, we must open the Directory so that
454 * the BackupRead will save its permissions and ownership streams.
456 if (ff_pkt->type != FT_LNKSAVED && S_ISREG(ff_pkt->statp.st_mode)) {
458 do_read = !is_portable_backup(&ff_pkt->bfd) || ff_pkt->statp.st_size > 0;
460 do_read = ff_pkt->statp.st_size > 0;
462 } else if (ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO ||
463 ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_JUNCTION ||
464 (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) {
468 if (ff_pkt->cmd_plugin && !ff_pkt->no_read) {
472 Dmsg2(150, "type=%d do_read=%d\n", ff_pkt->type, do_read);
476 if (ff_pkt->type == FT_FIFO) {
477 tid = start_thread_timer(jcr, pthread_self(), 60);
481 int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0;
482 ff_pkt->bfd.reparse_point = (ff_pkt->type == FT_REPARSE ||
483 ff_pkt->type == FT_JUNCTION);
484 set_fattrs(&ff_pkt->bfd, &ff_pkt->statp);
485 if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0) < 0) {
486 ff_pkt->ff_errno = errno;
488 Jmsg(jcr, M_NOTSAVED, 0, _(" Cannot open \"%s\": ERR=%s.\n"), ff_pkt->fname,
492 stop_thread_timer(tid);
498 stop_thread_timer(tid);
502 stat = send_data(bctx, bctx.data_stream);
504 if (ff_pkt->flags & FO_CHKCHANGES) {
505 has_file_changed(jcr, ff_pkt);
508 bclose(&ff_pkt->bfd);
515 #ifdef HAVE_DARWIN_OS
516 if (!send_resource_fork(bctx)) {
522 * Save ACLs and Extended Attributes when requested and available
523 * for anything not being a symlink.
526 if (jcr->bacl && jcr->bacl->backup_acl(jcr, ff_pkt) == bRC_BACL_error) {
531 if (jcr->bxattr && jcr->bxattr->backup_xattr(jcr, ff_pkt) == bRC_BXATTR_error) {
536 if (!crypto_terminate_digests(bctx)) {
544 if (jcr->is_incomplete() || jcr->is_canceled()) {
545 Dmsg0(100, "Job canceled by user or marked incomplete.\n");
548 if (plugin_started) {
549 send_plugin_name(jcr, sd, false); /* signal end of plugin data */
551 if (ff_pkt->opt_plugin) {
552 jcr->plugin_sp = NULL; /* sp is local to this function */
553 jcr->plugin_ctx = NULL;
555 jcr->opt_plugin = false;
562 * Send data read from an already open file descriptor.
564 * We return 1 on success and 0 on errors.
567 * We use ff_pkt->statp.st_size when FO_SPARSE to know when to stop
569 * Currently this is not a problem as the only other stream, resource forks,
570 * are not handled as sparse files.
572 static int send_data(bctx_t &bctx, int stream)
575 BSOCK *sd = jcr->store_bsock;
577 #ifdef FD_NO_SEND_TEST
581 bctx.rsize = jcr->buf_size;
583 bctx.cipher_ctx = NULL;
584 bctx.msgsave = sd->msg;
585 bctx.rbuf = sd->msg; /* read buffer */
586 bctx.wbuf = sd->msg; /* write buffer */
587 bctx.cipher_input = (uint8_t *)bctx.rbuf; /* encrypt uncompressed data */
589 Dmsg1(300, "Saving data, type=%d\n", bctx.ff_pkt->type);
591 if (!setup_compression(bctx)) {
595 if (bctx.ff_pkt->flags & FO_ENCRYPT && !crypto_allocate_ctx(bctx)) {
600 * Send Data header to Storage daemon
601 * <file-index> <stream> <expected stream length>
603 if (!sd->fsend("%ld %d %lld", jcr->JobFiles, stream,
604 (int64_t)bctx.ff_pkt->statp.st_size)) {
605 if (!jcr->is_job_canceled()) {
606 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
611 Dmsg1(300, ">stored: datahdr %s\n", sd->msg);
614 * Make space at beginning of buffer for fileAddr because this
615 * same buffer will be used for writing if compression is off.
617 if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
618 bctx.rbuf += OFFSET_FADDR_SIZE;
619 bctx.rsize -= OFFSET_FADDR_SIZE;
620 #if defined(HAVE_FREEBSD_OS) || defined(__FreeBSD_kernel__)
622 * To read FreeBSD partitions, the read size must be
625 bctx.rsize = (bctx.rsize/512) * 512;
629 /** a RAW device read on win32 only works if the buffer is a multiple of 512 */
631 if (S_ISBLK(bctx.ff_pkt->statp.st_mode)) {
632 bctx.rsize = (bctx.rsize/512) * 512;
634 Dmsg1(200, "Fattrs=0X%x\n", bctx.ff_pkt->bfd.fattrs);
635 if (bctx.ff_pkt->bfd.fattrs & FILE_ATTRIBUTE_ENCRYPTED) {
636 if (!p_ReadEncryptedFileRaw) {
637 Jmsg0(bctx.jcr, M_FATAL, 0, _("Windows Encrypted data not supported on this OS.\n"));
640 /* This single call reads all EFS data delivers it to a callback */
641 if (p_ReadEncryptedFileRaw((PFE_EXPORT_FUNC)read_efs_data_cb, &bctx,
642 bctx.ff_pkt->bfd.pvContext) != 0) {
645 /* All read, so skip to finish sending */
648 /* Fall through to standard bread() loop */
652 * Normal read the file data in a loop and send it to SD
654 while ((sd->msglen=(uint32_t)bread(&bctx.ff_pkt->bfd, bctx.rbuf, bctx.rsize)) > 0) {
655 if (!process_and_send_data(bctx)) {
658 } /* end while read file data */
662 if (sd->msglen < 0) { /* error */
664 Jmsg(jcr, M_ERROR, 0, _("Read error on file %s. ERR=%s\n"),
665 bctx.ff_pkt->fname, be.bstrerror(bctx.ff_pkt->bfd.berrno));
666 if (jcr->JobErrors++ > 1000) { /* insanity check */
667 Jmsg(jcr, M_FATAL, 0, _("Too many errors. JobErrors=%d.\n"), jcr->JobErrors);
669 } else if (bctx.ff_pkt->flags & FO_ENCRYPT) {
671 * For encryption, we must call finalize to push out any
674 if (!crypto_cipher_finalize(bctx.cipher_ctx, (uint8_t *)jcr->crypto.crypto_buf,
675 &bctx.encrypted_len)) {
676 /* Padding failed. Shouldn't happen. */
677 Jmsg(jcr, M_FATAL, 0, _("Encryption padding error\n"));
681 /** Note, on SSL pre-0.9.7, there is always some output */
682 if (bctx.encrypted_len > 0) {
683 sd->msglen = bctx.encrypted_len; /* set encrypted length */
684 sd->msg = jcr->crypto.crypto_buf; /* set correct write buffer */
686 if (!jcr->is_job_canceled()) {
687 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
692 Dmsg1(130, "Send data to SD len=%d\n", sd->msglen);
693 jcr->JobBytes += sd->msglen; /* count bytes saved possibly compressed/encrypted */
694 sd->msg = bctx.msgsave; /* restore bnet buffer */
699 if (!sd->signal(BNET_EOD)) { /* indicate end of file data */
700 if (!jcr->is_job_canceled()) {
701 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
707 /** Free the cipher context */
708 if (bctx.cipher_ctx) {
709 crypto_cipher_free(bctx.cipher_ctx);
714 /** Free the cipher context */
715 if (bctx.cipher_ctx) {
716 crypto_cipher_free(bctx.cipher_ctx);
719 sd->msg = bctx.msgsave; /* restore bnet buffer */
726 * Apply processing (sparse, compression, encryption, and
729 bool process_and_send_data(bctx_t &bctx)
734 /** Check for sparse blocks */
735 if (bctx.ff_pkt->flags & FO_SPARSE) {
737 bool allZeros = false;
738 if ((sd->msglen == bctx.rsize &&
739 bctx.fileAddr+sd->msglen < (uint64_t)bctx.ff_pkt->statp.st_size) ||
740 ((bctx.ff_pkt->type == FT_RAW || bctx.ff_pkt->type == FT_FIFO) &&
741 (uint64_t)bctx.ff_pkt->statp.st_size == 0)) {
742 allZeros = is_buf_zero(bctx.rbuf, bctx.rsize);
745 /** Put file address as first data in buffer */
746 ser_begin(bctx.wbuf, OFFSET_FADDR_SIZE);
747 ser_uint64(bctx.fileAddr); /* store fileAddr in begin of buffer */
749 bctx.fileAddr += sd->msglen; /* update file address */
750 /** Skip block of all zeros */
752 return true; /* skip block of zeros */
754 } else if (bctx.ff_pkt->flags & FO_OFFSETS) {
756 ser_begin(bctx.wbuf, OFFSET_FADDR_SIZE);
757 ser_uint64(bctx.ff_pkt->bfd.offset); /* store offset in begin of buffer */
760 jcr->ReadBytes += sd->msglen; /* count bytes read */
762 /** Uncompressed cipher input length */
763 bctx.cipher_input_len = sd->msglen;
765 /** Update checksum if requested */
767 crypto_digest_update(bctx.digest, (uint8_t *)bctx.rbuf, sd->msglen);
770 /** Update signing digest if requested */
771 if (bctx.signing_digest) {
772 crypto_digest_update(bctx.signing_digest, (uint8_t *)bctx.rbuf, sd->msglen);
775 if (have_libz && !do_libz_compression(bctx)) {
779 if (have_lzo && !do_lzo_compression(bctx)) {
784 * Note, here we prepend the current record length to the beginning
785 * of the encrypted data. This is because both sparse and compression
786 * restore handling want records returned to them with exactly the
787 * same number of bytes that were processed in the backup handling.
788 * That is, both are block filters rather than a stream. When doing
789 * compression, the compression routines may buffer data, so that for
790 * any one record compressed, when it is decompressed the same size
791 * will not be obtained. Of course, the buffered data eventually comes
792 * out in subsequent crypto_cipher_update() calls or at least
793 * when crypto_cipher_finalize() is called. Unfortunately, this
794 * "feature" of encryption enormously complicates the restore code.
796 if (bctx.ff_pkt->flags & FO_ENCRYPT) {
797 uint32_t initial_len = 0;
800 if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
801 bctx.cipher_input_len += OFFSET_FADDR_SIZE;
804 /** Encrypt the length of the input block */
805 uint8_t packet_len[sizeof(uint32_t)];
807 ser_begin(packet_len, sizeof(uint32_t));
808 ser_uint32(bctx.cipher_input_len); /* store data len in begin of buffer */
809 Dmsg1(20, "Encrypt len=%d\n", bctx.cipher_input_len);
811 if (!crypto_cipher_update(bctx.cipher_ctx, packet_len, sizeof(packet_len),
812 (uint8_t *)jcr->crypto.crypto_buf, &initial_len)) {
813 /** Encryption failed. Shouldn't happen. */
814 Jmsg(jcr, M_FATAL, 0, _("Encryption error\n"));
818 /** Encrypt the input block */
819 if (crypto_cipher_update(bctx.cipher_ctx, bctx.cipher_input, bctx.cipher_input_len,
820 (uint8_t *)&jcr->crypto.crypto_buf[initial_len], &bctx.encrypted_len)) {
821 if ((initial_len + bctx.encrypted_len) == 0) {
822 /** No full block of data available, read more data */
825 Dmsg2(400, "encrypted len=%d unencrypted len=%d\n", bctx.encrypted_len,
827 sd->msglen = initial_len + bctx.encrypted_len; /* set encrypted length */
829 /** Encryption failed. Shouldn't happen. */
830 Jmsg(jcr, M_FATAL, 0, _("Encryption error\n"));
835 /* Send the buffer to the Storage daemon */
836 if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
837 sd->msglen += OFFSET_FADDR_SIZE; /* include fileAddr in size */
839 sd->msg = bctx.wbuf; /* set correct write buffer */
841 if (!jcr->is_job_canceled()) {
842 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
847 Dmsg1(130, "Send data to SD len=%d\n", sd->msglen);
849 jcr->JobBytes += sd->msglen; /* count bytes saved possibly compressed/encrypted */
850 sd->msg = bctx.msgsave; /* restore read buffer */
857 bool encode_and_send_attributes(bctx_t &bctx)
859 BSOCK *sd = bctx.jcr->store_bsock;
861 FF_PKT *ff_pkt = bctx.ff_pkt;
862 char attribs[MAXSTRING];
863 char attribsExBuf[MAXSTRING];
864 char *attribsEx = NULL;
868 int hangup = get_hangup();
869 int blowup = get_blowup();
870 #ifdef FD_NO_SEND_TEST
874 Dmsg1(300, "encode_and_send_attrs fname=%s\n", ff_pkt->fname);
875 /** Find what data stream we will use, then encode the attributes */
876 if ((bctx.data_stream = select_data_stream(ff_pkt)) == STREAM_NONE) {
877 /* This should not happen */
878 Jmsg0(jcr, M_FATAL, 0, _("Invalid file flags, no supported data stream type.\n"));
881 encode_stat(attribs, &ff_pkt->statp, sizeof(ff_pkt->statp), ff_pkt->LinkFI, bctx.data_stream);
883 /** Now possibly extend the attributes */
884 if (IS_FT_OBJECT(ff_pkt->type)) {
885 attr_stream = STREAM_RESTORE_OBJECT;
887 attribsEx = attribsExBuf;
888 attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
891 Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx);
894 jcr->JobFiles++; /* increment number of files sent */
895 ff_pkt->FileIndex = jcr->JobFiles; /* return FileIndex */
896 pm_strcpy(jcr->last_fname, ff_pkt->fname);
899 /* Display the information about the current file if requested */
900 if (is_message_type_set(jcr, M_SAVED)) {
902 memcpy(&attr.statp, &ff_pkt->statp, sizeof(struct stat));
903 attr.type = ff_pkt->type;
904 attr.ofname = (POOLMEM *)ff_pkt->fname;
905 attr.olname = (POOLMEM *)ff_pkt->link;
906 print_ls_output(jcr, &attr, M_SAVED);
909 /* Debug code: check if we must hangup */
910 if (hangup > 0 && (jcr->JobFiles > (uint32_t)hangup)) {
911 jcr->setJobStatus(JS_Incomplete);
912 Jmsg1(jcr, M_FATAL, 0, "Debug hangup requested after %d files.\n", hangup);
917 if (blowup > 0 && (jcr->JobFiles > (uint32_t)blowup)) {
918 Jmsg1(jcr, M_ABORT, 0, "Debug blowup requested after %d files.\n", blowup);
923 * Send Attributes header to Storage daemon
924 * <file-index> <stream> <info>
926 if (!sd->fsend("%ld %d 0", jcr->JobFiles, attr_stream)) {
927 if (!jcr->is_canceled() && !jcr->is_incomplete()) {
928 Jmsg2(jcr, M_FATAL, 0, _("Network send error to SD. Data=%s ERR=%s\n"),
929 sd->msg, sd->bstrerror());
933 Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
936 * Send file attributes to Storage daemon
939 * Filename (full path)
941 * Link name (if type==FT_LNK or FT_LNKSAVED)
942 * Encoded extended-attributes (for Win32)
944 * or send Restore Object to Storage daemon
948 * Object_len (possibly compressed)
949 * Object_full_len (not compressed)
955 * For a directory, link is the same as fname, but with trailing
956 * slash. For a linked file, link is the link.
958 if (!IS_FT_OBJECT(ff_pkt->type) && ff_pkt->type != FT_DELETED) { /* already stripped */
961 switch (ff_pkt->type) {
964 Dmsg3(300, "Link %d %s to %s\n", jcr->JobFiles, ff_pkt->fname, ff_pkt->link);
965 stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c%u%c", jcr->JobFiles,
966 ff_pkt->type, ff_pkt->fname, 0, attribs, 0,
967 ff_pkt->link, 0, attribsEx, 0, ff_pkt->delta_seq, 0);
972 /* Here link is the canonical filename (i.e. with trailing slash) */
973 stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles,
974 ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0,
975 attribsEx, 0, ff_pkt->delta_seq, 0);
977 case FT_PLUGIN_CONFIG:
978 case FT_RESTORE_FIRST:
979 comp_len = ff_pkt->object_len;
980 ff_pkt->object_compression = 0;
981 if (ff_pkt->object_len > 1000) {
982 /* Big object, compress it */
983 comp_len = ff_pkt->object_len + 1000;
984 POOLMEM *comp_obj = get_memory(comp_len);
985 /* *** FIXME *** check Zdeflate error */
986 Zdeflate(ff_pkt->object, ff_pkt->object_len, comp_obj, comp_len);
987 if (comp_len < ff_pkt->object_len) {
988 ff_pkt->object = comp_obj;
989 ff_pkt->object_compression = 1; /* zlib level 9 compression */
991 /* Uncompressed object smaller, use it */
992 comp_len = ff_pkt->object_len;
994 Dmsg2(100, "Object compressed from %d to %d bytes\n", ff_pkt->object_len, comp_len);
996 sd->msglen = Mmsg(sd->msg, "%d %d %d %d %d %d %s%c%s%c",
997 jcr->JobFiles, ff_pkt->type, ff_pkt->object_index,
998 comp_len, ff_pkt->object_len, ff_pkt->object_compression,
999 ff_pkt->fname, 0, ff_pkt->object_name, 0);
1000 sd->msg = check_pool_memory_size(sd->msg, sd->msglen + comp_len + 2);
1001 memcpy(sd->msg + sd->msglen, ff_pkt->object, comp_len);
1002 /* Note we send one extra byte so Dir can store zero after object */
1003 sd->msglen += comp_len + 1;
1005 if (ff_pkt->object_compression) {
1006 free_and_null_pool_memory(ff_pkt->object);
1010 stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%d%c", jcr->JobFiles,
1011 ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0,
1012 ff_pkt->delta_seq, 0);
1015 stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles,
1016 ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0,
1017 attribsEx, 0, ff_pkt->delta_seq, 0);
1021 if (!IS_FT_OBJECT(ff_pkt->type) && ff_pkt->type != FT_DELETED) {
1022 unstrip_path(ff_pkt);
1025 Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
1026 if (!stat && !jcr->is_job_canceled()) {
1027 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
1030 sd->signal(BNET_EOD); /* indicate end of attributes data */
1035 * Setup bctx for doing compression
1037 static bool setup_compression(bctx_t &bctx)
1039 JCR *jcr = bctx.jcr;
1041 #if defined(HAVE_LIBZ) || defined(HAVE_LZO)
1042 bctx.compress_len = 0;
1043 bctx.max_compress_len = 0;
1048 if ((bctx.ff_pkt->flags & FO_COMPRESS) && bctx.ff_pkt->Compress_algo == COMPRESS_GZIP) {
1049 if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
1050 bctx.cbuf = (unsigned char *)jcr->compress_buf + OFFSET_FADDR_SIZE;
1051 bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE;
1053 bctx.cbuf = (unsigned char *)jcr->compress_buf;
1054 bctx.max_compress_len = jcr->compress_buf_size; /* set max length */
1056 bctx.wbuf = jcr->compress_buf; /* compressed output here */
1057 bctx.cipher_input = (uint8_t *)jcr->compress_buf; /* encrypt compressed data */
1060 * Only change zlib parameters if there is no pending operation.
1061 * This should never happen as deflatereset is called after each
1065 if (((z_stream*)jcr->pZLIB_compress_workset)->total_in == 0) {
1066 /** set gzip compression level - must be done per file */
1067 if ((zstat=deflateParams((z_stream*)jcr->pZLIB_compress_workset,
1068 bctx.ff_pkt->Compress_level, Z_DEFAULT_STRATEGY)) != Z_OK) {
1069 Jmsg(jcr, M_FATAL, 0, _("Compression deflateParams error: %d\n"), zstat);
1070 jcr->setJobStatus(JS_ErrorTerminated);
1077 memset(&bctx.ch, 0, sizeof(comp_stream_header));
1080 if ((bctx.ff_pkt->flags & FO_COMPRESS) && bctx.ff_pkt->Compress_algo == COMPRESS_LZO1X) {
1081 if ((bctx.ff_pkt->flags & FO_SPARSE) || (bctx.ff_pkt->flags & FO_OFFSETS)) {
1082 bctx.cbuf = (unsigned char *)jcr->compress_buf + OFFSET_FADDR_SIZE;
1083 bctx.cbuf2 = (unsigned char *)jcr->compress_buf + OFFSET_FADDR_SIZE + sizeof(comp_stream_header);
1084 bctx.max_compress_len = jcr->compress_buf_size - OFFSET_FADDR_SIZE;
1086 bctx.cbuf = (unsigned char *)jcr->compress_buf;
1087 bctx.cbuf2 = (unsigned char *)jcr->compress_buf + sizeof(comp_stream_header);
1088 bctx.max_compress_len = jcr->compress_buf_size; /* set max length */
1090 bctx.ch.magic = COMPRESS_LZO1X;
1091 bctx.ch.version = COMP_HEAD_VERSION;
1092 bctx.wbuf = jcr->compress_buf; /* compressed output here */
1093 bctx.cipher_input = (uint8_t *)jcr->compress_buf; /* encrypt compressed data */
1101 * Send MacOS resource fork to SD
1103 #ifdef HAVE_DARWIN_OS
1104 static bool send_resource_fork(bctx_t &bctx)
1106 FF_PKT *ff_pkt = bctx.ff_pkt;
1107 JCR *jcr = bctx.jcr;
1108 BSOCK *sd = bctx.sd;
1111 /** Regular files can have resource forks and Finder Info */
1112 if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) &&
1113 ff_pkt->flags & FO_HFSPLUS)) {
1114 if (ff_pkt->hfsinfo.rsrclength > 0) {
1117 if (bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
1118 ff_pkt->ff_errno = errno;
1120 Jmsg(jcr, M_NOTSAVED, -1, _(" Cannot open resource fork for \"%s\": ERR=%s.\n"),
1121 ff_pkt->fname, be.bstrerror());
1123 if (is_bopen(&ff_pkt->bfd)) {
1124 bclose(&ff_pkt->bfd);
1128 flags = ff_pkt->flags;
1129 ff_pkt->flags &= ~(FO_COMPRESS|FO_SPARSE|FO_OFFSETS);
1130 if (flags & FO_ENCRYPT) {
1131 rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA;
1133 rsrc_stream = STREAM_MACOS_FORK_DATA;
1135 stat = send_data(bctx, rsrc_stream);
1136 ff_pkt->flags = flags;
1137 bclose(&ff_pkt->bfd);
1143 Dmsg1(300, "Saving Finder Info for \"%s\"\n", ff_pkt->fname);
1144 sd->fsend("%ld %d 0", jcr->JobFiles, STREAM_HFSPLUS_ATTRIBUTES);
1145 Dmsg1(300, "bfiled>stored:header %s\n", sd->msg);
1146 pm_memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32);
1149 crypto_digest_update(bctx.digest, (uint8_t *)sd->msg, sd->msglen);
1151 if (bctx.signing_digest) {
1152 crypto_digest_update(bctx.signing_digest, (uint8_t *)sd->msg, sd->msglen);
1155 sd->signal(BNET_EOD);
1161 static bool do_libz_compression(bctx_t &bctx)
1164 JCR *jcr = bctx.jcr;
1165 BSOCK *sd = bctx.sd;
1168 /** Do compression if turned on */
1169 if (bctx.ff_pkt->flags & FO_COMPRESS && bctx.ff_pkt->Compress_algo == COMPRESS_GZIP && jcr->pZLIB_compress_workset) {
1170 Dmsg3(400, "cbuf=0x%x rbuf=0x%x len=%u\n", bctx.cbuf, bctx.rbuf, sd->msglen);
1172 ((z_stream*)jcr->pZLIB_compress_workset)->next_in = (unsigned char *)bctx.rbuf;
1173 ((z_stream*)jcr->pZLIB_compress_workset)->avail_in = sd->msglen;
1174 ((z_stream*)jcr->pZLIB_compress_workset)->next_out = bctx.cbuf;
1175 ((z_stream*)jcr->pZLIB_compress_workset)->avail_out = bctx.max_compress_len;
1177 if ((zstat=deflate((z_stream*)jcr->pZLIB_compress_workset, Z_FINISH)) != Z_STREAM_END) {
1178 Jmsg(jcr, M_FATAL, 0, _("Compression deflate error: %d\n"), zstat);
1179 jcr->setJobStatus(JS_ErrorTerminated);
1182 bctx.compress_len = ((z_stream*)jcr->pZLIB_compress_workset)->total_out;
1183 /** reset zlib stream to be able to begin from scratch again */
1184 if ((zstat=deflateReset((z_stream*)jcr->pZLIB_compress_workset)) != Z_OK) {
1185 Jmsg(jcr, M_FATAL, 0, _("Compression deflateReset error: %d\n"), zstat);
1186 jcr->setJobStatus(JS_ErrorTerminated);
1190 Dmsg2(400, "GZIP compressed len=%d uncompressed len=%d\n", bctx.compress_len,
1193 sd->msglen = bctx.compress_len; /* set compressed length */
1194 bctx.cipher_input_len = bctx.compress_len;
1200 static bool do_lzo_compression(bctx_t &bctx)
1203 JCR *jcr = bctx.jcr;
1204 BSOCK *sd = bctx.sd;
1207 /** Do compression if turned on */
1208 if (bctx.ff_pkt->flags & FO_COMPRESS && bctx.ff_pkt->Compress_algo == COMPRESS_LZO1X && jcr->LZO_compress_workset) {
1209 lzo_uint len; /* TODO: See with the latest patch how to handle lzo_uint with 64bit */
1212 ser_begin(bctx.cbuf, sizeof(comp_stream_header));
1214 Dmsg3(400, "cbuf=0x%x rbuf=0x%x len=%u\n", bctx.cbuf, bctx.rbuf, sd->msglen);
1216 lzores = lzo1x_1_compress((const unsigned char*)bctx.rbuf, sd->msglen, bctx.cbuf2,
1217 &len, jcr->LZO_compress_workset);
1218 bctx.compress_len = len;
1219 if (lzores == LZO_E_OK && bctx.compress_len <= bctx.max_compress_len) {
1220 /* complete header */
1221 ser_uint32(COMPRESS_LZO1X);
1222 ser_uint32(bctx.compress_len);
1223 ser_uint16(bctx.ch.level);
1224 ser_uint16(bctx.ch.version);
1226 /** this should NEVER happen */
1227 Jmsg(jcr, M_FATAL, 0, _("Compression LZO error: %d\n"), lzores);
1228 jcr->setJobStatus(JS_ErrorTerminated);
1232 Dmsg2(400, "LZO compressed len=%d uncompressed len=%d\n", bctx.compress_len,
1235 bctx.compress_len += sizeof(comp_stream_header); /* add size of header */
1236 sd->msglen = bctx.compress_len; /* set compressed length */
1237 bctx.cipher_input_len = bctx.compress_len;
1244 * Do in place strip of path
1246 static bool do_snap_strip(FF_PKT *ff)
1248 /* if the string starts with the snapshot path name, we can replace
1249 * by the volume name. The volume_path is smaller than the snapshot_path
1250 * snapshot_path = volume_path + /.snapshots/job-xxxx
1252 ASSERT(strlen(ff->snapshot_path) > strlen(ff->volume_path));
1253 int sp_first = strlen(ff->snapshot_path); /* point after snapshot_path in fname */
1254 if (strncmp(ff->fname, ff->snapshot_path, sp_first) == 0) {
1255 int last = pm_strcpy(ff->snap_fname, ff->volume_path);
1256 last = MAX(last - 1, 0);
1258 if (ff->snap_fname[last] == '/') {
1259 if (ff->fname[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */
1260 ff->snap_fname[last] = 0;
1263 if (ff->fname[sp_first] != '/') {
1264 pm_strcat(ff->snap_fname, "/");
1268 pm_strcat(ff->snap_fname, ff->fname + sp_first);
1269 ASSERT(strlen(ff->fname) > strlen(ff->snap_fname));
1270 strcpy(ff->fname, ff->snap_fname);
1271 Dmsg2(DT_SNAPSHOT|20, "%s -> %s\n", ff->fname_save, ff->fname);
1273 if (strncmp(ff->link, ff->snapshot_path, sp_first) == 0) {
1274 int last = pm_strcpy(ff->snap_fname, ff->volume_path);
1275 last = MAX(last - 1, 0);
1277 if (ff->snap_fname[last] == '/') {
1278 if (ff->link[sp_first] == '/') { /* compare with the first character of the string (sp_first not sp_first-1) */
1279 ff->snap_fname[last] = 0;
1282 if (ff->link[sp_first] != '/') {
1283 pm_strcat(ff->snap_fname, "/");
1287 pm_strcat(ff->snap_fname, ff->link + sp_first);
1288 ASSERT(strlen(ff->link) > strlen(ff->snap_fname));
1289 strcpy(ff->link, ff->snap_fname);
1290 Dmsg2(DT_SNAPSHOT|20, "%s -> %s\n", ff->link_save, ff->link);
1297 * Do in place strip of path
1299 static bool do_strip(int count, char *in)
1305 /** Copy to first path separator -- Win32 might have c: ... */
1306 while (*in && !IsPathSeparator(*in)) {
1309 if (*in) { /* Not at the end of the string */
1311 numsep++; /* one separator seen */
1313 for (stripped=0; stripped<count && *in; stripped++) {
1314 while (*in && !IsPathSeparator(*in)) {
1315 in++; /* skip chars */
1318 numsep++; /* count separators seen */
1319 in++; /* skip separator */
1323 while (*in) { /* copy to end */
1324 if (IsPathSeparator(*in)) {
1330 Dmsg4(500, "stripped=%d count=%d numsep=%d sep>count=%d\n",
1331 stripped, count, numsep, numsep>count);
1332 return stripped==count && numsep>count;
1336 * If requested strip leading components of the path so that we can
1337 * save file as if it came from a subdirectory. This is most useful
1338 * for dealing with snapshots, by removing the snapshot directory, or
1339 * in handling vendor migrations where files have been restored with
1340 * a vendor product into a subdirectory.
1342 * When we are using snapshots, we might need to convert the path
1343 * back to the original one using the strip_snap_path option.
1345 void strip_path(FF_PKT *ff_pkt)
1347 if (!ff_pkt->strip_snap_path &&
1348 (!(ff_pkt->flags & FO_STRIPPATH) || ff_pkt->strip_path <= 0))
1350 Dmsg1(200, "No strip for %s\n", ff_pkt->fname);
1353 /* shared part between strip and snapshot */
1354 if (!ff_pkt->fname_save) {
1355 ff_pkt->fname_save = get_pool_memory(PM_FNAME);
1356 ff_pkt->link_save = get_pool_memory(PM_FNAME);
1357 *ff_pkt->link_save = 0;
1359 pm_strcpy(ff_pkt->fname_save, ff_pkt->fname);
1360 if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) {
1361 pm_strcpy(ff_pkt->link_save, ff_pkt->link);
1362 Dmsg2(500, "strcpy link_save=%d link=%d\n", strlen(ff_pkt->link_save),
1363 strlen(ff_pkt->link));
1367 if (ff_pkt->strip_snap_path) {
1368 if (!do_snap_strip(ff_pkt)) {
1369 Dmsg1(0, "Something wrong with do_snap_strip(%s)\n", ff_pkt->fname);
1370 unstrip_path(ff_pkt);
1375 /* See if we want also to strip the path */
1376 if (!(ff_pkt->flags & FO_STRIPPATH) || ff_pkt->strip_path <= 0) {
1381 * Strip path. If it doesn't succeed put it back. If
1382 * it does, and there is a different link string,
1383 * attempt to strip the link. If it fails, back them
1385 * Do not strip symlinks.
1386 * I.e. if either stripping fails don't strip anything.
1388 if (!do_strip(ff_pkt->strip_path, ff_pkt->fname)) {
1389 unstrip_path(ff_pkt);
1392 /** Strip links but not symlinks */
1393 if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) {
1394 if (!do_strip(ff_pkt->strip_path, ff_pkt->link)) {
1395 unstrip_path(ff_pkt);
1400 Dmsg3(10, "fname=%s stripped=%s link=%s\n", ff_pkt->fname_save, ff_pkt->fname,
1404 void unstrip_path(FF_PKT *ff_pkt)
1406 if (!ff_pkt->strip_snap_path &&
1407 (!(ff_pkt->flags & FO_STRIPPATH) || ff_pkt->strip_path <= 0))
1412 strcpy(ff_pkt->fname, ff_pkt->fname_save);
1413 if (ff_pkt->type != FT_LNK && ff_pkt->fname != ff_pkt->link) {
1414 Dmsg2(10, "strcpy link=%s link_save=%s\n", ff_pkt->link,
1416 strcpy(ff_pkt->link, ff_pkt->link_save);
1417 Dmsg2(10, "strcpy link=%d link_save=%d\n", strlen(ff_pkt->link),
1418 strlen(ff_pkt->link_save));
1423 static void close_vss_backup_session(JCR *jcr)
1425 #if defined(WIN32_VSS)
1426 /* STOP VSS ON WIN32 */
1427 /* tell vss to close the backup session */
1428 if (jcr->Snapshot && jcr->pVSSClient) {
1429 if (jcr->pVSSClient->CloseBackup()) {
1430 /* inform user about writer states */
1431 for (int i=0; i<(int)jcr->pVSSClient->GetWriterCount(); i++) {
1432 int msg_type = M_INFO;
1433 if (jcr->pVSSClient->GetWriterState(i) < 1) {
1434 msg_type = M_WARNING;
1437 Jmsg(jcr, msg_type, 0, _("VSS Writer (BackupComplete): %s\n"),
1438 jcr->pVSSClient->GetWriterInfo(i));
1441 /* Generate Job global writer metadata */
1442 WCHAR *metadata = jcr->pVSSClient->GetMetadata();
1444 FF_PKT *ff_pkt = jcr->ff;
1445 ff_pkt->fname = (char *)"*all*"; /* for all plugins */
1446 ff_pkt->type = FT_RESTORE_FIRST;
1448 ff_pkt->object_name = (char *)"job_metadata.xml";
1449 ff_pkt->object = (char *)metadata;
1450 ff_pkt->object_len = (wcslen(metadata) + 1) * sizeof(WCHAR);
1451 ff_pkt->object_index = (int)time(NULL);
1452 save_file(jcr, ff_pkt, true);