]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/restore.c
4ca022dee0d1fe1d60c0dc690c0758dafbd89a77
[bacula/bacula] / bacula / src / filed / restore.c
1 /*
2  *  Bacula File Daemon  restore.c Restorefiles.
3  *
4  *    Kern Sibbald, November MM
5  *
6  *   Version $Id$
7  *
8  */
9 /*
10    Copyright (C) 2000-2005 Kern Sibbald
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License
14    version 2 as amended with additional clauses defined in the
15    file LICENSE in the main source directory.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
20    the file LICENSE for additional details.
21
22  */
23
24 #include "bacula.h"
25 #include "filed.h"
26
27 #ifdef HAVE_DARWIN_OS
28 #include <sys/attr.h>
29 #endif
30
31 /* Data received from Storage Daemon */
32 static char rec_header[] = "rechdr %ld %ld %ld %ld %ld";
33
34 /* Forward referenced functions */
35 #ifdef HAVE_LIBZ
36 static const char *zlib_strerror(int stat);
37 #endif
38
39 int verify_signature(JCR *jcr, SIGNATURE *sig);
40 int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen,
41       uint64_t *addr, int flags);
42
43 #define RETRY 10                      /* retry wait time */
44
45 /*
46  * Close a bfd check that we are at the expected file offset.
47  * Makes some code in set_attributes().
48  */
49 int bclose_chksize(JCR *jcr, BFILE *bfd, off_t osize)
50 {
51    char ec1[50], ec2[50];
52    off_t fsize;
53
54    fsize = blseek(bfd, 0, SEEK_CUR);
55    bclose(bfd);                              /* first close file */
56    if (fsize > 0 && fsize != osize) {
57       Qmsg3(jcr, M_ERROR, 0, _("Size of data or stream of %s not correct. Original %s, restored %s.\n"),
58             jcr->last_fname, edit_uint64(osize, ec1),
59             edit_uint64(fsize, ec2));
60       return -1;
61    }
62    return 0;
63 }
64
65 /*
66  * Restore the requested files.
67  *
68  */
69 void do_restore(JCR *jcr)
70 {
71    BSOCK *sd;
72    int32_t stream = 0;
73    int32_t prev_stream;
74    uint32_t VolSessionId, VolSessionTime;
75    bool extract = false;
76    int32_t file_index;
77    char ec1[50];                      /* Buffer printing huge values */
78
79    BFILE bfd;                         /* File content */
80    uint64_t fileAddr = 0;             /* file write address */
81    uint32_t size;                     /* Size of file */
82    BFILE altbfd;                      /* Alternative data stream */
83    uint64_t alt_addr = 0;             /* Write address for alternative stream */
84    intmax_t alt_size = 0;             /* Size of alternate stream */
85    SIGNATURE *sig = NULL;             /* Cryptographic signature (if any) for file */
86    int flags;                         /* Options for extract_data() */
87    int stat;
88    ATTR *attr;
89
90    /* The following variables keep track of "known unknowns" */
91    int non_support_data = 0;
92    int non_support_attr = 0;
93    int non_support_rsrc = 0;
94    int non_support_finfo = 0;
95    int non_support_acl = 0;
96    int non_support_progname = 0;
97
98    /* Finally, set up for special configurations */
99 #ifdef HAVE_DARWIN_OS
100    intmax_t rsrc_len = 0;             /* Original length of resource fork */
101    struct attrlist attrList;
102
103    memset(&attrList, 0, sizeof(attrList));
104    attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
105    attrList.commonattr = ATTR_CMN_FNDRINFO;
106 #endif
107
108    sd = jcr->store_bsock;
109    set_jcr_job_status(jcr, JS_Running);
110
111    LockRes();
112    CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
113    UnlockRes();
114    uint32_t buf_size;
115    if (client) {
116       buf_size = client->max_network_buffer_size;
117    } else {
118       buf_size = 0;                   /* use default */
119    }
120    if (!bnet_set_buffer_size(sd, buf_size, BNET_SETBUF_WRITE)) {
121       set_jcr_job_status(jcr, JS_ErrorTerminated);
122       return;
123    }
124    jcr->buf_size = sd->msglen;
125
126 #ifdef HAVE_LIBZ
127    uint32_t compress_buf_size = jcr->buf_size + 12 + ((jcr->buf_size+999) / 1000) + 100;
128    jcr->compress_buf = (char *)bmalloc(compress_buf_size);
129    jcr->compress_buf_size = compress_buf_size;
130 #endif
131
132    /*
133     * Get a record from the Storage daemon. We are guaranteed to
134     *   receive records in the following order:
135     *   1. Stream record header
136     *   2. Stream data
137     *        a. Attributes (Unix or Win32)
138     *    or  b. File data for the file
139     *    or  c. Alternate data stream (e.g. Resource Fork)
140     *    or  d. Finder info
141     *    or  e. ACLs
142     *    or  f. Possibly a cryptographic signature
143     *    or  g. Possibly MD5 or SHA1 record
144     *   3. Repeat step 1
145     *
146     * NOTE: We keep track of two bacula file descriptors:
147     *   1. bfd for file data.
148     *      This fd is opened for non empty files when an attribute stream is
149     *      encountered and closed when we find the next attribute stream.
150     *   2. alt_bfd for alternate data streams
151     *      This fd is opened every time we encounter a new alternate data
152     *      stream for the current file. When we find any other stream, we
153     *      close it again.
154     *      The expected size of the stream, alt_len, should be set when
155     *      opening the fd.
156     */
157    binit(&bfd);
158    binit(&altbfd);
159    attr = new_attr();
160    jcr->acl_text = get_pool_memory(PM_MESSAGE);
161
162    while (bget_msg(sd) >= 0 && !job_canceled(jcr)) {
163       /* Remember previous stream type */
164       prev_stream = stream;
165
166       /* First we expect a Stream Record Header */
167       if (sscanf(sd->msg, rec_header, &VolSessionId, &VolSessionTime, &file_index,
168           &stream, &size) != 5) {
169          Jmsg1(jcr, M_FATAL, 0, _("Record header scan error: %s\n"), sd->msg);
170          goto bail_out;
171       }
172       Dmsg2(30, "Got hdr: FilInx=%d Stream=%d.\n", file_index, stream);
173
174       /* * Now we expect the Stream Data */
175       if (bget_msg(sd) < 0) {
176          Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), bnet_strerror(sd));
177          goto bail_out;
178       }
179       if (size != (uint32_t)sd->msglen) {
180          Jmsg2(jcr, M_FATAL, 0, _("Actual data size %d not same as header %d\n"), sd->msglen, size);
181          goto bail_out;
182       }
183       Dmsg1(30, "Got stream data, len=%d\n", sd->msglen);
184
185       /* If we change streams, close and reset alternate data streams */
186       if (prev_stream != stream) {
187          if (is_bopen(&altbfd)) {
188             bclose_chksize(jcr, &altbfd, alt_size);
189          }
190          alt_size = -1; /* Use an impossible value and set a proper one below */
191          alt_addr = 0;
192       }
193
194       /* File Attributes stream */
195       switch (stream) {
196       case STREAM_UNIX_ATTRIBUTES:
197       case STREAM_UNIX_ATTRIBUTES_EX:
198          Dmsg1(30, "Stream=Unix Attributes. extract=%d\n", extract);
199          /*
200           * If extracting, it was from previous stream, so
201           * close the output file and validate the signature.
202           */
203          if (extract) {
204             if (size > 0 && !is_bopen(&bfd)) {
205                Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should be open\n"));
206             }
207             set_attributes(jcr, attr, &bfd);
208             extract = false;
209
210             /* Verify the cryptographic signature, if any */
211             if (jcr->pki_sign) {
212                if (sig) {
213                   if (!verify_signature(jcr, sig)) {
214                      // TODO landonf: Better signature failure handling.
215                      // The failure is reported to the director in verify_signature() ...
216                      Dmsg1(100, "Bad signature on %s\n", jcr->last_fname);
217                   } else {
218                      Dmsg1(100, "Signature good on %s\n", jcr->last_fname);
219                   }
220                   crypto_sign_free(sig);
221                   sig = NULL;
222                } else {
223                   Jmsg1(jcr, M_ERROR, 0, _("Missing cryptographic signature for %s\n"), jcr->last_fname);
224                }
225             }
226             Dmsg0(30, "Stop extracting.\n");
227          } else if (is_bopen(&bfd)) {
228             Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should not be open\n"));
229             bclose(&bfd);
230          }
231
232          /*
233           * Unpack and do sanity check fo attributes.
234           */
235          if (!unpack_attributes_record(jcr, stream, sd->msg, attr)) {
236             goto bail_out;
237          }
238          if (file_index != attr->file_index) {
239             Jmsg(jcr, M_FATAL, 0, _("Record header file index %ld not equal record index %ld\n"),
240                  file_index, attr->file_index);
241             Dmsg0(100, "File index error\n");
242             goto bail_out;
243          }
244
245          Dmsg3(200, "File %s\nattrib=%s\nattribsEx=%s\n", attr->fname,
246                attr->attr, attr->attrEx);
247
248          attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
249
250          if (!is_restore_stream_supported(attr->data_stream)) {
251             if (!non_support_data++) {
252                Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
253                   stream_to_ascii(attr->data_stream));
254             }
255             continue;
256          }
257
258          build_attr_output_fnames(jcr, attr);
259
260          /*
261           * Now determine if we are extracting or not.
262           */
263          jcr->num_files_examined++;
264          Dmsg1(30, "Outfile=%s\n", attr->ofname);
265          extract = false;
266          stat = create_file(jcr, attr, &bfd, jcr->replace);
267          switch (stat) {
268          case CF_ERROR:
269          case CF_SKIP:
270             break;
271          case CF_EXTRACT:        /* File created and we expect file data */
272             extract = true;
273             /* FALLTHROUGH */
274          case CF_CREATED:        /* File created, but there is no content */
275             P(jcr->mutex);
276             pm_strcpy(jcr->last_fname, attr->ofname);
277             V(jcr->mutex);
278             jcr->JobFiles++;
279             fileAddr = 0;
280             print_ls_output(jcr, attr);
281 #ifdef HAVE_DARWIN_OS
282             /* Only restore the resource fork for regular files */
283             from_base64(&rsrc_len, attr->attrEx);
284             if (attr->type == FT_REG && rsrc_len > 0) {
285                extract = true;
286             }
287 #endif
288             if (!extract) {
289                /* set attributes now because file will not be extracted */
290                set_attributes(jcr, attr, &bfd);
291             }
292             break;
293          }
294          break;
295
296       /* Data stream */
297       case STREAM_FILE_DATA:
298       case STREAM_SPARSE_DATA:
299       case STREAM_WIN32_DATA:
300       case STREAM_GZIP_DATA:
301       case STREAM_SPARSE_GZIP_DATA:
302       case STREAM_WIN32_GZIP_DATA:
303          /* Force an expected, consistent stream type here */
304          if (extract && (prev_stream == stream || prev_stream == STREAM_UNIX_ATTRIBUTES
305                   || prev_stream == STREAM_UNIX_ATTRIBUTES_EX)) {
306             flags = 0;
307             if (stream == STREAM_SPARSE_DATA || stream == STREAM_SPARSE_GZIP_DATA) {
308                flags |= FO_SPARSE;
309             }
310             if (stream == STREAM_GZIP_DATA || stream == STREAM_SPARSE_GZIP_DATA
311                   || stream == STREAM_WIN32_GZIP_DATA) {
312                flags |= FO_GZIP;
313             }
314
315             if (is_win32_stream(stream) && !have_win32_api()) {
316                set_portable_backup(&bfd);
317                flags |= FO_WIN32DECOMP;    /* "decompose" BackupWrite data */
318             }
319
320             if (extract_data(jcr, &bfd, sd->msg, sd->msglen, &fileAddr, flags) < 0) {
321                extract = false;
322                bclose(&bfd);
323                continue;
324             }
325          }
326          break;
327
328       /* Resource fork stream - only recorded after a file to be restored */
329       /* Silently ignore if we cannot write - we already reported that */
330       case STREAM_MACOS_FORK_DATA:
331 #ifdef HAVE_DARWIN_OS
332          if (extract) {
333             if (prev_stream != stream) {
334                if (bopen_rsrc(&altbfd, jcr->last_fname, O_WRONLY | O_TRUNC | O_BINARY, 0) < 0) {
335                   Jmsg(jcr, M_ERROR, 0, _("     Cannot open resource fork for %s.\n"), jcr->last_fname);
336                   extract = false;
337                   continue;
338                }
339                alt_size = rsrc_len;
340                Dmsg0(30, "Restoring resource fork\n");
341             }
342             flags = 0;
343             if (extract_data(jcr, &altbfd, sd->msg, sd->msglen, &alt_addr, flags) < 0) {
344                extract = false;
345                bclose(&altbfd);
346                continue;
347             }
348          }
349 #else
350          non_support_rsrc++;
351 #endif
352          break;
353
354       case STREAM_HFSPLUS_ATTRIBUTES:
355 #ifdef HAVE_DARWIN_OS
356          Dmsg0(30, "Restoring Finder Info\n");
357          if (sd->msglen != 32) {
358             Jmsg(jcr, M_ERROR, 0, _("     Invalid length of Finder Info (got %d, not 32)\n"), sd->msglen);
359             continue;
360          }
361          if (setattrlist(jcr->last_fname, &attrList, sd->msg, sd->msglen, 0) != 0) {
362             Jmsg(jcr, M_ERROR, 0, _("     Could not set Finder Info on %s\n"), jcr->last_fname);
363             continue;
364          }
365 #else
366          non_support_finfo++;
367 #endif
368
369       case STREAM_UNIX_ATTRIBUTES_ACCESS_ACL:
370 #ifdef HAVE_ACL
371          pm_strcpy(jcr->acl_text, sd->msg);
372          Dmsg2(400, "Restoring ACL type 0x%2x <%s>\n", BACL_TYPE_ACCESS, jcr->acl_text);
373          if (bacl_set(jcr, BACL_TYPE_ACCESS) != 0) {
374                Qmsg1(jcr, M_WARNING, 0, _("Can't restore ACL of %s\n"), jcr->last_fname);
375          }
376 #else 
377          non_support_acl++;
378 #endif
379          break;
380
381       case STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL:
382 #ifdef HAVE_ACL
383          pm_strcpy(jcr->acl_text, sd->msg);
384          Dmsg2(400, "Restoring ACL type 0x%2x <%s>\n", BACL_TYPE_DEFAULT, jcr->acl_text);
385          if (bacl_set(jcr, BACL_TYPE_DEFAULT) != 0) {
386                Qmsg1(jcr, M_WARNING, 0, _("Can't restore default ACL of %s\n"), jcr->last_fname);
387          }
388 #else 
389          non_support_acl++;
390 #endif
391          break;
392
393       case STREAM_SIGNED_DIGEST:
394          /* Save signature. */
395          sig = crypto_sign_decode(sd->msg, (size_t) sd->msglen);
396          break;
397
398       case STREAM_MD5_DIGEST:
399       case STREAM_SHA1_DIGEST:
400       case STREAM_SHA256_DIGEST:
401       case STREAM_SHA512_DIGEST:
402          break;
403
404       case STREAM_PROGRAM_NAMES:
405       case STREAM_PROGRAM_DATA:
406          if (!non_support_progname) {
407             Pmsg0(000, "Got Program Name or Data Stream. Ignored.\n");
408             non_support_progname++;
409          }
410          break;
411
412       default:
413          /* If extracting, wierd stream (not 1 or 2), close output file anyway */
414          if (extract) {
415             Dmsg1(30, "Found wierd stream %d\n", stream);
416             if (size > 0 && !is_bopen(&bfd)) {
417                Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should be open\n"));
418             }
419             set_attributes(jcr, attr, &bfd);
420             extract = false;
421          } else if (is_bopen(&bfd)) {
422             Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should not be open\n"));
423             bclose(&bfd);
424          }
425          Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"), stream);
426          Dmsg2(0, "None of above!!! stream=%d data=%s\n", stream,sd->msg);
427          break;
428       } /* end switch(stream) */
429
430    } /* end while get_msg() */
431
432    /* If output file is still open, it was the last one in the
433     * archive since we just hit an end of file, so close the file.
434     */
435    if (is_bopen(&altbfd)) {
436       bclose_chksize(jcr, &altbfd, alt_size);
437    }
438    if (extract) {
439       set_attributes(jcr, attr, &bfd);
440    }
441    if (is_bopen(&bfd)) {
442       bclose(&bfd);
443    }
444    set_jcr_job_status(jcr, JS_Terminated);
445    goto ok_out;
446
447 bail_out:
448    set_jcr_job_status(jcr, JS_ErrorTerminated);
449 ok_out:
450    if (jcr->compress_buf) {
451       free(jcr->compress_buf);
452       jcr->compress_buf = NULL;
453       jcr->compress_buf_size = 0;
454    }
455    bclose(&altbfd);
456    bclose(&bfd);
457    free_attr(attr);
458    free_pool_memory(jcr->acl_text);
459    Dmsg2(10, "End Do Restore. Files=%d Bytes=%s\n", jcr->JobFiles,
460       edit_uint64(jcr->JobBytes, ec1));
461    if (non_support_data > 1 || non_support_attr > 1) {
462       Jmsg(jcr, M_ERROR, 0, _("%d non-supported data streams and %d non-supported attrib streams ignored.\n"),
463          non_support_data, non_support_attr);
464    }
465    if (non_support_rsrc) {
466       Jmsg(jcr, M_INFO, 0, _("%d non-supported resource fork streams ignored.\n"), non_support_rsrc);
467    }
468    if (non_support_finfo) {
469       Jmsg(jcr, M_INFO, 0, _("%d non-supported Finder Info streams ignored.\n"), non_support_rsrc);
470    }
471    if (non_support_acl) {
472       Jmsg(jcr, M_INFO, 0, _("%d non-supported acl streams ignored.\n"), non_support_acl);
473    }
474
475 }
476
477 #ifdef HAVE_LIBZ
478 /*
479  * Convert ZLIB error code into an ASCII message
480  */
481 static const char *zlib_strerror(int stat)
482 {
483    if (stat >= 0) {
484       return _("None");
485    }
486    switch (stat) {
487    case Z_ERRNO:
488       return _("Zlib errno");
489    case Z_STREAM_ERROR:
490       return _("Zlib stream error");
491    case Z_DATA_ERROR:
492       return _("Zlib data error");
493    case Z_MEM_ERROR:
494       return _("Zlib memory error");
495    case Z_BUF_ERROR:
496       return _("Zlib buffer error");
497    case Z_VERSION_ERROR:
498       return _("Zlib version error");
499    default:
500       return _("*none*");
501    }
502 }
503 #endif
504
505 static int do_file_digest(FF_PKT *ff_pkt, void *pkt, bool top_level) {
506    JCR *jcr = (JCR *) pkt;
507    return (digest_file(jcr, ff_pkt, jcr->digest));
508 }
509
510 /*
511  * Verify the signature for the last restored file
512  * Return value is either true (signature correct)
513  * or false (signature could not be verified).
514  */
515 int verify_signature(JCR *jcr, SIGNATURE *sig)
516 {
517    X509_KEYPAIR *keypair;
518    DIGEST *digest = NULL;
519    crypto_error_t err;
520
521
522    /* Iterate through the trusted signers */
523    foreach_alist(keypair, jcr->pki_signers) {
524       err = crypto_sign_get_digest(sig, jcr->pki_keypair, &digest);
525
526       switch (err) {
527       case CRYPTO_ERROR_NONE:
528          /* Signature found, digest allocated */
529          jcr->digest = digest;
530
531          /* Checksum the entire file */
532          if (find_one_file(jcr, jcr->ff, do_file_digest, jcr, jcr->last_fname, (dev_t)-1, 1) != 0) {
533             Qmsg(jcr, M_ERROR, 0, _("Signature validation failed for %s: \n"), jcr->last_fname);
534             return false;
535          }
536
537          /* Verify the signature */
538          if ((err = crypto_sign_verify(sig, keypair, digest)) != CRYPTO_ERROR_NONE) {
539             Qmsg2(jcr, M_ERROR, 0, _("Signature validation failed for %s: %s\n"), jcr->last_fname, crypto_strerror(err));
540             crypto_digest_free(digest);
541             return false;
542          }
543
544          /* Valid signature */
545          crypto_digest_free(digest);
546          return true;
547
548       case CRYPTO_ERROR_NOSIGNER:
549          /* Signature not found, try again */
550          continue;
551       default:
552          /* Something strange happened (that shouldn't happen!)... */
553          Qmsg2(jcr, M_ERROR, 0, _("Signature validation failed for %s: %s\n"), jcr->last_fname, crypto_strerror(err));
554          if (digest) {
555             crypto_digest_free(digest);
556          }
557          return false;
558       }
559    }
560
561    /* Unreachable */
562    return false;
563 }
564
565 /*
566  * In the context of jcr, write data to bfd.
567  * We write buflen bytes in buf at addr. addr is updated in place.
568  * The flags specify whether to use sparse files or compression.
569  * Return value is the number of bytes written, or -1 on errors.
570  */
571 int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen,
572       uint64_t *addr, int flags)
573 {
574    int stat;
575    char *wbuf;                        /* write buffer */
576    uint32_t wsize;                    /* write size */
577    uint32_t rsize;                    /* read size */
578    char ec1[50];                      /* Buffer printing huge values */
579
580    if (flags & FO_SPARSE) {
581       ser_declare;
582       uint64_t faddr;
583       char ec1[50];
584       wbuf = buf + SPARSE_FADDR_SIZE;
585       rsize = buflen - SPARSE_FADDR_SIZE;
586       ser_begin(buf, SPARSE_FADDR_SIZE);
587       unser_uint64(faddr);
588       if (*addr != faddr) {
589          *addr = faddr;
590          if (blseek(bfd, (off_t)*addr, SEEK_SET) < 0) {
591             berrno be;
592             Jmsg3(jcr, M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
593                   edit_uint64(*addr, ec1), jcr->last_fname, 
594                   be.strerror(bfd->berrno));
595             return -1;
596          }
597       }
598    } else {
599       wbuf = buf;
600       rsize = buflen;
601    }
602    wsize = rsize;
603
604    if (flags & FO_GZIP) {
605 #ifdef HAVE_LIBZ
606       uLong compress_len;
607       /* 
608        * NOTE! We only use uLong and Byte because they are
609        *  needed by the zlib routines, they should not otherwise
610        *  be used in Bacula.
611        */
612       compress_len = jcr->compress_buf_size;
613       Dmsg2(100, "Comp_len=%d msglen=%d\n", compress_len, wsize);
614       if ((stat=uncompress((Byte *)jcr->compress_buf, &compress_len,
615                   (const Byte *)wbuf, (uLong)rsize)) != Z_OK) {
616          Qmsg(jcr, M_ERROR, 0, _("Uncompression error on file %s. ERR=%s\n"),
617                jcr->last_fname, zlib_strerror(stat));
618          return -1;
619       }
620       wbuf = jcr->compress_buf;
621       wsize = compress_len;
622       Dmsg2(100, "Write uncompressed %d bytes, total before write=%s\n", compress_len, edit_uint64(jcr->JobBytes, ec1));
623 #else
624       Qmsg(jcr, M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
625       return -1;
626 #endif
627    } else {
628       Dmsg2(30, "Write %u bytes, total before write=%s\n", wsize, edit_uint64(jcr->JobBytes, ec1));
629    }
630
631    if (flags & FO_WIN32DECOMP) {
632       if (!processWin32BackupAPIBlock(bfd, wbuf, wsize)) {
633          berrno be;
634          Jmsg2(jcr, M_ERROR, 0, _("Write error in Win32 Block Decomposition on %s: %s\n"), 
635                jcr->last_fname, be.strerror(bfd->berrno));
636          return -1;
637       }
638    } else if (bwrite(bfd, wbuf, wsize) != (ssize_t)wsize) {
639       berrno be;
640       Jmsg2(jcr, M_ERROR, 0, _("Write error on %s: %s\n"), 
641             jcr->last_fname, be.strerror(bfd->berrno));
642       return -1;
643    }
644
645    jcr->JobBytes += wsize;
646    jcr->ReadBytes += rsize;
647    *addr += wsize;
648
649    return wsize;
650 }