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