2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 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 verify-vol.c Verify files on a Volume
21 * versus attributes in Catalog
23 * Kern Sibbald, July MMII
29 #include "findlib/win32filter.h"
31 #if defined(HAVE_LIBZ)
32 const bool have_libz = true;
34 const bool have_libz = false;
38 const bool have_lzo = true;
40 const bool have_lzo = false;
43 /* Context used during Verify Data job. We use it in the
44 * verify loop to compute checksums and check attributes.
49 int32_t stream; /* stream less new bits */
50 int32_t prev_stream; /* previous stream */
51 int32_t full_stream; /* full stream including new bits */
52 int32_t type; /* file type FT_ */
53 int64_t size; /* current file size */
54 ATTR *attr; /* Pointer to attributes */
56 bool check_size; /* Check or not the size attribute */
57 bool check_chksum; /* Check the checksum */
59 crypto_digest_t digesttype;
60 Win32Filter win32filter;
61 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; /* current digest */
64 jcr(ajcr), stream(0), prev_stream(0), full_stream(0), type(0), size(-1),
65 attr(new_attr(jcr)), check_size(false), check_chksum(false),
66 digesttype(CRYPTO_DIGEST_NONE), win32filter()
74 /* Call this function when we change the file
75 * We check the st_size and we compute the digest
77 bool close_previous_stream();
79 /* Call when we have a sparse record */
80 void skip_sparse_header(char **data, uint32_t *length);
82 /* Scan the fileset to know if we want to check checksums or st_size */
85 /* In cleanup, we reset the current file size to -1 */
90 /* Used for sparse files */
91 void set_size(int64_t val) {
92 size = MAX(size, val);
95 void update_size(int64_t val) {
102 void update_checksum(char *wbuf, int32_t wsize) {
103 if (wsize > 0 && check_chksum) {
104 if (!jcr->crypto.digest) {
105 jcr->crypto.digest = crypto_digest_new(jcr, digesttype);
107 crypto_digest_update(jcr->crypto.digest, (uint8_t *)wbuf, wsize);
112 /* Data received from Storage Daemon */
113 static char rec_header[] = "rechdr %ld %ld %ld %ld %ld";
115 /* Forward referenced functions */
117 /* We don't know in advance which digest mode is needed, we do not
118 * want to store files on disk either to check afterward. So, we read
119 * the fileset definition and we try to guess the digest that will be
120 * used. If the FileSet uses multiple digests, it will not work.
122 void v_ctx::scan_fileset()
124 findFILESET *fileset;
126 check_size = check_chksum = false;
127 digesttype = CRYPTO_DIGEST_NONE;
129 if (!jcr->ff || !jcr->ff->fileset) {
133 fileset = jcr->ff->fileset;
135 for (int i=0; i<fileset->include_list.size(); i++) {
136 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
138 for (int j=0; j<incexe->opts_list.size(); j++) {
139 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
140 check_size = (strchr(fo->VerifyOpts, 's') != NULL);
141 if ((strchr(fo->VerifyOpts, '1') != NULL) ||
142 (strchr(fo->VerifyOpts, '5') != NULL))
147 if (fo->flags & FO_MD5) {
148 digesttype = CRYPTO_DIGEST_MD5;
151 if (fo->flags & FO_SHA1) {
152 digesttype = CRYPTO_DIGEST_SHA1;
155 if (fo->flags & FO_SHA256) {
156 digesttype = CRYPTO_DIGEST_SHA256;
159 if (fo->flags & FO_SHA512) {
160 digesttype = CRYPTO_DIGEST_SHA512;
165 digesttype = CRYPTO_DIGEST_NONE;
167 Jmsg(jcr, M_WARNING, 0, _("Checksum verification required in Verify FileSet option, but no Signature found in the FileSet\n"));
168 check_chksum = false;
172 /* Compute the file size for sparse records and adjust the data */
173 void v_ctx::skip_sparse_header(char **data, uint32_t *length)
177 unser_begin(*data, OFFSET_FADDR_SIZE);
180 /* For sparse, we assume that the file is at least big as faddr */
183 *data += OFFSET_FADDR_SIZE;
184 *length -= OFFSET_FADDR_SIZE;
188 * If extracting, close any previous stream
190 bool v_ctx::close_previous_stream()
193 uint8_t buf[CRYPTO_DIGEST_MAX_SIZE];
194 uint32_t len = CRYPTO_DIGEST_MAX_SIZE;
195 char ed1[50], ed2[50];
197 /* Reset the win32 filter that strips header stream out of the file */
200 /* Check the size if possible */
201 if (check_size && size >= 0) {
202 if (attr->type == FT_REG && size != attr->statp.st_size) {
203 Dmsg1(50, "Size comparison failed for %s\n", jcr->last_fname);
205 _(" st_size differs on \"%s\". Vol: %s File: %s\n"),
207 edit_int64(size, ed1),
208 edit_int64((int64_t)attr->statp.st_size, ed2));
209 jcr->setJobStatus(JS_Differences);
214 /* Compute the digest and store it */
216 if (jcr->crypto.digest) {
217 if (!crypto_digest_finalize(jcr->crypto.digest, buf, &len)) {
218 Dmsg1(50, "Unable to finalize digest for %s\n", jcr->last_fname);
222 bin_to_base64(digest, sizeof(digest), (char *)buf, len, true);
224 crypto_digest_free(jcr->crypto.digest);
225 jcr->crypto.digest = NULL;
231 * Verify attributes or data of the requested files on the Volume
234 void do_verify_volume(JCR *jcr)
238 uint32_t VolSessionId, VolSessionTime, file_index;
239 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
241 char *wbuf; /* write buffer */
242 uint32_t wsize; /* write size */
243 uint32_t rsize; /* read size */
244 bool msg_encrypt = false;
247 ATTR *attr = vctx.attr;
249 sd = jcr->store_bsock;
251 Jmsg(jcr, M_FATAL, 0, _("Storage command not issued before Verify.\n"));
252 jcr->setJobStatus(JS_FatalError);
255 dir = jcr->dir_bsock;
256 jcr->setJobStatus(JS_Running);
259 CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
263 buf_size = client->max_network_buffer_size;
265 buf_size = 0; /* use default */
267 if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) {
268 jcr->setJobStatus(JS_FatalError);
271 jcr->buf_size = sd->msglen;
273 /* use the same buffer size to decompress both gzip and lzo */
274 if (have_libz || have_lzo) {
275 uint32_t compress_buf_size = jcr->buf_size + 12 + ((jcr->buf_size+999) / 1000) + 100;
276 jcr->compress_buf = get_memory(compress_buf_size);
277 jcr->compress_buf_size = compress_buf_size;
280 GetMsg *fdmsg = New(GetMsg(jcr, sd, rec_header, GETMSG_MAX_MSG_SIZE));
281 fdmsg->start_read_sock();
282 bmessage *bmsg = New(bmessage(GETMSG_MAX_MSG_SIZE));
285 * Get a record from the Storage daemon
287 while (fdmsg->bget_msg(&bmsg) >= 0 && !job_canceled(jcr)) {
288 /* Remember previous stream type */
289 vctx.prev_stream = vctx.stream;
292 * First we expect a Stream Record Header
294 if (sscanf(bmsg->rbuf, rec_header, &VolSessionId, &VolSessionTime, &file_index,
295 &vctx.full_stream, &size) != 5) {
296 Jmsg1(jcr, M_FATAL, 0, _("Record header scan error: %s\n"), bmsg->rbuf);
299 vctx.stream = vctx.full_stream & STREAMMASK_TYPE;
300 Dmsg4(30, "Got hdr: FilInx=%d FullStream=%d Stream=%d size=%d.\n",
301 file_index, vctx.full_stream, vctx.stream, size);
304 * Now we expect the Stream Data
306 if (fdmsg->bget_msg(&bmsg) < 0) {
307 Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), sd->bstrerror());
310 if (size != ((uint32_t)bmsg->origlen)) {
311 Jmsg2(jcr, M_FATAL, 0, _("Actual data size %d not same as header %d\n"), bmsg->origlen, size);
314 Dmsg2(30, "Got stream data %s, len=%d\n", stream_to_ascii(vctx.stream), bmsg->rbuflen);
316 /* File Attributes stream */
317 switch (vctx.stream) {
318 case STREAM_UNIX_ATTRIBUTES:
319 case STREAM_UNIX_ATTRIBUTES_EX:
320 Dmsg0(400, "Stream=Unix Attributes.\n");
321 if (!vctx.close_previous_stream()) {
325 * Unpack attributes and do sanity check them
327 if (!unpack_attributes_record(jcr, vctx.stream,
328 bmsg->rbuf, bmsg->rbuflen, attr)) {
332 attr->data_stream = decode_stat(attr->attr, &attr->statp,
333 sizeof(attr->statp), &attr->LinkFI);
337 jcr->num_files_examined++;
338 pm_strcpy(jcr->last_fname, attr->fname); /* last file examined */
341 if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
343 * Send file attributes to Director
347 * Filename (full path)
349 * Link name (if type==FT_LNK)
350 * For a directory, link is the same as fname, but with trailing
351 * slash. For a linked file, link is the link.
353 /* Send file attributes to Director */
354 Dmsg2(200, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, attr->fname);
355 if (attr->type == FT_LNK || attr->type == FT_LNKSAVED) {
356 stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
357 STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
358 0, attr->attr, 0, attr->lname, 0);
359 /* for a deleted record, we set fileindex=0 */
360 } else if (attr->type == FT_DELETED) {
361 stat = dir->fsend("%d %d %s %s%c%s%c%c", 0,
362 STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
363 0, attr->attr, 0, 0);
365 stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
366 STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
367 0, attr->attr, 0, 0);
369 Dmsg2(200, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg);
371 Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror());
378 * Restore stream object is counted, but not restored here
380 case STREAM_RESTORE_OBJECT:
383 jcr->num_files_examined++;
391 const char *digest_code = NULL;
393 switch(vctx.stream) {
394 case STREAM_MD5_DIGEST:
395 bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_MD5_SIZE, true);
399 case STREAM_SHA1_DIGEST:
400 bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA1_SIZE, true);
401 digest_code = "SHA1";
404 case STREAM_SHA256_DIGEST:
405 bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA256_SIZE, true);
406 digest_code = "SHA256";
409 case STREAM_SHA512_DIGEST:
410 bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA512_SIZE, true);
411 digest_code = "SHA512";
419 if (digest_code && jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
420 dir->fsend("%d %d %s *%s-%d*", jcr->JobFiles, vctx.stream,
421 digest, digest_code, jcr->JobFiles);
423 } else if (jcr->getJobLevel() == L_VERIFY_DATA) {
426 if (vctx.check_chksum && *digest) {
427 /* probably an empty file, we can create an empty crypto session */
428 if (!jcr->crypto.digest) {
429 jcr->crypto.digest = crypto_digest_new(jcr, vctx.digesttype);
431 vctx.close_previous_stream();
432 if (strncmp(digest, vctx.digest,
433 MIN(sizeof(digest), sizeof(vctx.digest))) != 0)
436 _(" %s differs on \"%s\". File=%s Vol=%s\n"),
437 stream_to_ascii(vctx.stream), jcr->last_fname,
438 vctx.digest, digest);
439 jcr->setJobStatus(JS_Differences);
440 Dmsg3(50, "Signature verification failed for %s %s != %s\n",
441 jcr->last_fname, digest, vctx.digest);
445 /* Compute size and checksum for level=Data */
446 switch (vctx.stream) {
447 case STREAM_ENCRYPTED_FILE_DATA:
448 case STREAM_ENCRYPTED_WIN32_DATA:
449 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
450 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
451 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
452 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
454 Jmsg(jcr, M_WARNING, 0,
455 _("Verification of encrypted file data is not supported.\n"));
460 case STREAM_PLUGIN_DATA:
461 case STREAM_FILE_DATA:
462 case STREAM_SPARSE_DATA:
463 case STREAM_WIN32_DATA:
464 case STREAM_GZIP_DATA:
465 case STREAM_SPARSE_GZIP_DATA:
466 case STREAM_WIN32_GZIP_DATA:
467 case STREAM_COMPRESSED_DATA:
468 case STREAM_SPARSE_COMPRESSED_DATA:
469 case STREAM_WIN32_COMPRESSED_DATA:
471 if (!(attr->type == FT_RAW || attr->type == FT_FIFO || attr->type == FT_REG)) {
476 rsize = bmsg->rbuflen;
477 jcr->ReadBytes += rsize;
480 if (vctx.stream == STREAM_SPARSE_DATA
481 || vctx.stream == STREAM_SPARSE_COMPRESSED_DATA
482 || vctx.stream == STREAM_SPARSE_GZIP_DATA) {
483 vctx.skip_sparse_header(&wbuf, &wsize);
486 /* On Windows, the checksum is computed after the compression
487 * On Unix, the checksum is computed before the compression
489 if (vctx.stream == STREAM_WIN32_GZIP_DATA
490 || vctx.stream == STREAM_WIN32_DATA
491 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
492 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
493 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA)
496 vctx.update_checksum(wbuf, wsize);
499 if (vctx.stream == STREAM_GZIP_DATA
500 || vctx.stream == STREAM_SPARSE_GZIP_DATA
501 || vctx.stream == STREAM_WIN32_GZIP_DATA
502 || vctx.stream == STREAM_ENCRYPTED_FILE_GZIP_DATA
503 || vctx.stream == STREAM_COMPRESSED_DATA
504 || vctx.stream == STREAM_SPARSE_COMPRESSED_DATA
505 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
506 || vctx.stream == STREAM_ENCRYPTED_FILE_COMPRESSED_DATA
507 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
508 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
510 if (!decompress_data(jcr, vctx.stream, &wbuf, &wsize)) {
511 dequeue_messages(jcr);
516 /* Unix way to deal with checksums */
518 vctx.update_checksum(wbuf, wsize);
521 if (vctx.stream == STREAM_WIN32_GZIP_DATA
522 || vctx.stream == STREAM_WIN32_DATA
523 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
524 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
525 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
527 int64_t wbuf_len = wsize;
529 if (vctx.win32filter.have_data(&wbuf, &wbuf_len, &wsize64)) {
533 jcr->JobBytes += wsize;
534 vctx.update_size(wsize);
537 /* TODO: Handle data to compute checksums */
538 /* Ignore everything else */
543 } /* end while bnet_get */
544 if (!vctx.close_previous_stream()) {
547 jcr->setJobStatus(JS_Terminated);
551 jcr->setJobStatus(JS_ErrorTerminated);
554 Dmsg0(215, "wait BufferedMsg\n");
555 fdmsg->wait_read_sock();
558 if (jcr->compress_buf) {
559 free_pool_memory(jcr->compress_buf);
560 jcr->compress_buf = NULL;
562 /* TODO: We probably want to mark the job as failed if we have errors */
563 Dmsg2(50, "End Verify-Vol. Files=%d Bytes=%" lld "\n", jcr->JobFiles,