bool get_or_create_fileset_record(JCR *jcr) { FILESET_DBR fsr; /* * Get or Create FileSet record */ memset(&fsr, 0, sizeof(FILESET_DBR)); bstrncpy(fsr.FileSet, jcr->res.fileset->hdr.name, sizeof(fsr.FileSet)); if (jcr->res.fileset->have_MD5) { struct MD5Context md5c; unsigned char digest[MD5HashSize]; memcpy(&md5c, &jcr->res.fileset->md5c, sizeof(md5c)); MD5Final(digest, &md5c); /* * Keep the flag (last arg) set to false otherwise old FileSets will * get new MD5 sums and the user will get Full backups on everything */ bin_to_base64(fsr.MD5, sizeof(fsr.MD5), (char *)digest, MD5HashSize, false); bstrncpy(jcr->res.fileset->MD5, fsr.MD5, sizeof(jcr->res.fileset->MD5)); } else { Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n")); } if (!jcr->res.fileset->ignore_fs_changes || !db_get_fileset_record(jcr, jcr->db, &fsr)) { if (!db_create_fileset_record(jcr, jcr->db, &fsr)) { Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"), fsr.FileSet, db_strerror(jcr->db)); return false; } } jcr->jr.FileSetId = fsr.FileSetId; bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime)); Dmsg2(119, "Created FileSet %s record %u\n", jcr->res.fileset->hdr.name, jcr->jr.FileSetId); return true; }
/* * Do a verification of the specified files against the Catlaog * * Returns: false on failure * true on success */ bool do_verify(JCR *jcr) { const char *level; BSOCK *fd, *sd; int stat; char ed1[100]; JOB_DBR jr; JobId_t verify_jobid = 0; char *store_address; uint32_t store_port; const char *Name; free_wstorage(jcr); /* we don't write */ memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr)); /* * Find JobId of last job that ran. Note, we do this when * the job actually starts running, not at schedule time, * so that we find the last job that terminated before * this job runs rather than before it is scheduled. This * permits scheduling a Backup and Verify at the same time, * but with the Verify at a lower priority. * * For VERIFY_CATALOG we want the JobId of the last INIT. * For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the * last backup Job. */ if (jcr->getJobLevel() == L_VERIFY_CATALOG || jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) { memcpy(&jr, &jcr->jr, sizeof(jr)); if (jcr->verify_job && (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA)) { Name = jcr->verify_job->name(); } else { Name = NULL; } Dmsg1(100, "find last jobid for: %s\n", NPRT(Name)); /* see if user supplied a jobid= as run argument or from menu */ if (jcr->RestoreJobId) { verify_jobid = jcr->RestoreJobId; Dmsg1(100, "Supplied jobid=%d\n", verify_jobid); } else { if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) { if (jcr->getJobLevel() == L_VERIFY_CATALOG) { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous InitCatalog Job.\n" "Please run a Verify with Level=InitCatalog before\n" "running the current Job.\n")); } else { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous Job for this client.\n")); } return false; } verify_jobid = jr.JobId; } Dmsg1(100, "Last full jobid=%d\n", verify_jobid); } /* * Now get the job record for the previous backup that interests * us. We use the verify_jobid that we found above. */ if (jcr->getJobLevel() == L_VERIFY_CATALOG || jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) { jcr->previous_jr.JobId = verify_jobid; if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) { Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"), db_strerror(jcr->db)); return false; } if (!(jcr->previous_jr.JobStatus == JS_Terminated || jcr->previous_jr.JobStatus == JS_Warnings)) { Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"), verify_jobid, jcr->previous_jr.JobStatus); return false; } Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"), jcr->previous_jr.JobId, jcr->previous_jr.Job); } /* * If we are verifying a Volume, we need the Storage * daemon, so open a connection, otherwise, just * create a dummy authorization key (passed to * File daemon but not used). */ if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) { int stat; /* * Note: negative status is an error, zero status, means * no files were backed up, so skip calling SD and * client. */ stat = create_restore_bootstrap_file(jcr); if (stat < 0) { /* error */ return false; } else if (stat == 0) { /* No files, nothing to do */ verify_cleanup(jcr, JS_Terminated); /* clean up */ return true; /* get out */ } } else { jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */ } /* Pass the original fileset to the client */ if (jcr->getJobLevel() == L_VERIFY_DATA) { FILESET_DBR fdbr; memset(&fdbr, 0, sizeof(fdbr)); fdbr.FileSetId = jcr->previous_jr.FileSetId; if (!db_get_fileset_record(jcr, jcr->db, &fdbr)) { Jmsg(jcr, M_FATAL, 0, _("Could not get fileset record from previous Job. ERR=%s"), db_strerror(jcr->db)); return false; } jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fdbr.FileSet); if (!jcr->fileset) { if (jcr->verify_job) { jcr->fileset = jcr->verify_job->fileset; Jmsg(jcr, M_WARNING, 0, _("Could not find FileSet resource \"%s\" from previous Job\n"), fdbr.FileSet); Jmsg(jcr, M_INFO, 0, _("Using FileSet \"%\"\n"), jcr->fileset->name()); } else { Jmsg(jcr, M_FATAL, 0, _("Could not get FileSet resource for verify Job.")); return false; } } Dmsg1(50, "FileSet = %s\n", jcr->fileset->name()); } /* Pass the current fileset to the client */ if (jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG && jcr->verify_job) { jcr->fileset = jcr->verify_job->fileset; } Dmsg2(100, "ClientId=%u JobLevel=%c\n", jcr->previous_jr.ClientId, jcr->getJobLevel()); if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); return false; } /* Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start Verify JobId=%s Level=%s Job=%s\n"), edit_uint64(jcr->JobId, ed1), level_to_str(jcr->getJobLevel()), jcr->Job); if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) { /* * Start conversation with Storage daemon */ jcr->setJobStatus(JS_Blocked); if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) { return false; } /* * Now start a job with the Storage daemon */ if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) { return false; } sd = jcr->store_bsock; jcr->sd_calls_client = jcr->client->sd_calls_client; /* * Send the bootstrap file -- what Volumes/files to restore */ if (!send_bootstrap_file(jcr, sd) || !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) { goto bail_out; } if (!jcr->sd_calls_client) { if (!run_storage_and_start_message_thread(jcr, sd)) { return false; } } } /* * OK, now connect to the File daemon * and ask him for the files. */ jcr->setJobStatus(JS_Blocked); if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) { goto bail_out; } jcr->setJobStatus(JS_Running); fd = jcr->file_bsock; Dmsg0(30, ">filed: Send include list\n"); if (!send_include_list(jcr)) { goto bail_out; } Dmsg0(30, ">filed: Send exclude list\n"); if (!send_exclude_list(jcr)) { goto bail_out; } /* * Send Level command to File daemon, as well * as the Storage address if appropriate. */ switch (jcr->getJobLevel()) { case L_VERIFY_INIT: level = "init"; break; case L_VERIFY_CATALOG: level = "catalog"; break; case L_VERIFY_DATA: case L_VERIFY_VOLUME_TO_CATALOG: if (jcr->sd_calls_client) { if (jcr->FDVersion < 10) { Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n")); goto bail_out; } if (!send_client_addr_to_sd(jcr)) { goto bail_out; } if (!run_storage_and_start_message_thread(jcr, jcr->store_bsock)) { return false; } store_address = jcr->rstore->address; /* dummy */ store_port = 0; /* flag that SD calls FD */ } else { /* * send Storage daemon address to the File daemon */ if (jcr->rstore->SDDport == 0) { jcr->rstore->SDDport = jcr->rstore->SDport; } store_address = get_storage_address(jcr->client, jcr->rstore); store_port = jcr->rstore->SDDport; } if (!send_store_addr_to_fd(jcr, jcr->rstore, store_address, store_port)) { goto bail_out; } if (!jcr->RestoreBootstrap) { Jmsg0(jcr, M_FATAL, 0, _("Deprecated feature ... use bootstrap.\n")); goto bail_out; } if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) { level = "volume"; } else { level = "data"; } break; case L_VERIFY_DISK_TO_CATALOG: level="disk_to_catalog"; break; default: Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), jcr->getJobLevel(), jcr->getJobLevel()); goto bail_out; } if (!send_runscripts_commands(jcr)) { goto bail_out; } /* * Send verify command/level to File daemon */ fd->fsend(verifycmd, level); if (!response(jcr, fd, OKverify, "Verify", DISPLAY_ERROR)) { goto bail_out; } /* * Now get data back from File daemon and * compare it to the catalog or store it in the * catalog depending on the run type. */ /* Compare to catalog */ switch (jcr->getJobLevel()) { case L_VERIFY_CATALOG: Dmsg0(10, "Verify level=catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_VOLUME_TO_CATALOG: Dmsg0(10, "Verify level=volume\n"); get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_DISK_TO_CATALOG: Dmsg0(10, "Verify level=disk_to_catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_INIT: /* Build catalog */ Dmsg0(10, "Verify level=init\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_put_in_catalog(jcr); db_end_transaction(jcr, jcr->db); /* terminate any open transaction */ db_write_batch_file_records(jcr); break; case L_VERIFY_DATA: /* Nothing special to do */ bget_dirmsg(fd); /* eat EOD */ break; default: Jmsg1(jcr, M_FATAL, 0, _("Unimplemented verify level %d\n"), jcr->getJobLevel()); goto bail_out; } stat = wait_for_job_termination(jcr); verify_cleanup(jcr, stat); return true; bail_out: return false; }