]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/restore.c
Suite de dirdconf.tex
[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     *        b. Possibly stream encryption session data (e.g., symmetric session key)
139     *    or  c. File data for the file
140     *    or  d. Alternate data stream (e.g. Resource Fork)
141     *    or  e. Finder info
142     *    or  f. ACLs
143     *    or  g. Possibly a cryptographic signature
144     *    or  h. Possibly MD5 or SHA1 record
145     *   3. Repeat step 1
146     *
147     * NOTE: We keep track of two bacula file descriptors:
148     *   1. bfd for file data.
149     *      This fd is opened for non empty files when an attribute stream is
150     *      encountered and closed when we find the next attribute stream.
151     *   2. alt_bfd for alternate data streams
152     *      This fd is opened every time we encounter a new alternate data
153     *      stream for the current file. When we find any other stream, we
154     *      close it again.
155     *      The expected size of the stream, alt_len, should be set when
156     *      opening the fd.
157     */
158    binit(&bfd);
159    binit(&altbfd);
160    attr = new_attr();
161    jcr->acl_text = get_pool_memory(PM_MESSAGE);
162
163    while (bget_msg(sd) >= 0 && !job_canceled(jcr)) {
164       /* Remember previous stream type */
165       prev_stream = stream;
166
167       /* First we expect a Stream Record Header */
168       if (sscanf(sd->msg, rec_header, &VolSessionId, &VolSessionTime, &file_index,
169           &stream, &size) != 5) {
170          Jmsg1(jcr, M_FATAL, 0, _("Record header scan error: %s\n"), sd->msg);
171          goto bail_out;
172       }
173       Dmsg2(30, "Got hdr: FilInx=%d Stream=%d.\n", file_index, stream);
174
175       /* * Now we expect the Stream Data */
176       if (bget_msg(sd) < 0) {
177          Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), bnet_strerror(sd));
178          goto bail_out;
179       }
180       if (size != (uint32_t)sd->msglen) {
181          Jmsg2(jcr, M_FATAL, 0, _("Actual data size %d not same as header %d\n"), sd->msglen, size);
182          goto bail_out;
183       }
184       Dmsg1(30, "Got stream data, len=%d\n", sd->msglen);
185
186       /* If we change streams, close and reset alternate data streams */
187       if (prev_stream != stream) {
188          if (is_bopen(&altbfd)) {
189             bclose_chksize(jcr, &altbfd, alt_size);
190          }
191          alt_size = -1; /* Use an impossible value and set a proper one below */
192          alt_addr = 0;
193       }
194
195       /* File Attributes stream */
196       switch (stream) {
197       case STREAM_UNIX_ATTRIBUTES:
198       case STREAM_UNIX_ATTRIBUTES_EX:
199          Dmsg1(30, "Stream=Unix Attributes. extract=%d\n", extract);
200          /*
201           * If extracting, it was from previous stream, so
202           * close the output file and validate the signature.
203           */
204          if (extract) {
205             if (size > 0 && !is_bopen(&bfd)) {
206                Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should be open\n"));
207             }
208             set_attributes(jcr, attr, &bfd);
209             extract = false;
210
211             /* Verify the cryptographic signature, if any */
212             if (jcr->pki_sign) {
213                if (sig) {
214                   if (!verify_signature(jcr, sig)) {
215                      // TODO landonf: Better signature failure handling.
216                      // The failure is reported to the director in verify_signature() ...
217                      Dmsg1(100, "Bad signature on %s\n", jcr->last_fname);
218                   } else {
219                      Dmsg1(100, "Signature good on %s\n", jcr->last_fname);
220                   }
221                   crypto_sign_free(sig);
222                   sig = NULL;
223                } else {
224                   Jmsg1(jcr, M_ERROR, 0, _("Missing cryptographic signature for %s\n"), jcr->last_fname);
225                }
226             }
227             Dmsg0(30, "Stop extracting.\n");
228          } else if (is_bopen(&bfd)) {
229             Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should not be open\n"));
230             bclose(&bfd);
231          }
232
233          /*
234           * Unpack and do sanity check fo attributes.
235           */
236          if (!unpack_attributes_record(jcr, stream, sd->msg, attr)) {
237             goto bail_out;
238          }
239          if (file_index != attr->file_index) {
240             Jmsg(jcr, M_FATAL, 0, _("Record header file index %ld not equal record index %ld\n"),
241                  file_index, attr->file_index);
242             Dmsg0(100, "File index error\n");
243             goto bail_out;
244          }
245
246          Dmsg3(200, "File %s\nattrib=%s\nattribsEx=%s\n", attr->fname,
247                attr->attr, attr->attrEx);
248
249          attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
250
251          if (!is_restore_stream_supported(attr->data_stream)) {
252             if (!non_support_data++) {
253                Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
254                   stream_to_ascii(attr->data_stream));
255             }
256             continue;
257          }
258
259          build_attr_output_fnames(jcr, attr);
260
261          /*
262           * Now determine if we are extracting or not.
263           */
264          jcr->num_files_examined++;
265          Dmsg1(30, "Outfile=%s\n", attr->ofname);
266          extract = false;
267          stat = create_file(jcr, attr, &bfd, jcr->replace);
268          switch (stat) {
269          case CF_ERROR:
270          case CF_SKIP:
271             break;
272          case CF_EXTRACT:        /* File created and we expect file data */
273             extract = true;
274             /* FALLTHROUGH */
275          case CF_CREATED:        /* File created, but there is no content */
276             P(jcr->mutex);
277             pm_strcpy(jcr->last_fname, attr->ofname);
278             V(jcr->mutex);
279             jcr->JobFiles++;
280             fileAddr = 0;
281             print_ls_output(jcr, attr);
282 #ifdef HAVE_DARWIN_OS
283             /* Only restore the resource fork for regular files */
284             from_base64(&rsrc_len, attr->attrEx);
285             if (attr->type == FT_REG && rsrc_len > 0) {
286                extract = true;
287             }
288 #endif
289             if (!extract) {
290                /* set attributes now because file will not be extracted */
291                set_attributes(jcr, attr, &bfd);
292             }
293             break;
294          }
295          break;
296
297       /* Data stream */
298       case STREAM_ENCRYPTED_SESSION_DATA:
299          // TODO landonf: Implement
300          // sig = crypto_sign_decode(sd->msg, (size_t) sd->msglen);
301          Dmsg1(30, "Stream=Encrypted Session Data, size: %d\n", sd->msglen);
302          break;
303
304       case STREAM_FILE_DATA:
305       case STREAM_SPARSE_DATA:
306       case STREAM_WIN32_DATA:
307       case STREAM_GZIP_DATA:
308       case STREAM_SPARSE_GZIP_DATA:
309       case STREAM_WIN32_GZIP_DATA:
310          /* Force an expected, consistent stream type here */
311          if (extract && (prev_stream == stream || prev_stream == STREAM_UNIX_ATTRIBUTES
312                   || prev_stream == STREAM_UNIX_ATTRIBUTES_EX
313                   || prev_stream == STREAM_ENCRYPTED_SESSION_DATA)) {
314             flags = 0;
315             if (stream == STREAM_SPARSE_DATA || stream == STREAM_SPARSE_GZIP_DATA) {
316                flags |= FO_SPARSE;
317             }
318             if (stream == STREAM_GZIP_DATA || stream == STREAM_SPARSE_GZIP_DATA
319                   || stream == STREAM_WIN32_GZIP_DATA) {
320                flags |= FO_GZIP;
321             }
322
323             if (is_win32_stream(stream) && !have_win32_api()) {
324                set_portable_backup(&bfd);
325                flags |= FO_WIN32DECOMP;    /* "decompose" BackupWrite data */
326             }
327
328             if (extract_data(jcr, &bfd, sd->msg, sd->msglen, &fileAddr, flags) < 0) {
329                extract = false;
330                bclose(&bfd);
331                continue;
332             }
333          }
334          break;
335
336       /* Resource fork stream - only recorded after a file to be restored */
337       /* Silently ignore if we cannot write - we already reported that */
338       case STREAM_MACOS_FORK_DATA:
339 #ifdef HAVE_DARWIN_OS
340          if (extract) {
341             if (prev_stream != stream) {
342                if (bopen_rsrc(&altbfd, jcr->last_fname, O_WRONLY | O_TRUNC | O_BINARY, 0) < 0) {
343                   Jmsg(jcr, M_ERROR, 0, _("     Cannot open resource fork for %s.\n"), jcr->last_fname);
344                   extract = false;
345                   continue;
346                }
347                alt_size = rsrc_len;
348                Dmsg0(30, "Restoring resource fork\n");
349             }
350             flags = 0;
351             if (extract_data(jcr, &altbfd, sd->msg, sd->msglen, &alt_addr, flags) < 0) {
352                extract = false;
353                bclose(&altbfd);
354                continue;
355             }
356          }
357 #else
358          non_support_rsrc++;
359 #endif
360          break;
361
362       case STREAM_HFSPLUS_ATTRIBUTES:
363 #ifdef HAVE_DARWIN_OS
364          Dmsg0(30, "Restoring Finder Info\n");
365          if (sd->msglen != 32) {
366             Jmsg(jcr, M_ERROR, 0, _("     Invalid length of Finder Info (got %d, not 32)\n"), sd->msglen);
367             continue;
368          }
369          if (setattrlist(jcr->last_fname, &attrList, sd->msg, sd->msglen, 0) != 0) {
370             Jmsg(jcr, M_ERROR, 0, _("     Could not set Finder Info on %s\n"), jcr->last_fname);
371             continue;
372          }
373 #else
374          non_support_finfo++;
375 #endif
376
377       case STREAM_UNIX_ATTRIBUTES_ACCESS_ACL:
378 #ifdef HAVE_ACL
379          pm_strcpy(jcr->acl_text, sd->msg);
380          Dmsg2(400, "Restoring ACL type 0x%2x <%s>\n", BACL_TYPE_ACCESS, jcr->acl_text);
381          if (bacl_set(jcr, BACL_TYPE_ACCESS) != 0) {
382                Qmsg1(jcr, M_WARNING, 0, _("Can't restore ACL of %s\n"), jcr->last_fname);
383          }
384 #else 
385          non_support_acl++;
386 #endif
387          break;
388
389       case STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL:
390 #ifdef HAVE_ACL
391          pm_strcpy(jcr->acl_text, sd->msg);
392          Dmsg2(400, "Restoring ACL type 0x%2x <%s>\n", BACL_TYPE_DEFAULT, jcr->acl_text);
393          if (bacl_set(jcr, BACL_TYPE_DEFAULT) != 0) {
394                Qmsg1(jcr, M_WARNING, 0, _("Can't restore default ACL of %s\n"), jcr->last_fname);
395          }
396 #else 
397          non_support_acl++;
398 #endif
399          break;
400
401       case STREAM_SIGNED_DIGEST:
402          /* Save signature. */
403          sig = crypto_sign_decode(sd->msg, (size_t) sd->msglen);
404          break;
405
406       case STREAM_MD5_DIGEST:
407       case STREAM_SHA1_DIGEST:
408       case STREAM_SHA256_DIGEST:
409       case STREAM_SHA512_DIGEST:
410          break;
411
412       case STREAM_PROGRAM_NAMES:
413       case STREAM_PROGRAM_DATA:
414          if (!non_support_progname) {
415             Pmsg0(000, "Got Program Name or Data Stream. Ignored.\n");
416             non_support_progname++;
417          }
418          break;
419
420       default:
421          /* If extracting, wierd stream (not 1 or 2), close output file anyway */
422          if (extract) {
423             Dmsg1(30, "Found wierd stream %d\n", stream);
424             if (size > 0 && !is_bopen(&bfd)) {
425                Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should be open\n"));
426             }
427             set_attributes(jcr, attr, &bfd);
428             extract = false;
429          } else if (is_bopen(&bfd)) {
430             Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should not be open\n"));
431             bclose(&bfd);
432          }
433          Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"), stream);
434          Dmsg2(0, "None of above!!! stream=%d data=%s\n", stream,sd->msg);
435          break;
436       } /* end switch(stream) */
437
438    } /* end while get_msg() */
439
440    /* If output file is still open, it was the last one in the
441     * archive since we just hit an end of file, so close the file.
442     */
443    if (is_bopen(&altbfd)) {
444       bclose_chksize(jcr, &altbfd, alt_size);
445    }
446    if (extract) {
447       set_attributes(jcr, attr, &bfd);
448    }
449    if (is_bopen(&bfd)) {
450       bclose(&bfd);
451    }
452    set_jcr_job_status(jcr, JS_Terminated);
453    goto ok_out;
454
455 bail_out:
456    set_jcr_job_status(jcr, JS_ErrorTerminated);
457 ok_out:
458    if (jcr->compress_buf) {
459       free(jcr->compress_buf);
460       jcr->compress_buf = NULL;
461       jcr->compress_buf_size = 0;
462    }
463    bclose(&altbfd);
464    bclose(&bfd);
465    free_attr(attr);
466    free_pool_memory(jcr->acl_text);
467    Dmsg2(10, "End Do Restore. Files=%d Bytes=%s\n", jcr->JobFiles,
468       edit_uint64(jcr->JobBytes, ec1));
469    if (non_support_data > 1 || non_support_attr > 1) {
470       Jmsg(jcr, M_ERROR, 0, _("%d non-supported data streams and %d non-supported attrib streams ignored.\n"),
471          non_support_data, non_support_attr);
472    }
473    if (non_support_rsrc) {
474       Jmsg(jcr, M_INFO, 0, _("%d non-supported resource fork streams ignored.\n"), non_support_rsrc);
475    }
476    if (non_support_finfo) {
477       Jmsg(jcr, M_INFO, 0, _("%d non-supported Finder Info streams ignored.\n"), non_support_rsrc);
478    }
479    if (non_support_acl) {
480       Jmsg(jcr, M_INFO, 0, _("%d non-supported acl streams ignored.\n"), non_support_acl);
481    }
482
483 }
484
485 #ifdef HAVE_LIBZ
486 /*
487  * Convert ZLIB error code into an ASCII message
488  */
489 static const char *zlib_strerror(int stat)
490 {
491    if (stat >= 0) {
492       return _("None");
493    }
494    switch (stat) {
495    case Z_ERRNO:
496       return _("Zlib errno");
497    case Z_STREAM_ERROR:
498       return _("Zlib stream error");
499    case Z_DATA_ERROR:
500       return _("Zlib data error");
501    case Z_MEM_ERROR:
502       return _("Zlib memory error");
503    case Z_BUF_ERROR:
504       return _("Zlib buffer error");
505    case Z_VERSION_ERROR:
506       return _("Zlib version error");
507    default:
508       return _("*none*");
509    }
510 }
511 #endif
512
513 static int do_file_digest(FF_PKT *ff_pkt, void *pkt, bool top_level) {
514    JCR *jcr = (JCR *) pkt;
515    return (digest_file(jcr, ff_pkt, jcr->digest));
516 }
517
518 /*
519  * Verify the signature for the last restored file
520  * Return value is either true (signature correct)
521  * or false (signature could not be verified).
522  */
523 int verify_signature(JCR *jcr, SIGNATURE *sig)
524 {
525    X509_KEYPAIR *keypair;
526    DIGEST *digest = NULL;
527    crypto_error_t err;
528
529
530    /* Iterate through the trusted signers */
531    foreach_alist(keypair, jcr->pki_signers) {
532       err = crypto_sign_get_digest(sig, jcr->pki_keypair, &digest);
533
534       switch (err) {
535       case CRYPTO_ERROR_NONE:
536          /* Signature found, digest allocated */
537          jcr->digest = digest;
538
539          /* Checksum the entire file */
540          if (find_one_file(jcr, jcr->ff, do_file_digest, jcr, jcr->last_fname, (dev_t)-1, 1) != 0) {
541             Qmsg(jcr, M_ERROR, 0, _("Signature validation failed for %s: \n"), jcr->last_fname);
542             return false;
543          }
544
545          /* Verify the signature */
546          if ((err = crypto_sign_verify(sig, keypair, digest)) != CRYPTO_ERROR_NONE) {
547             Qmsg2(jcr, M_ERROR, 0, _("Signature validation failed for %s: %s\n"), jcr->last_fname, crypto_strerror(err));
548             crypto_digest_free(digest);
549             return false;
550          }
551
552          /* Valid signature */
553          crypto_digest_free(digest);
554          return true;
555
556       case CRYPTO_ERROR_NOSIGNER:
557          /* Signature not found, try again */
558          continue;
559       default:
560          /* Something strange happened (that shouldn't happen!)... */
561          Qmsg2(jcr, M_ERROR, 0, _("Signature validation failed for %s: %s\n"), jcr->last_fname, crypto_strerror(err));
562          if (digest) {
563             crypto_digest_free(digest);
564          }
565          return false;
566       }
567    }
568
569    /* Unreachable */
570    return false;
571 }
572
573 /*
574  * In the context of jcr, write data to bfd.
575  * We write buflen bytes in buf at addr. addr is updated in place.
576  * The flags specify whether to use sparse files or compression.
577  * Return value is the number of bytes written, or -1 on errors.
578  */
579 int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen,
580       uint64_t *addr, int flags)
581 {
582    int stat;
583    char *wbuf;                        /* write buffer */
584    uint32_t wsize;                    /* write size */
585    uint32_t rsize;                    /* read size */
586    char ec1[50];                      /* Buffer printing huge values */
587
588    if (flags & FO_SPARSE) {
589       ser_declare;
590       uint64_t faddr;
591       char ec1[50];
592       wbuf = buf + SPARSE_FADDR_SIZE;
593       rsize = buflen - SPARSE_FADDR_SIZE;
594       ser_begin(buf, SPARSE_FADDR_SIZE);
595       unser_uint64(faddr);
596       if (*addr != faddr) {
597          *addr = faddr;
598          if (blseek(bfd, (off_t)*addr, SEEK_SET) < 0) {
599             berrno be;
600             Jmsg3(jcr, M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
601                   edit_uint64(*addr, ec1), jcr->last_fname, 
602                   be.strerror(bfd->berrno));
603             return -1;
604          }
605       }
606    } else {
607       wbuf = buf;
608       rsize = buflen;
609    }
610    wsize = rsize;
611
612    if (flags & FO_GZIP) {
613 #ifdef HAVE_LIBZ
614       uLong compress_len;
615       /* 
616        * NOTE! We only use uLong and Byte because they are
617        *  needed by the zlib routines, they should not otherwise
618        *  be used in Bacula.
619        */
620       compress_len = jcr->compress_buf_size;
621       Dmsg2(100, "Comp_len=%d msglen=%d\n", compress_len, wsize);
622       if ((stat=uncompress((Byte *)jcr->compress_buf, &compress_len,
623                   (const Byte *)wbuf, (uLong)rsize)) != Z_OK) {
624          Qmsg(jcr, M_ERROR, 0, _("Uncompression error on file %s. ERR=%s\n"),
625                jcr->last_fname, zlib_strerror(stat));
626          return -1;
627       }
628       wbuf = jcr->compress_buf;
629       wsize = compress_len;
630       Dmsg2(100, "Write uncompressed %d bytes, total before write=%s\n", compress_len, edit_uint64(jcr->JobBytes, ec1));
631 #else
632       Qmsg(jcr, M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
633       return -1;
634 #endif
635    } else {
636       Dmsg2(30, "Write %u bytes, total before write=%s\n", wsize, edit_uint64(jcr->JobBytes, ec1));
637    }
638
639    if (flags & FO_WIN32DECOMP) {
640       if (!processWin32BackupAPIBlock(bfd, wbuf, wsize)) {
641          berrno be;
642          Jmsg2(jcr, M_ERROR, 0, _("Write error in Win32 Block Decomposition on %s: %s\n"), 
643                jcr->last_fname, be.strerror(bfd->berrno));
644          return -1;
645       }
646    } else if (bwrite(bfd, wbuf, wsize) != (ssize_t)wsize) {
647       berrno be;
648       Jmsg2(jcr, M_ERROR, 0, _("Write error on %s: %s\n"), 
649             jcr->last_fname, be.strerror(bfd->berrno));
650       return -1;
651    }
652
653    jcr->JobBytes += wsize;
654    jcr->ReadBytes += rsize;
655    *addr += wsize;
656
657    return wsize;
658 }