2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 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 Director -- verify.c -- responsible for running file verification
22 * Kern Sibbald, October MM
24 * Basic tasks done here:
26 * Open connection with File daemon and pass him commands
28 * When the File daemon sends the attributes, compare them to
35 #include "findlib/find.h"
37 /* Commands sent to File daemon */
38 static char verifycmd[] = "verify level=%s\n";
40 /* Responses received from File daemon */
41 static char OKverify[] = "2000 OK verify\n";
43 /* Commands received from Storage daemon */
44 static char OKbootstrap[] = "3000 OK bootstrap\n";
46 /* Forward referenced functions */
47 static void prt_fname(JCR *jcr);
48 static int missing_handler(void *ctx, int num_fields, char **row);
51 * Called here before the job is run to do the job
54 bool do_verify_init(JCR *jcr)
56 if (!allow_duplicate_job(jcr)) {
59 switch (jcr->getJobLevel()) {
61 case L_VERIFY_CATALOG:
62 case L_VERIFY_DISK_TO_CATALOG:
67 case L_VERIFY_VOLUME_TO_CATALOG:
71 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), jcr->getJobLevel(),
80 * Do a verification of the specified files against the Catlaog
82 * Returns: false on failure
85 bool do_verify(JCR *jcr)
92 JobId_t verify_jobid = 0;
97 free_wstorage(jcr); /* we don't write */
99 memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr));
102 * Find JobId of last job that ran. Note, we do this when
103 * the job actually starts running, not at schedule time,
104 * so that we find the last job that terminated before
105 * this job runs rather than before it is scheduled. This
106 * permits scheduling a Backup and Verify at the same time,
107 * but with the Verify at a lower priority.
109 * For VERIFY_CATALOG we want the JobId of the last INIT.
110 * For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the
113 if (jcr->getJobLevel() == L_VERIFY_CATALOG ||
114 jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG ||
115 jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG ||
116 jcr->getJobLevel() == L_VERIFY_DATA) {
117 memcpy(&jr, &jcr->jr, sizeof(jr));
118 if (jcr->verify_job &&
119 (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG ||
120 jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG ||
121 jcr->getJobLevel() == L_VERIFY_DATA)) {
122 Name = jcr->verify_job->name();
126 Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
128 /* see if user supplied a jobid= as run argument or from menu */
129 if (jcr->RestoreJobId) {
130 verify_jobid = jcr->RestoreJobId;
131 Dmsg1(100, "Supplied jobid=%d\n", verify_jobid);
134 if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
135 if (jcr->getJobLevel() == L_VERIFY_CATALOG) {
136 Jmsg(jcr, M_FATAL, 0, _(
137 "Unable to find JobId of previous InitCatalog Job.\n"
138 "Please run a Verify with Level=InitCatalog before\n"
139 "running the current Job.\n"));
141 Jmsg(jcr, M_FATAL, 0, _(
142 "Unable to find JobId of previous Job for this client.\n"));
146 verify_jobid = jr.JobId;
148 Dmsg1(100, "Last full jobid=%d\n", verify_jobid);
151 * Now get the job record for the previous backup that interests
152 * us. We use the verify_jobid that we found above.
154 if (jcr->getJobLevel() == L_VERIFY_CATALOG ||
155 jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG ||
156 jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG ||
157 jcr->getJobLevel() == L_VERIFY_DATA) {
158 jcr->previous_jr.JobId = verify_jobid;
159 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
160 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"),
161 db_strerror(jcr->db));
164 if (!(jcr->previous_jr.JobStatus == JS_Terminated ||
165 jcr->previous_jr.JobStatus == JS_Warnings)) {
166 Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
167 verify_jobid, jcr->previous_jr.JobStatus);
170 Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"),
171 jcr->previous_jr.JobId, jcr->previous_jr.Job);
175 * If we are verifying a Volume, we need the Storage
176 * daemon, so open a connection, otherwise, just
177 * create a dummy authorization key (passed to
178 * File daemon but not used).
180 if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) {
183 * Note: negative status is an error, zero status, means
184 * no files were backed up, so skip calling SD and
187 stat = create_restore_bootstrap_file(jcr);
188 if (stat < 0) { /* error */
190 } else if (stat == 0) { /* No files, nothing to do */
191 verify_cleanup(jcr, JS_Terminated); /* clean up */
192 return true; /* get out */
195 jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */
198 /* Pass the original fileset to the client */
199 if (jcr->getJobLevel() == L_VERIFY_DATA) {
201 memset(&fdbr, 0, sizeof(fdbr));
202 fdbr.FileSetId = jcr->previous_jr.FileSetId;
203 if (!db_get_fileset_record(jcr, jcr->db, &fdbr)) {
204 Jmsg(jcr, M_FATAL, 0,
205 _("Could not get fileset record from previous Job. ERR=%s"),
206 db_strerror(jcr->db));
210 jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fdbr.FileSet);
212 if (jcr->verify_job) {
213 jcr->fileset = jcr->verify_job->fileset;
214 Jmsg(jcr, M_WARNING, 0,
215 _("Could not find FileSet resource \"%s\" from previous Job\n"),
218 _("Using FileSet \"%\"\n"), jcr->fileset->name());
221 Jmsg(jcr, M_FATAL, 0,
222 _("Could not get FileSet resource for verify Job."));
226 Dmsg1(50, "FileSet = %s\n", jcr->fileset->name());
229 /* Pass the current fileset to the client */
230 if (jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG && jcr->verify_job) {
231 jcr->fileset = jcr->verify_job->fileset;
233 Dmsg2(100, "ClientId=%u JobLevel=%c\n",
234 jcr->previous_jr.ClientId, jcr->getJobLevel());
236 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
237 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
241 /* Print Job Start message */
242 Jmsg(jcr, M_INFO, 0, _("Start Verify JobId=%s Level=%s Job=%s\n"),
243 edit_uint64(jcr->JobId, ed1), level_to_str(jcr->getJobLevel()), jcr->Job);
245 if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG ||
246 jcr->getJobLevel() == L_VERIFY_DATA)
249 * Start conversation with Storage daemon
251 jcr->setJobStatus(JS_Blocked);
252 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
256 * Now start a job with the Storage daemon
258 if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) {
261 sd = jcr->store_bsock;
262 jcr->sd_calls_client = jcr->client->sd_calls_client;
264 * Send the bootstrap file -- what Volumes/files to restore
266 if (!send_bootstrap_file(jcr, sd) ||
267 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
270 if (!jcr->sd_calls_client) {
271 if (!run_storage_and_start_message_thread(jcr, sd)) {
277 * OK, now connect to the File daemon
278 * and ask him for the files.
280 jcr->setJobStatus(JS_Blocked);
281 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
285 jcr->setJobStatus(JS_Running);
286 fd = jcr->file_bsock;
289 Dmsg0(30, ">filed: Send include list\n");
290 if (!send_include_list(jcr)) {
294 Dmsg0(30, ">filed: Send exclude list\n");
295 if (!send_exclude_list(jcr)) {
300 * Send Level command to File daemon, as well
301 * as the Storage address if appropriate.
303 switch (jcr->getJobLevel()) {
307 case L_VERIFY_CATALOG:
311 send_accurate_current_files(jcr);
312 /* Fall-through wanted */
313 case L_VERIFY_VOLUME_TO_CATALOG:
314 if (jcr->sd_calls_client) {
315 if (jcr->FDVersion < 10) {
316 Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n"));
320 if (!send_client_addr_to_sd(jcr)) {
324 if (!run_storage_and_start_message_thread(jcr, jcr->store_bsock)) {
327 store_address = jcr->rstore->address; /* dummy */
328 store_port = 0; /* flag that SD calls FD */
331 * send Storage daemon address to the File daemon
333 if (jcr->rstore->SDDport == 0) {
334 jcr->rstore->SDDport = jcr->rstore->SDport;
337 store_address = get_storage_address(jcr->client, jcr->rstore);
338 store_port = jcr->rstore->SDDport;
341 if (!send_store_addr_to_fd(jcr, jcr->rstore, store_address, store_port)) {
345 if (!jcr->RestoreBootstrap) {
346 Jmsg0(jcr, M_FATAL, 0, _("Deprecated feature ... use bootstrap.\n"));
349 if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
355 case L_VERIFY_DISK_TO_CATALOG:
356 level="disk_to_catalog";
359 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"),
365 if (!send_runscripts_commands(jcr)) {
370 * Send verify command/level to File daemon
372 fd->fsend(verifycmd, level);
373 if (!response(jcr, fd, OKverify, "Verify", DISPLAY_ERROR)) {
378 * Now get data back from File daemon and
379 * compare it to the catalog or store it in the
380 * catalog depending on the run type.
382 /* Compare to catalog */
383 switch (jcr->getJobLevel()) {
384 case L_VERIFY_CATALOG:
385 Dmsg0(10, "Verify level=catalog\n");
386 jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */
387 jcr->SDJobStatus = JS_Terminated;
388 get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
391 case L_VERIFY_VOLUME_TO_CATALOG:
392 Dmsg0(10, "Verify level=volume\n");
393 get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
396 case L_VERIFY_DISK_TO_CATALOG:
397 Dmsg0(10, "Verify level=disk_to_catalog\n");
398 jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */
399 jcr->SDJobStatus = JS_Terminated;
400 get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
405 Dmsg0(10, "Verify level=init\n");
406 jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */
407 jcr->SDJobStatus = JS_Terminated;
408 get_attributes_and_put_in_catalog(jcr);
409 db_end_transaction(jcr, jcr->db); /* terminate any open transaction */
410 db_write_batch_file_records(jcr);
414 /* Nothing special to do */
415 bget_dirmsg(fd); /* eat EOD */
418 Jmsg1(jcr, M_FATAL, 0, _("Unimplemented verify level %d\n"), jcr->getJobLevel());
422 stat = wait_for_job_termination(jcr);
423 verify_cleanup(jcr, stat);
432 * Release resources allocated during backup.
435 void verify_cleanup(JCR *jcr, int TermCode)
437 char sdt[50], edt[50];
438 char ec1[30], ec2[30], elapsed[50];
439 char term_code[100], fd_term_msg[100], sd_term_msg[100];
440 const char *term_msg;
445 // Dmsg1(100, "Enter verify_cleanup() TermCod=%d\n", TermCode);
447 Dmsg3(900, "JobLevel=%c Expected=%u JobFiles=%u\n", jcr->getJobLevel(),
448 jcr->ExpectedFiles, jcr->JobFiles);
449 if ((jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) &&
450 jcr->ExpectedFiles != jcr->JobFiles) {
451 TermCode = JS_ErrorTerminated;
454 update_job_end(jcr, TermCode);
456 if (job_canceled(jcr)) {
457 cancel_storage_daemon_job(jcr);
460 if (jcr->unlink_bsr && jcr->RestoreBootstrap) {
461 unlink(jcr->RestoreBootstrap);
462 jcr->unlink_bsr = false;
465 msg_type = M_INFO; /* by default INFO message */
468 if (jcr->JobErrors || jcr->SDErrors) {
469 term_msg = _("Verify OK -- with warnings");
471 term_msg = _("Verify OK");
475 case JS_ErrorTerminated:
476 term_msg = _("*** Verify Error ***");
477 msg_type = M_ERROR; /* Generate error message */
480 term_msg = _("Verify warnings");
483 term_msg = _("Verify Canceled");
486 term_msg = _("Verify Differences");
489 term_msg = term_code;
490 bsnprintf(term_code, sizeof(term_code),
491 _("Inappropriate term code: %d %c\n"), TermCode, TermCode);
494 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
495 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
496 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
497 if (jcr->verify_job) {
498 Name = jcr->verify_job->hdr.name;
503 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
504 if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) {
505 const char *accurate = "yes";
506 if (jcr->is_JobLevel(L_VERIFY_DATA)) {
507 accurate = jcr->accurate ? "yes": "no";
509 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
510 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
511 " Build OS: %s %s %s\n"
515 " Verify Level: %s\n"
517 " Verify JobId: %d\n"
521 " Elapsed time: %s\n"
523 " Files Expected: %s\n"
524 " Files Examined: %s\n"
525 " Non-fatal FD errors: %d\n"
527 " FD termination status: %s\n"
528 " SD termination status: %s\n"
529 " Termination: %s\n\n"),
530 BACULA, my_name, VERSION, LSMDATE,
531 HOST_OS, DISTNAME, DISTVER,
534 jcr->fileset->hdr.name,
535 level_to_str(jcr->getJobLevel()),
536 jcr->client->hdr.name,
537 jcr->previous_jr.JobId,
541 edit_utime(RunTime, elapsed, sizeof(elapsed)),
543 edit_uint64_with_commas(jcr->ExpectedFiles, ec1),
544 edit_uint64_with_commas(jcr->JobFiles, ec2),
551 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
556 " Verify Level: %s\n"
558 " Verify JobId: %d\n"
562 " Elapsed time: %s\n"
563 " Files Examined: %s\n"
564 " Non-fatal FD errors: %d\n"
565 " FD termination status: %s\n"
566 " Termination: %s\n\n"),
567 BACULA, my_name, VERSION, LSMDATE,
568 HOST_OS, DISTNAME, DISTVER,
571 jcr->fileset->hdr.name,
572 level_to_str(jcr->getJobLevel()),
574 jcr->previous_jr.JobId,
578 edit_utime(RunTime, elapsed, sizeof(elapsed)),
579 edit_uint64_with_commas(jcr->JobFiles, ec1),
584 Dmsg0(100, "Leave verify_cleanup()\n");
588 * This routine is called only during a Verify
590 void get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId)
595 struct stat statf; /* file stat */
596 struct stat statc; /* catalog stat */
598 POOLMEM *fname = get_pool_memory(PM_MESSAGE);
599 int do_Digest = CRYPTO_DIGEST_NONE;
600 int32_t file_index = 0;
602 memset(&fdbr, 0, sizeof(FILE_DBR));
603 fd = jcr->file_bsock;
607 Dmsg0(20, "bdird: waiting to receive file attributes\n");
609 * Get Attributes and Signature from File daemon
613 * Options or Digest (MD5/SHA1)
618 while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) {
619 int32_t stream, full_stream;
621 char Opts_Digest[MAXSTRING]; /* Verify Opts or MD5/SHA1 digest */
623 if (job_canceled(jcr)) {
624 free_pool_memory(fname);
627 fname = check_pool_memory_size(fname, fd->msglen);
628 jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
629 Dmsg1(200, "Atts+Digest=%s\n", fd->msg);
630 if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &full_stream,
632 Jmsg3(jcr, M_FATAL, 0, _("bird<filed: bad attributes, expected 3 fields got %d\n"
633 " mslen=%d msg=%s\n"), len, fd->msglen, fd->msg);
634 free_pool_memory(fname);
637 stream = full_stream & STREAMMASK_TYPE;
638 Dmsg4(30, "Got hdr: FilInx=%d FullStream=%d Stream=%d fname=%s.\n", file_index, full_stream, stream, fname);
641 * We read the Options or Signature into fname
642 * to prevent overrun, now copy it to proper location.
644 bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest));
646 skip_nonspaces(&p); /* skip FileIndex */
648 skip_nonspaces(&p); /* skip Stream */
650 skip_nonspaces(&p); /* skip Opts_Digest */
651 p++; /* skip space */
654 *fn++ = *p++; /* copy filename */
656 *fn = *p++; /* term filename and point to attribs */
659 * Got attributes stream, decode it
661 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
662 int32_t LinkFIf, LinkFIc;
663 Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
665 jcr->FileIndex = file_index; /* remember attribute file_index */
666 jcr->previous_jr.FileIndex = file_index;
667 decode_stat(attr, &statf, sizeof(statf), &LinkFIf); /* decode file stat packet */
668 do_Digest = CRYPTO_DIGEST_NONE;
669 jcr->fn_printed = false;
670 pm_strcpy(jcr->fname, fname); /* move filename into JCR */
672 Dmsg2(040, "dird<filed: stream=%d %s\n", stream, jcr->fname);
673 Dmsg1(020, "dird<filed: attr=%s\n", attr);
676 * Find equivalent record in the database
679 if (!db_get_file_attributes_record(jcr, jcr->db, jcr->fname,
680 &jcr->previous_jr, &fdbr)) {
681 Jmsg(jcr, M_INFO, 0, _("New file: %s\n"), jcr->fname);
682 Dmsg1(020, _("File not in catalog: %s\n"), jcr->fname);
683 jcr->setJobStatus(JS_Differences);
687 * mark file record as visited by stuffing the
688 * current JobId, which is unique, into the MarkId field.
690 db_mark_file_record(jcr, jcr->db, fdbr.FileId, jcr->JobId);
693 Dmsg3(400, "Found %s in catalog. inx=%d Opts=%s\n", jcr->fname,
694 file_index, Opts_Digest);
695 decode_stat(fdbr.LStat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
697 * Loop over options supplied by user and verify the
698 * fields he requests.
700 for (p=Opts_Digest; *p; p++) {
701 char ed1[30], ed2[30];
703 case 'i': /* compare INODEs */
704 if (statc.st_ino != statf.st_ino) {
706 Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
707 edit_uint64((uint64_t)statc.st_ino, ed1),
708 edit_uint64((uint64_t)statf.st_ino, ed2));
709 jcr->setJobStatus(JS_Differences);
712 case 'p': /* permissions bits */
713 if (statc.st_mode != statf.st_mode) {
715 Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
716 (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
717 jcr->setJobStatus(JS_Differences);
720 case 'n': /* number of links */
721 if (statc.st_nlink != statf.st_nlink) {
723 Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
724 (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
725 jcr->setJobStatus(JS_Differences);
728 case 'u': /* user id */
729 if (statc.st_uid != statf.st_uid) {
731 Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
732 (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
733 jcr->setJobStatus(JS_Differences);
736 case 'g': /* group id */
737 if (statc.st_gid != statf.st_gid) {
739 Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
740 (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
741 jcr->setJobStatus(JS_Differences);
745 if (statc.st_size != statf.st_size) {
747 Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
748 edit_uint64((uint64_t)statc.st_size, ed1),
749 edit_uint64((uint64_t)statf.st_size, ed2));
750 jcr->setJobStatus(JS_Differences);
753 case 'a': /* access time */
754 if (statc.st_atime != statf.st_atime) {
756 Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
757 jcr->setJobStatus(JS_Differences);
761 if (statc.st_mtime != statf.st_mtime) {
763 Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
764 jcr->setJobStatus(JS_Differences);
767 case 'c': /* ctime */
768 if (statc.st_ctime != statf.st_ctime) {
770 Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
771 jcr->setJobStatus(JS_Differences);
774 case 'd': /* file size decrease */
775 if (statc.st_size > statf.st_size) {
777 Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
778 edit_uint64((uint64_t)statc.st_size, ed1),
779 edit_uint64((uint64_t)statf.st_size, ed2));
780 jcr->setJobStatus(JS_Differences);
783 case '5': /* compare MD5 */
784 Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
785 do_Digest = CRYPTO_DIGEST_MD5;
787 case '1': /* compare SHA1 */
788 do_Digest = CRYPTO_DIGEST_SHA1;
797 * Got Digest Signature from Storage daemon
798 * It came across in the Opts_Digest field.
800 } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
801 Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest);
803 * When ever we get a digest it MUST have been
804 * preceded by an attributes record, which sets attr_file_index
806 if (jcr->FileIndex != (uint32_t)file_index) {
807 Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
808 file_index, jcr->FileIndex);
809 free_pool_memory(fname);
812 if (do_Digest != CRYPTO_DIGEST_NONE) {
813 db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest));
814 if (strcmp(buf, fdbr.Digest) != 0) {
816 Jmsg(jcr, M_INFO, 0, _(" %s differs. File=%s Cat=%s\n"),
817 stream_to_ascii(stream), buf, fdbr.Digest);
818 jcr->setJobStatus(JS_Differences);
820 do_Digest = CRYPTO_DIGEST_NONE;
823 jcr->JobFiles = file_index;
825 if (fd->is_error()) {
827 Jmsg2(jcr, M_FATAL, 0, _("bdird<filed: bad attributes from filed n=%d : %s\n"),
829 free_pool_memory(fname);
833 /* Now find all the files that are missing -- i.e. all files in
834 * the database where the MarkId != current JobId
836 jcr->fn_printed = false;
837 bsnprintf(buf, sizeof(buf),
838 "SELECT Path.Path,Filename.Name FROM File,Path,Filename "
839 "WHERE File.JobId=%d AND File.FileIndex > 0 "
840 "AND File.MarkId!=%d AND File.PathId=Path.PathId "
841 "AND File.FilenameId=Filename.FilenameId",
843 /* missing_handler is called for each file found */
844 db_sql_query(jcr->db, buf, missing_handler, (void *)jcr);
845 if (jcr->fn_printed) {
846 jcr->setJobStatus(JS_Differences);
848 free_pool_memory(fname);
852 * We are called here for each record that matches the above
853 * SQL query -- that is for each file contained in the Catalog
854 * that was not marked earlier. This means that the file in
855 * question is a missing file (in the Catalog but not on Disk).
857 static int missing_handler(void *ctx, int num_fields, char **row)
859 JCR *jcr = (JCR *)ctx;
861 if (job_canceled(jcr)) {
864 if (!jcr->fn_printed) {
865 Qmsg(jcr, M_WARNING, 0, _("The following files are in the Catalog but not on %s:\n"),
866 jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG ? "the Volume(s)" : "disk");
867 jcr->fn_printed = true;
869 Qmsg(jcr, M_INFO, 0, " %s%s\n", row[0]?row[0]:"", row[1]?row[1]:"");
875 * Print filename for verify
877 static void prt_fname(JCR *jcr)
879 if (!jcr->fn_printed) {
880 Jmsg(jcr, M_INFO, 0, _("File: %s\n"), jcr->fname);
881 jcr->fn_printed = true;