]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/verify_vol.c
b05ae65ade61aaab37c64a9d045e2c27dcff916f
[bacula/bacula] / bacula / src / filed / verify_vol.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *  Bacula File Daemon  verify-vol.c Verify files on a Volume
22  *    versus attributes in Catalog
23  *
24  *    Kern Sibbald, July MMII
25  *
26  */
27
28 #include "bacula.h"
29 #include "filed.h"
30 #include "findlib/win32filter.h"
31
32 #if   defined(HAVE_LIBZ)
33 const bool have_libz = true;
34 #else
35 const bool have_libz = false;
36 #endif
37
38 #ifdef HAVE_LZO
39 const bool have_lzo = true;
40 #else
41 const bool have_lzo = false;
42 #endif
43
44 class v_ctx {
45 public:
46    JCR *jcr;
47    int32_t stream;              /* stream less new bits */
48    int32_t prev_stream;         /* previous stream */
49    int32_t full_stream;         /* full stream including new bits */
50    int32_t type;                /* file type FT_ */
51    int64_t size;                /* current file size */
52    ATTR *attr;                  /* Pointer to attributes */
53
54    bool check_size;             /* Check or not the size attribute */
55    bool check_chksum;           /* Check the checksum */
56
57    crypto_digest_t digesttype;
58    Win32Filter win32filter;
59    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; /* current digest */
60
61    v_ctx(JCR *ajcr) :
62       jcr(ajcr), stream(0), prev_stream(0), full_stream(0), type(0), size(-1),
63       attr(new_attr(jcr)), check_size(false), check_chksum(false),
64       digesttype(CRYPTO_DIGEST_NONE), win32filter()
65    {
66       *digest = 0;
67       scan_fileset();
68    };
69    ~v_ctx() {
70       free_attr(attr);
71    };
72    /* Call this function when we change the file
73     * We check the st_size and we compute the digest
74     */
75    bool close_previous_stream();
76
77    /* Call when we have a sparse record */
78    void skip_sparse_header(char **data, uint32_t *length);
79
80    /* Scan the fileset to know if we want to check checksums or st_size */
81    void scan_fileset();
82
83    /* In cleanup, we reset the current file size to -1 */
84    void reset_size() {
85       size = -1;
86    };
87
88    /* Used for sparse files */
89    void set_size(int64_t val) {
90       size = MAX(size, val);
91    };
92
93    void update_size(int64_t val) {
94       if (size == -1) {
95          size = 0;
96       }
97       size += val;
98    };
99
100    void update_checksum(char *wbuf, int32_t wsize) {
101       if (wsize > 0 && check_chksum) {
102          if (!jcr->crypto.digest) {
103             jcr->crypto.digest = crypto_digest_new(jcr, digesttype);
104          }
105          crypto_digest_update(jcr->crypto.digest, (uint8_t *)wbuf, wsize);
106       }
107    };
108 };
109
110 /* Data received from Storage Daemon */
111 static char rec_header[] = "rechdr %ld %ld %ld %ld %ld";
112
113 /* Forward referenced functions */
114
115 /* We don't know in advance which digest mode is needed, we do not
116  * want to store files on disk either to check afterward. So, we read
117  * the fileset definition and we try to guess the digest that will be
118  * used. If the FileSet uses multiple digests, it will not work.
119  */
120 void v_ctx::scan_fileset()
121 {
122    findFILESET *fileset;
123
124    check_size = check_chksum = false;
125    digesttype = CRYPTO_DIGEST_NONE;
126
127    if (!jcr->ff || !jcr->ff->fileset) {
128       return;
129    }
130
131    fileset = jcr->ff->fileset;
132
133    for (int i=0; i<fileset->include_list.size(); i++) {
134       findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
135
136       for (int j=0; j<incexe->opts_list.size(); j++) {
137          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
138          check_size = (strchr(fo->VerifyOpts, 's') != NULL);
139          if ((strchr(fo->VerifyOpts, '1') != NULL) ||
140              (strchr(fo->VerifyOpts, '5') != NULL))
141          {
142             check_chksum = true;
143          } 
144
145          if (fo->flags & FO_MD5) {
146             digesttype = CRYPTO_DIGEST_MD5;
147             return;
148          }
149          if (fo->flags & FO_SHA1) {
150             digesttype = CRYPTO_DIGEST_SHA1;
151             return;
152          }
153          if (fo->flags & FO_SHA256) {
154             digesttype = CRYPTO_DIGEST_SHA256;
155             return;
156          }
157          if (fo->flags & FO_SHA512) {
158             digesttype = CRYPTO_DIGEST_SHA512;
159             return;
160          }
161       }
162    }
163    digesttype = CRYPTO_DIGEST_NONE;
164    if (check_chksum) {
165       Jmsg(jcr, M_WARNING, 0, _("Checksum verification required in Verify FileSet option, but no Signature found in the FileSet\n"));
166       check_chksum = false;
167    }
168 }
169
170 /* Compute the file size for sparse records and adjust the data */
171 void v_ctx::skip_sparse_header(char **data, uint32_t *length)
172 {
173    unser_declare;
174    uint64_t faddr;
175    unser_begin(*data, OFFSET_FADDR_SIZE);
176    unser_uint64(faddr);
177
178    /* For sparse, we assume that the file is at least big as faddr */
179    set_size(faddr);
180    
181    *data += OFFSET_FADDR_SIZE;
182    *length -= OFFSET_FADDR_SIZE;
183 }
184
185 /*
186  * If extracting, close any previous stream
187  */
188 bool v_ctx::close_previous_stream()
189 {
190    bool rtn = true;
191    uint8_t buf[CRYPTO_DIGEST_MAX_SIZE];
192    uint32_t len = CRYPTO_DIGEST_MAX_SIZE;
193    char ed1[50], ed2[50];
194
195    /* Reset the win32 filter that strips header stream out of the file */
196    win32filter.init();
197
198    /* Check the size if possible */
199    if (check_size && size >= 0) {
200       if (attr->type == FT_REG && size != attr->statp.st_size) {
201          Dmsg1(50, "Size comparison failed for %s\n", jcr->last_fname);
202          Jmsg(jcr, M_INFO, 0,
203               _("   st_size  differs on \"%s\". Vol: %s File: %s\n"),
204               jcr->last_fname,
205               edit_int64(size, ed1),
206               edit_int64((int64_t)attr->statp.st_size, ed2));
207          jcr->setJobStatus(JS_Differences);
208       }
209       reset_size();
210    }
211
212    /* Compute the digest and store it */
213    *digest = 0;
214    if (jcr->crypto.digest) {
215       if (!crypto_digest_finalize(jcr->crypto.digest, buf, &len)) {
216          Dmsg1(50, "Unable to finalize digest for %s\n", jcr->last_fname);
217          rtn = false;
218
219       } else {
220          bin_to_base64(digest, sizeof(digest), (char *)buf, len, true);
221       }
222       crypto_digest_free(jcr->crypto.digest);
223       jcr->crypto.digest = NULL;
224    }
225    return rtn;
226 }
227
228 /*
229  * Verify attributes or data of the requested files on the Volume
230  *
231  */
232 void do_verify_volume(JCR *jcr)
233 {
234    BSOCK *sd, *dir;
235    uint32_t size;
236    uint32_t VolSessionId, VolSessionTime, file_index;
237    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
238    int stat;
239    char *wbuf;                        /* write buffer */
240    uint32_t wsize;                    /* write size */
241    uint32_t rsize;                    /* read size */
242    bool msg_encrypt = false;
243    bool do_chksum;
244    v_ctx vctx(jcr);
245    ATTR *attr = vctx.attr;
246
247    sd = jcr->store_bsock;
248    if (!sd) {
249       Jmsg(jcr, M_FATAL, 0, _("Storage command not issued before Verify.\n"));
250       jcr->setJobStatus(JS_FatalError);
251       return;
252    }
253    dir = jcr->dir_bsock;
254    jcr->setJobStatus(JS_Running);
255
256    LockRes();
257    CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
258    UnlockRes();
259    uint32_t buf_size;
260    if (client) {
261       buf_size = client->max_network_buffer_size;
262    } else {
263       buf_size = 0;                   /* use default */
264    }
265    if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) {
266       jcr->setJobStatus(JS_FatalError);
267       return;
268    }
269    jcr->buf_size = sd->msglen;
270
271    /* use the same buffer size to decompress both gzip and lzo */
272    if (have_libz || have_lzo) {
273       uint32_t compress_buf_size = jcr->buf_size + 12 + ((jcr->buf_size+999) / 1000) + 100;
274       jcr->compress_buf = get_memory(compress_buf_size);
275       jcr->compress_buf_size = compress_buf_size;
276    }
277
278    GetMsg *fdmsg = New(GetMsg(jcr, sd, rec_header, GETMSG_MAX_MSG_SIZE));
279    fdmsg->start_read_sock();
280    bmessage *bmsg = New(bmessage(GETMSG_MAX_MSG_SIZE));
281
282    /*
283     * Get a record from the Storage daemon
284     */
285    while (fdmsg->bget_msg(&bmsg) >= 0 && !job_canceled(jcr)) {
286       /* Remember previous stream type */
287       vctx.prev_stream = vctx.stream;
288
289       /*
290        * First we expect a Stream Record Header
291        */
292       if (sscanf(bmsg->rbuf, rec_header, &VolSessionId, &VolSessionTime, &file_index,
293           &vctx.full_stream, &size) != 5) {
294          Jmsg1(jcr, M_FATAL, 0, _("Record header scan error: %s\n"), bmsg->rbuf);
295          goto bail_out;
296       }
297       vctx.stream = vctx.full_stream & STREAMMASK_TYPE;
298       Dmsg4(30, "Got hdr: FilInx=%d FullStream=%d Stream=%d size=%d.\n",
299             file_index, vctx.full_stream, vctx.stream, size);
300
301       /*
302        * Now we expect the Stream Data
303        */
304       if (fdmsg->bget_msg(&bmsg) < 0) {
305          Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), sd->bstrerror());
306          goto bail_out;
307       }
308       if (size != ((uint32_t)bmsg->origlen)) {
309          Jmsg2(jcr, M_FATAL, 0, _("Actual data size %d not same as header %d\n"), bmsg->origlen, size);
310          goto bail_out;
311       }
312       Dmsg2(30, "Got stream data %s, len=%d\n", stream_to_ascii(vctx.stream), bmsg->rbuflen);
313
314       /* File Attributes stream */
315       switch (vctx.stream) {
316       case STREAM_UNIX_ATTRIBUTES:
317       case STREAM_UNIX_ATTRIBUTES_EX:
318          Dmsg0(400, "Stream=Unix Attributes.\n");
319          if (!vctx.close_previous_stream()) {
320             goto bail_out;
321          }
322          /*
323           * Unpack attributes and do sanity check them
324           */
325          if (!unpack_attributes_record(jcr, vctx.stream,
326                                        bmsg->rbuf, bmsg->rbuflen, attr)) {
327             goto bail_out;
328          }
329
330          attr->data_stream = decode_stat(attr->attr, &attr->statp,
331                                          sizeof(attr->statp), &attr->LinkFI);
332
333          jcr->lock();
334          jcr->JobFiles++;
335          jcr->num_files_examined++;
336          pm_strcpy(jcr->last_fname, attr->fname); /* last file examined */
337          jcr->unlock();
338
339          if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
340             /*
341              * Send file attributes to Director
342              *   File_index
343              *   Stream
344              *   Verify Options
345              *   Filename (full path)
346              *   Encoded attributes
347              *   Link name (if type==FT_LNK)
348              * For a directory, link is the same as fname, but with trailing
349              * slash. For a linked file, link is the link.
350              */
351             /* Send file attributes to Director */
352             Dmsg2(200, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, attr->fname);
353             if (attr->type == FT_LNK || attr->type == FT_LNKSAVED) {
354                stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
355                                  STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
356                                  0, attr->attr, 0, attr->lname, 0);
357                /* for a deleted record, we set fileindex=0 */
358             } else if (attr->type == FT_DELETED)  {
359                stat = dir->fsend("%d %d %s %s%c%s%c%c", 0,
360                                  STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
361                                  0, attr->attr, 0, 0);
362             } else {
363                stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
364                                  STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
365                                  0, attr->attr, 0, 0);
366             }
367             Dmsg2(200, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg);
368             if (!stat) {
369                Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror());
370                goto bail_out;
371             }
372          }
373          break;
374
375          /*
376           * Restore stream object is counted, but not restored here
377           */
378       case STREAM_RESTORE_OBJECT:
379          jcr->lock();
380          jcr->JobFiles++;
381          jcr->num_files_examined++;
382          jcr->unlock();
383          break;
384
385       default:
386          break;
387       }
388
389       const char *digest_code = NULL;
390
391       switch(vctx.stream) {
392       case STREAM_MD5_DIGEST:
393          bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_MD5_SIZE, true);
394          digest_code = "MD5";
395          break;
396
397       case STREAM_SHA1_DIGEST:
398          bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA1_SIZE, true);
399          digest_code = "SHA1";
400          break;
401
402       case STREAM_SHA256_DIGEST:
403          bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA256_SIZE, true);
404          digest_code = "SHA256";
405          break;
406
407       case STREAM_SHA512_DIGEST:
408          bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA512_SIZE, true);
409          digest_code = "SHA512";
410          break;
411
412       default:
413          *digest = 0;
414          break;
415       }
416
417       if (digest_code && jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
418          dir->fsend("%d %d %s *%s-%d*", jcr->JobFiles, vctx.stream,
419                     digest, digest_code, jcr->JobFiles);
420
421       } else if (jcr->getJobLevel() == L_VERIFY_DATA) {
422
423          /* Compare digest */
424          if (vctx.check_chksum && *digest) {
425             /* probably an empty file, we can create an empty crypto session */
426             if (!jcr->crypto.digest) {
427                jcr->crypto.digest = crypto_digest_new(jcr, vctx.digesttype);
428             }
429             vctx.close_previous_stream();
430             if (strncmp(digest, vctx.digest,
431                         MIN(sizeof(digest), sizeof(vctx.digest))) != 0)
432             {
433                Jmsg(jcr, M_INFO, 0,
434                     _("   %s differs on \"%s\". File=%s Vol=%s\n"),
435                     stream_to_ascii(vctx.stream), jcr->last_fname,
436                     vctx.digest, digest);
437                jcr->setJobStatus(JS_Differences);
438                Dmsg3(50, "Signature verification failed for %s %s != %s\n",
439                      jcr->last_fname, digest, vctx.digest);
440             }
441          }
442
443          /* Compute size and checksum for level=Data */
444          switch (vctx.stream) {
445          case STREAM_ENCRYPTED_FILE_DATA:
446          case STREAM_ENCRYPTED_WIN32_DATA:
447          case STREAM_ENCRYPTED_FILE_GZIP_DATA:
448          case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
449          case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
450          case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
451             if (!msg_encrypt) {
452                Jmsg(jcr, M_WARNING, 0,
453                   _("Verification of encrypted file data is not supported.\n"));
454                msg_encrypt = true;
455             }
456             break;
457
458          case STREAM_PLUGIN_DATA:
459          case STREAM_FILE_DATA:
460          case STREAM_SPARSE_DATA:
461          case STREAM_WIN32_DATA:
462          case STREAM_GZIP_DATA:
463          case STREAM_SPARSE_GZIP_DATA:
464          case STREAM_WIN32_GZIP_DATA:
465          case STREAM_COMPRESSED_DATA:
466          case STREAM_SPARSE_COMPRESSED_DATA:
467          case STREAM_WIN32_COMPRESSED_DATA:
468             do_chksum=true;
469             if (!(attr->type ==  FT_RAW || attr->type == FT_FIFO || attr->type == FT_REG)) {
470                break;
471             }
472
473             wbuf = bmsg->rbuf;
474             rsize = bmsg->rbuflen;
475             jcr->ReadBytes += rsize;
476             wsize = rsize;
477
478             if (vctx.stream == STREAM_SPARSE_DATA
479                 || vctx.stream == STREAM_SPARSE_COMPRESSED_DATA
480                 || vctx.stream == STREAM_SPARSE_GZIP_DATA) {
481                vctx.skip_sparse_header(&wbuf, &wsize);
482             }
483
484             /* On Windows, the checksum is computed after the compression
485              * On Unix, the checksum is computed before the compression
486              */
487             if (vctx.stream == STREAM_WIN32_GZIP_DATA
488                 || vctx.stream == STREAM_WIN32_DATA
489                 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
490                 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
491                 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA)
492             {
493                do_chksum = false;
494                vctx.update_checksum(wbuf, wsize);
495             }
496
497             if (vctx.stream == STREAM_GZIP_DATA
498                 || vctx.stream == STREAM_SPARSE_GZIP_DATA
499                 || vctx.stream == STREAM_WIN32_GZIP_DATA
500                 || vctx.stream == STREAM_ENCRYPTED_FILE_GZIP_DATA
501                 || vctx.stream == STREAM_COMPRESSED_DATA
502                 || vctx.stream == STREAM_SPARSE_COMPRESSED_DATA
503                 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
504                 || vctx.stream == STREAM_ENCRYPTED_FILE_COMPRESSED_DATA
505                 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
506                 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
507
508                if (!decompress_data(jcr, vctx.stream, &wbuf, &wsize)) {
509                   dequeue_messages(jcr);
510                   goto bail_out;
511                }
512             }
513
514             /* Unix way to deal with checksums */
515             if (do_chksum) {
516                vctx.update_checksum(wbuf, wsize);
517             }
518
519             if (vctx.stream == STREAM_WIN32_GZIP_DATA
520                 || vctx.stream == STREAM_WIN32_DATA
521                 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
522                 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
523                 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
524
525                int64_t wbuf_len = wsize;
526                int64_t wsize64 = 0;
527                if (vctx.win32filter.have_data(&wbuf, &wbuf_len, &wsize64)) {
528                   wsize = wsize64;
529                }
530             }
531             jcr->JobBytes += wsize;
532             vctx.update_size(wsize);
533             break;
534
535             /* TODO: Handle data to compute checksums */
536             /* Ignore everything else */
537          default:
538             break;
539          }
540       } /* end switch */
541    } /* end while bnet_get */
542    if (!vctx.close_previous_stream()) {
543       goto bail_out;
544    }
545    jcr->setJobStatus(JS_Terminated);
546    goto ok_out;
547
548 bail_out:
549    jcr->setJobStatus(JS_ErrorTerminated);
550
551 ok_out:
552    Dmsg0(215, "wait BufferedMsg\n");
553    fdmsg->wait_read_sock();
554    delete bmsg;
555    free_GetMsg(fdmsg);
556    if (jcr->compress_buf) {
557       free_pool_memory(jcr->compress_buf);
558       jcr->compress_buf = NULL;
559    }
560    /* TODO: We probably want to mark the job as failed if we have errors */
561    Dmsg2(50, "End Verify-Vol. Files=%d Bytes=%" lld "\n", jcr->JobFiles,
562       jcr->JobBytes);
563 }