2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
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.
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.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Bacula File Daemon verify-vol.c Verify files on a Volume
22 * versus attributes in Catalog
24 * Kern Sibbald, July MMII
30 #include "findlib/win32filter.h"
32 #if defined(HAVE_LIBZ)
33 const bool have_libz = true;
35 const bool have_libz = false;
39 const bool have_lzo = true;
41 const bool have_lzo = false;
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 */
54 bool check_size; /* Check or not the size attribute */
55 bool check_chksum; /* Check the checksum */
57 crypto_digest_t digesttype;
58 Win32Filter win32filter;
59 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; /* current digest */
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()
72 /* Call this function when we change the file
73 * We check the st_size and we compute the digest
75 bool close_previous_stream();
77 /* Call when we have a sparse record */
78 void skip_sparse_header(char **data, uint32_t *length);
80 /* Scan the fileset to know if we want to check checksums or st_size */
83 /* In cleanup, we reset the current file size to -1 */
88 /* Used for sparse files */
89 void set_size(int64_t val) {
90 size = MAX(size, val);
93 void update_size(int64_t val) {
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);
105 crypto_digest_update(jcr->crypto.digest, (uint8_t *)wbuf, wsize);
110 /* Data received from Storage Daemon */
111 static char rec_header[] = "rechdr %ld %ld %ld %ld %ld";
113 /* Forward referenced functions */
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.
120 void v_ctx::scan_fileset()
122 findFILESET *fileset;
124 check_size = check_chksum = false;
125 digesttype = CRYPTO_DIGEST_NONE;
127 if (!jcr->ff || !jcr->ff->fileset) {
131 fileset = jcr->ff->fileset;
133 for (int i=0; i<fileset->include_list.size(); i++) {
134 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
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))
145 if (fo->flags & FO_MD5) {
146 digesttype = CRYPTO_DIGEST_MD5;
149 if (fo->flags & FO_SHA1) {
150 digesttype = CRYPTO_DIGEST_SHA1;
153 if (fo->flags & FO_SHA256) {
154 digesttype = CRYPTO_DIGEST_SHA256;
157 if (fo->flags & FO_SHA512) {
158 digesttype = CRYPTO_DIGEST_SHA512;
163 digesttype = CRYPTO_DIGEST_NONE;
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;
170 /* Compute the file size for sparse records and adjust the data */
171 void v_ctx::skip_sparse_header(char **data, uint32_t *length)
175 unser_begin(*data, OFFSET_FADDR_SIZE);
178 /* For sparse, we assume that the file is at least big as faddr */
181 *data += OFFSET_FADDR_SIZE;
182 *length -= OFFSET_FADDR_SIZE;
186 * If extracting, close any previous stream
188 bool v_ctx::close_previous_stream()
191 uint8_t buf[CRYPTO_DIGEST_MAX_SIZE];
192 uint32_t len = CRYPTO_DIGEST_MAX_SIZE;
193 char ed1[50], ed2[50];
195 /* Reset the win32 filter that strips header stream out of the file */
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);
203 _(" st_size differs on \"%s\". Vol: %s File: %s\n"),
205 edit_int64(size, ed1),
206 edit_int64((int64_t)attr->statp.st_size, ed2));
207 jcr->setJobStatus(JS_Differences);
212 /* Compute the digest and store it */
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);
220 bin_to_base64(digest, sizeof(digest), (char *)buf, len, true);
222 crypto_digest_free(jcr->crypto.digest);
223 jcr->crypto.digest = NULL;
229 * Verify attributes or data of the requested files on the Volume
232 void do_verify_volume(JCR *jcr)
236 uint32_t VolSessionId, VolSessionTime, file_index;
237 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
239 char *wbuf; /* write buffer */
240 uint32_t wsize; /* write size */
241 uint32_t rsize; /* read size */
242 bool msg_encrypt = false;
245 ATTR *attr = vctx.attr;
247 sd = jcr->store_bsock;
249 Jmsg(jcr, M_FATAL, 0, _("Storage command not issued before Verify.\n"));
250 jcr->setJobStatus(JS_FatalError);
253 dir = jcr->dir_bsock;
254 jcr->setJobStatus(JS_Running);
257 CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
261 buf_size = client->max_network_buffer_size;
263 buf_size = 0; /* use default */
265 if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) {
266 jcr->setJobStatus(JS_FatalError);
269 jcr->buf_size = sd->msglen;
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;
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));
283 * Get a record from the Storage daemon
285 while (fdmsg->bget_msg(&bmsg) >= 0 && !job_canceled(jcr)) {
286 /* Remember previous stream type */
287 vctx.prev_stream = vctx.stream;
290 * First we expect a Stream Record Header
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);
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);
302 * Now we expect the Stream Data
304 if (fdmsg->bget_msg(&bmsg) < 0) {
305 Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), sd->bstrerror());
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);
312 Dmsg2(30, "Got stream data %s, len=%d\n", stream_to_ascii(vctx.stream), bmsg->rbuflen);
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()) {
323 * Unpack attributes and do sanity check them
325 if (!unpack_attributes_record(jcr, vctx.stream,
326 bmsg->rbuf, bmsg->rbuflen, attr)) {
330 attr->data_stream = decode_stat(attr->attr, &attr->statp,
331 sizeof(attr->statp), &attr->LinkFI);
335 jcr->num_files_examined++;
336 pm_strcpy(jcr->last_fname, attr->fname); /* last file examined */
339 if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
341 * Send file attributes to Director
345 * Filename (full path)
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.
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);
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);
367 Dmsg2(200, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg);
369 Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror());
376 * Restore stream object is counted, but not restored here
378 case STREAM_RESTORE_OBJECT:
381 jcr->num_files_examined++;
389 const char *digest_code = NULL;
391 switch(vctx.stream) {
392 case STREAM_MD5_DIGEST:
393 bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_MD5_SIZE, true);
397 case STREAM_SHA1_DIGEST:
398 bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA1_SIZE, true);
399 digest_code = "SHA1";
402 case STREAM_SHA256_DIGEST:
403 bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA256_SIZE, true);
404 digest_code = "SHA256";
407 case STREAM_SHA512_DIGEST:
408 bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA512_SIZE, true);
409 digest_code = "SHA512";
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);
421 } else if (jcr->getJobLevel() == L_VERIFY_DATA) {
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);
429 vctx.close_previous_stream();
430 if (strncmp(digest, vctx.digest,
431 MIN(sizeof(digest), sizeof(vctx.digest))) != 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);
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:
452 Jmsg(jcr, M_WARNING, 0,
453 _("Verification of encrypted file data is not supported.\n"));
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:
469 if (!(attr->type == FT_RAW || attr->type == FT_FIFO || attr->type == FT_REG)) {
474 rsize = bmsg->rbuflen;
475 jcr->ReadBytes += rsize;
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);
484 /* On Windows, the checksum is computed after the compression
485 * On Unix, the checksum is computed before the compression
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)
494 vctx.update_checksum(wbuf, wsize);
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) {
508 if (!decompress_data(jcr, vctx.stream, &wbuf, &wsize)) {
509 dequeue_messages(jcr);
514 /* Unix way to deal with checksums */
516 vctx.update_checksum(wbuf, wsize);
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) {
525 int64_t wbuf_len = wsize;
527 if (vctx.win32filter.have_data(&wbuf, &wbuf_len, &wsize64)) {
531 jcr->JobBytes += wsize;
532 vctx.update_size(wsize);
535 /* TODO: Handle data to compute checksums */
536 /* Ignore everything else */
541 } /* end while bnet_get */
542 if (!vctx.close_previous_stream()) {
545 jcr->setJobStatus(JS_Terminated);
549 jcr->setJobStatus(JS_ErrorTerminated);
552 Dmsg0(215, "wait BufferedMsg\n");
553 fdmsg->wait_read_sock();
556 if (jcr->compress_buf) {
557 free_pool_memory(jcr->compress_buf);
558 jcr->compress_buf = NULL;
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,