1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2001-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2016 Planets Communications B.V.
6    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Kern E. Sibbald, December 2001
25  */
26 /**
27  * @file
28  * Program to scan a Bareos Volume and compare it with
29  * the catalog and optionally synchronize the catalog
30  * with the tape.
31  */
32 
33 #include "include/bareos.h"
34 #include "stored/stored.h"
35 #include "stored/stored_globals.h"
36 #include "stored/jcr_private.h"
37 #include "lib/crypto_cache.h"
38 #include "findlib/find.h"
39 #include "cats/cats.h"
40 #include "cats/cats_backends.h"
41 #include "cats/sql.h"
42 #include "stored/acquire.h"
43 #include "stored/butil.h"
44 #include "stored/label.h"
45 #include "stored/mount.h"
46 #include "stored/read_record.h"
47 #include "lib/attribs.h"
48 #include "lib/edit.h"
49 #include "lib/parse_bsr.h"
50 #include "lib/bsignal.h"
51 #include "include/jcr.h"
52 #include "lib/bsock.h"
53 #include "lib/parse_conf.h"
54 #include "lib/util.h"
55 
56 /* Dummy functions */
57 namespace storagedaemon {
58 extern bool ParseSdConfig(const char* configfile, int exit_code);
59 }
60 
61 using namespace storagedaemon;
62 
63 /* Forward referenced functions */
64 static void do_scan(void);
65 static bool RecordCb(DeviceControlRecord* dcr, DeviceRecord* rec);
66 static bool CreateFileAttributesRecord(BareosDb* db,
67                                        JobControlRecord* mjcr,
68                                        char* fname,
69                                        char* lname,
70                                        int type,
71                                        char* ap,
72                                        DeviceRecord* rec);
73 static bool CreateMediaRecord(BareosDb* db,
74                               MediaDbRecord* mr,
75                               Volume_Label* vl);
76 static bool UpdateMediaRecord(BareosDb* db, MediaDbRecord* mr);
77 static bool CreatePoolRecord(BareosDb* db, PoolDbRecord* pr);
78 static JobControlRecord* CreateJobRecord(BareosDb* db,
79                                          JobDbRecord* mr,
80                                          Session_Label* label,
81                                          DeviceRecord* rec);
82 static bool UpdateJobRecord(BareosDb* db,
83                             JobDbRecord* mr,
84                             Session_Label* elabel,
85                             DeviceRecord* rec);
86 static bool CreateClientRecord(BareosDb* db, ClientDbRecord* cr);
87 static bool CreateFilesetRecord(BareosDb* db, FileSetDbRecord* fsr);
88 static bool CreateJobmediaRecord(BareosDb* db, JobControlRecord* jcr);
89 static JobControlRecord* create_jcr(JobDbRecord* jr,
90                                     DeviceRecord* rec,
91                                     uint32_t JobId);
92 static bool UpdateDigestRecord(BareosDb* db,
93                                char* digest,
94                                DeviceRecord* rec,
95                                int type);
96 
97 /* Local variables */
98 static Device* dev = NULL;
99 static BareosDb* db;
100 static JobControlRecord* bjcr; /* jcr for bscan */
101 static BootStrapRecord* bsr = NULL;
102 static MediaDbRecord mr;
103 static PoolDbRecord pr;
104 static JobDbRecord jr;
105 static ClientDbRecord cr;
106 static FileSetDbRecord fsr;
107 static RestoreObjectDbRecord rop;
108 static AttributesDbRecord ar;
109 static FileDbRecord fr;
110 static Session_Label label;
111 static Session_Label elabel;
112 static Attributes* attr;
113 
114 static time_t lasttime = 0;
115 
116 static const char* backend_directory = _PATH_BAREOS_BACKENDDIR;
117 static const char* db_driver = "NULL";
118 static const char* db_name = "bareos";
119 static const char* db_user = "bareos";
120 static const char* db_password = "";
121 static const char* db_host = NULL;
122 static int db_port = 0;
123 static const char* wd = NULL;
124 static bool update_db = false;
125 static bool update_vol_info = false;
126 static bool list_records = false;
127 static int ignored_msgs = 0;
128 
129 static uint64_t currentVolumeSize;
130 static int last_pct = -1;
131 static bool showProgress = false;
132 static int num_jobs = 0;
133 static int num_pools = 0;
134 static int num_media = 0;
135 static int num_files = 0;
136 static int num_restoreobjects = 0;
137 
usage()138 static void usage()
139 {
140   kBareosVersionStrings.PrintCopyrightWithFsfAndPlanets(stderr, 2001);
141   fprintf(
142       stderr,
143       _("Usage: bscan [ options ] <device-name>\n"
144         "       -B <drivername>   specify the database driver name (default "
145         "NULL) <postgresql|mysql|sqlite3>\n"
146         "       -b <bootstrap>    specify a bootstrap file\n"
147         "       -c <path>         specify a Storage configuration file or "
148         "directory\n"
149         "       -d <nnn>          set debug level to <nnn>\n"
150         "       -dt               print timestamp in debug output\n"
151         "       -m                update media info in database\n"
152         "       -D <director>     specify a director name specified in the "
153         "storage daemon\n"
154         "                         configuration file for the Key Encryption "
155         "Key selection\n"
156         "       -a <directory>    specify the database backend directory "
157         "(default %s)\n"
158         "       -n <name>         specify the database name (default bareos)\n"
159         "       -u <user>         specify database user name (default bareos)\n"
160         "       -P <password>     specify database password (default none)\n"
161         "       -h <host>         specify database host (default NULL)\n"
162         "       -t <port>         specify database port (default 0)\n"
163         "       -p                proceed inspite of I/O errors\n"
164         "       -r                list records\n"
165         "       -s                synchronize or store in database\n"
166         "       -S                show scan progress periodically\n"
167         "       -v                verbose\n"
168         "       -V <Volumes>      specify Volume names (separated by |)\n"
169         "       -w <directory>    specify working directory (default from "
170         "configuration file)\n"
171         "       -?                print this message\n\n"
172         "example:\n"
173         "bscan -B postgresql -V Full-0001 FileStorage\n"),
174       backend_directory);
175   exit(1);
176 }
177 
main(int argc,char * argv[])178 int main(int argc, char* argv[])
179 {
180   int ch;
181   struct stat stat_buf;
182   char* VolumeName = NULL;
183   char* DirectorName = NULL;
184   DirectorResource* director = NULL;
185   DeviceControlRecord* dcr;
186 #if defined(HAVE_DYNAMIC_CATS_BACKENDS)
187   std::vector<std::string> backend_directories;
188 #endif
189 
190   setlocale(LC_ALL, "");
191   tzset();
192   bindtextdomain("bareos", LOCALEDIR);
193   textdomain("bareos");
194   InitStackDump();
195 
196   MyNameIs(argc, argv, "bscan");
197   InitMsg(NULL, NULL);
198 
199   OSDependentInit();
200 
201   while ((ch = getopt(argc, argv, "a:B:b:c:d:D:h:p:mn:pP:q:rsSt:u:vV:w:?")) !=
202          -1) {
203     switch (ch) {
204       case 'a':
205         backend_directory = optarg;
206         break;
207 
208       case 'B':
209         db_driver = optarg;
210         break;
211 
212       case 'b':
213         bsr = libbareos::parse_bsr(NULL, optarg);
214         break;
215 
216       case 'c': /* specify config file */
217         if (configfile != NULL) { free(configfile); }
218         configfile = strdup(optarg);
219         break;
220 
221       case 'D': /* specify director name */
222         if (DirectorName != NULL) { free(DirectorName); }
223         DirectorName = strdup(optarg);
224         break;
225 
226       case 'd': /* debug level */
227         if (*optarg == 't') {
228           dbg_timestamp = true;
229         } else {
230           debug_level = atoi(optarg);
231           if (debug_level <= 0) { debug_level = 1; }
232         }
233         break;
234 
235       case 'h':
236         db_host = optarg;
237         break;
238 
239       case 't':
240         db_port = atoi(optarg);
241         break;
242 
243       case 'm':
244         update_vol_info = true;
245         break;
246 
247       case 'n':
248         db_name = optarg;
249         break;
250 
251       case 'P':
252         db_password = optarg;
253         break;
254 
255       case 'p':
256         forge_on = true;
257         break;
258 
259       case 'r':
260         list_records = true;
261         break;
262 
263       case 'S':
264         showProgress = true;
265         break;
266 
267       case 's':
268         update_db = true;
269         break;
270 
271       case 'u':
272         db_user = optarg;
273         break;
274 
275       case 'V': /* Volume name */
276         VolumeName = optarg;
277         break;
278 
279       case 'v':
280         verbose++;
281         break;
282 
283       case 'w':
284         wd = optarg;
285         break;
286 
287       case '?':
288       default:
289         usage();
290     }
291   }
292   argc -= optind;
293   argv += optind;
294 
295   if (argc != 1) {
296     Pmsg0(0, _("Wrong number of arguments: \n"));
297     usage();
298   }
299 
300   my_config = InitSdConfig(configfile, M_ERROR_TERM);
301   ParseSdConfig(configfile, M_ERROR_TERM);
302 
303   if (DirectorName) {
304     foreach_res (director, R_DIRECTOR) {
305       if (bstrcmp(director->resource_name_, DirectorName)) { break; }
306     }
307     if (!director) {
308       Emsg2(
309           M_ERROR_TERM, 0,
310           _("No Director resource named %s defined in %s. Cannot continue.\n"),
311           DirectorName, configfile);
312     }
313   }
314 
315   LoadSdPlugins(me->plugin_directory, me->plugin_names);
316 
317   ReadCryptoCache(me->working_directory, "bareos-sd",
318                   GetFirstPortHostOrder(me->SDaddrs));
319 
320   /* Check if -w option given, otherwise use resource for working directory */
321   if (wd) {
322     working_directory = wd;
323   } else if (!me->working_directory) {
324     Emsg1(M_ERROR_TERM, 0,
325           _("No Working Directory defined in %s. Cannot continue.\n"),
326           configfile);
327   } else {
328     working_directory = me->working_directory;
329   }
330 
331   /* Check that working directory is good */
332   if (stat(working_directory, &stat_buf) != 0) {
333     Emsg1(M_ERROR_TERM, 0,
334           _("Working Directory: %s not found. Cannot continue.\n"),
335           working_directory);
336   }
337   if (!S_ISDIR(stat_buf.st_mode)) {
338     Emsg1(M_ERROR_TERM, 0,
339           _("Working Directory: %s is not a directory. Cannot continue.\n"),
340           working_directory);
341   }
342 
343   dcr = new DeviceControlRecord;
344   bjcr = SetupJcr("bscan", argv[0], bsr, director, dcr, VolumeName, true);
345   if (!bjcr) { exit(1); }
346   dev = bjcr->impl->read_dcr->dev;
347 
348   if (showProgress) {
349     char ed1[50];
350     struct stat sb;
351     fstat(dev->fd(), &sb);
352     currentVolumeSize = sb.st_size;
353     Pmsg1(000, _("First Volume Size = %s\n"),
354           edit_uint64(currentVolumeSize, ed1));
355   }
356 
357 #if defined(HAVE_DYNAMIC_CATS_BACKENDS)
358   backend_directories.emplace_back(backend_directory);
359   DbSetBackendDirs(backend_directories);
360 #endif
361 
362   db = db_init_database(NULL, db_driver, db_name, db_user, db_password, db_host,
363                         db_port, NULL, false, false, false, false);
364   if (db == NULL) {
365     Emsg0(M_ERROR_TERM, 0, _("Could not init Bareos database\n"));
366   }
367   if (!db->OpenDatabase(NULL)) { Emsg0(M_ERROR_TERM, 0, db->strerror()); }
368   Dmsg0(200, "Database opened\n");
369   if (verbose) {
370     Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
371   }
372 
373   do_scan();
374   if (update_db) {
375     printf(
376         "Records added or updated in the catalog:\n%7d Media\n"
377         "%7d Pool\n%7d Job\n%7d File\n%7d RestoreObject\n",
378         num_media, num_pools, num_jobs, num_files, num_restoreobjects);
379   } else {
380     printf(
381         "Records would have been added or updated in the catalog:\n"
382         "%7d Media\n%7d Pool\n%7d Job\n%7d File\n%7d RestoreObject\n",
383         num_media, num_pools, num_jobs, num_files, num_restoreobjects);
384   }
385   DbFlushBackends();
386 
387   CleanDevice(bjcr->impl->dcr);
388   dev->term();
389   FreeDeviceControlRecord(bjcr->impl->dcr);
390   FreeJcr(bjcr);
391 
392   return 0;
393 }
394 
395 /**
396  * We are at the end of reading a tape. Now, we simulate handling
397  * the end of writing a tape by wiffling through the attached
398  * jcrs creating jobmedia records.
399  */
BscanMountNextReadVolume(DeviceControlRecord * dcr)400 static bool BscanMountNextReadVolume(DeviceControlRecord* dcr)
401 {
402   bool status;
403   Device* dev = dcr->dev;
404   DeviceControlRecord* mdcr;
405 
406   Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->getVolCatName());
407   foreach_dlist (mdcr, dev->attached_dcrs) {
408     JobControlRecord* mjcr = mdcr->jcr;
409     Dmsg1(000, "========== JobId=%u ========\n", mjcr->JobId);
410     if (mjcr->JobId == 0) { continue; }
411     if (verbose) { Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job); }
412     mdcr->StartBlock = dcr->StartBlock;
413     mdcr->StartFile = dcr->StartFile;
414     mdcr->EndBlock = dcr->EndBlock;
415     mdcr->EndFile = dcr->EndFile;
416     mdcr->VolMediaId = dcr->VolMediaId;
417     mjcr->impl->read_dcr->VolLastIndex = dcr->VolLastIndex;
418     if (mjcr->impl->insert_jobmedia_records) {
419       if (!CreateJobmediaRecord(db, mjcr)) {
420         Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
421               dev->getVolCatName(), mjcr->Job);
422       }
423     }
424   }
425 
426   UpdateMediaRecord(db, &mr);
427 
428   /* Now let common read routine get up next tape. Note,
429    * we call mount_next... with bscan's jcr because that is where we
430    * have the Volume list, but we get attached.
431    */
432   status = MountNextReadVolume(dcr);
433   if (showProgress) {
434     char ed1[50];
435     struct stat sb;
436     fstat(dev->fd(), &sb);
437     currentVolumeSize = sb.st_size;
438     Pmsg1(000, _("First Volume Size = %s\n"),
439           edit_uint64(currentVolumeSize, ed1));
440   }
441   return status;
442 }
443 
do_scan()444 static void do_scan()
445 {
446   attr = new_attr(bjcr);
447 
448   AttributesDbRecord ar_emtpy;
449   PoolDbRecord pr_empty;
450   JobDbRecord jr_empty;
451   ClientDbRecord cr_empty;
452   FileSetDbRecord fsr_empty;
453   FileDbRecord fr_empty;
454 
455   ar = ar_emtpy;
456   pr = pr_empty;
457   jr = jr_empty;
458   cr = cr_empty;
459   fsr = fsr_empty;
460   fr = fr_empty;
461 
462   /*
463    * Detach bscan's jcr as we are not a real Job on the tape
464    */
465   ReadRecords(bjcr->impl->read_dcr, RecordCb, BscanMountNextReadVolume);
466 
467   if (update_db) {
468     db->WriteBatchFileRecords(bjcr); /* used by bulk batch file insert */
469   }
470 
471   FreeAttr(attr);
472 }
473 
474 /**
475  * Returns: true  if OK
476  *          false if error
477  */
UnpackRestoreObject(JobControlRecord * jcr,int32_t stream,char * rec,int32_t reclen,RestoreObjectDbRecord * rop)478 static inline bool UnpackRestoreObject(JobControlRecord* jcr,
479                                        int32_t stream,
480                                        char* rec,
481                                        int32_t reclen,
482                                        RestoreObjectDbRecord* rop)
483 {
484   char* bp;
485   uint32_t JobFiles;
486 
487   if (sscanf(rec, "%d %d %d %d %d %d ", &JobFiles, &rop->Stream,
488              &rop->object_index, &rop->object_len, &rop->object_full_len,
489              &rop->object_compression) != 6) {
490     return false;
491   }
492 
493   /*
494    * Skip over the first 6 fields we scanned in the previous scan.
495    */
496   bp = rec;
497   for (int i = 0; i < 6; i++) {
498     bp = strchr(bp, ' ');
499     if (bp) {
500       bp++;
501     } else {
502       return false;
503     }
504   }
505 
506   rop->plugin_name = bp;
507   rop->object_name = rop->plugin_name + strlen(rop->plugin_name) + 1;
508   rop->object = rop->object_name + strlen(rop->object_name) + 1;
509 
510   return true;
511 }
512 
513 /**
514  * Returns: true  if OK
515  *          false if error
516  */
RecordCb(DeviceControlRecord * dcr,DeviceRecord * rec)517 static bool RecordCb(DeviceControlRecord* dcr, DeviceRecord* rec)
518 {
519   JobControlRecord* mjcr;
520   char ec1[30];
521   Device* dev = dcr->dev;
522   JobControlRecord* bjcr = dcr->jcr;
523   DeviceBlock* block = dcr->block;
524   PoolMem sql_buffer;
525   db_int64_ctx jmr_count;
526   char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
527 
528   if (rec->data_len > 0) {
529     mr.VolBytes +=
530         rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
531     if (showProgress && currentVolumeSize > 0) {
532       int pct = (mr.VolBytes * 100) / currentVolumeSize;
533       if (pct != last_pct) {
534         fprintf(stdout, _("done: %d%%\n"), pct);
535         fflush(stdout);
536         last_pct = pct;
537       }
538     }
539   }
540 
541   if (list_records) {
542     Pmsg5(000,
543           _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
544           rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, rec->Stream,
545           rec->data_len);
546   }
547 
548   /*
549    * Check for Start or End of Session Record
550    */
551   if (rec->FileIndex < 0) {
552     bool save_update_db = update_db;
553 
554     if (verbose > 1) { DumpLabelRecord(dev, rec, true); }
555     switch (rec->FileIndex) {
556       case PRE_LABEL:
557         Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
558         return false;
559         break;
560 
561       case VOL_LABEL:
562         UnserVolumeLabel(dev, rec);
563         /*
564          * Check Pool info
565          */
566         bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
567         bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
568         num_pools++;
569         if (db->GetPoolRecord(bjcr, &pr)) {
570           if (verbose) {
571             Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
572           }
573         } else {
574           if (!update_db) {
575             Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
576                   pr.Name);
577           }
578           CreatePoolRecord(db, &pr);
579         }
580         if (!bstrcmp(pr.PoolType, dev->VolHdr.PoolType)) {
581           Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
582                 pr.PoolType, dev->VolHdr.PoolType);
583           return true;
584         } else if (verbose) {
585           Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
586         }
587 
588         /*
589          * Check Media Info
590          */
591         mr = MediaDbRecord{};
592         bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
593         mr.PoolId = pr.PoolId;
594         num_media++;
595         if (db->GetMediaRecord(bjcr, &mr)) {
596           if (verbose) {
597             Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
598           }
599           /*
600            * Clear out some volume statistics that will be updated
601            */
602           mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
603           mr.VolBytes = rec->data_len + 20;
604         } else {
605           if (!update_db) {
606             Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
607                   mr.VolumeName);
608           }
609           bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
610           CreateMediaRecord(db, &mr, &dev->VolHdr);
611         }
612         if (!bstrcmp(mr.MediaType, dev->VolHdr.MediaType)) {
613           Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
614                 mr.MediaType, dev->VolHdr.MediaType);
615           return true; /* ignore error */
616         } else if (verbose) {
617           Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
618         }
619 
620         /*
621          * Reset some DeviceControlRecord variables
622          */
623         foreach_dlist (dcr, dev->attached_dcrs) {
624           dcr->VolFirstIndex = dcr->FileIndex = 0;
625           dcr->StartBlock = dcr->EndBlock = 0;
626           dcr->StartFile = dcr->EndFile = 0;
627           dcr->VolMediaId = 0;
628         }
629 
630         Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
631         break;
632 
633       case SOS_LABEL:
634         if (bsr && rec->match_stat < 1) {
635           /*
636            * Skipping record, because does not match BootStrapRecord filter
637            */
638           Dmsg0(200, _("SOS_LABEL skipped. Record does not match "
639                        "BootStrapRecord filter.\n"));
640         } else {
641           mr.VolJobs++;
642           num_jobs++;
643           if (ignored_msgs > 0) {
644             Pmsg1(000,
645                   _("%d \"errors\" ignored before first Start of Session "
646                     "record.\n"),
647                   ignored_msgs);
648             ignored_msgs = 0;
649           }
650           UnserSessionLabel(&label, rec);
651           jr = JobDbRecord{};
652           bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
653           if (db->GetJobRecord(bjcr, &jr)) {
654             /*
655              * Job record already exists in DB
656              */
657             update_db = false; /* don't change db in CreateJobRecord */
658             if (verbose) {
659               Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"),
660                     jr.JobId);
661             }
662           } else {
663             /*
664              * Must create a Job record in DB
665              */
666             if (!update_db) {
667               Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
668                     jr.JobId);
669             }
670           }
671 
672           /*
673            * Create Client record if not already there
674            */
675           bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
676           CreateClientRecord(db, &cr);
677           jr.ClientId = cr.ClientId;
678 
679           /*
680            * Process label, if Job record exists don't update db
681            */
682           mjcr = CreateJobRecord(db, &jr, &label, rec);
683           dcr = mjcr->impl->read_dcr;
684           update_db = save_update_db;
685 
686           jr.PoolId = pr.PoolId;
687           mjcr->start_time = jr.StartTime;
688           mjcr->setJobLevel(jr.JobLevel);
689 
690           mjcr->client_name = GetPoolMemory(PM_FNAME);
691           PmStrcpy(mjcr->client_name, label.ClientName);
692           mjcr->impl->fileset_name = GetPoolMemory(PM_FNAME);
693           PmStrcpy(mjcr->impl->fileset_name, label.FileSetName);
694           bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
695           bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
696 
697           /*
698            * Look for existing Job Media records for this job.  If there are
699            * any, no new ones need be created.  This may occur if File
700            * Retention has expired before Job Retention, or if the volume
701            * has already been bscan'd
702            */
703           Mmsg(sql_buffer, "SELECT count(*) from JobMedia where JobId=%d",
704                jr.JobId);
705           db->SqlQuery(sql_buffer.c_str(), db_int64_handler, &jmr_count);
706           if (jmr_count.value > 0) {
707             mjcr->impl->insert_jobmedia_records = false;
708           } else {
709             mjcr->impl->insert_jobmedia_records = true;
710           }
711 
712           if (rec->VolSessionId != jr.VolSessionId) {
713             Pmsg3(
714                 000,
715                 _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
716                 jr.JobId, jr.VolSessionId, rec->VolSessionId);
717             return true; /* ignore error */
718           }
719           if (rec->VolSessionTime != jr.VolSessionTime) {
720             Pmsg3(000,
721                   _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d "
722                     "Vol=%d\n"),
723                   jr.JobId, jr.VolSessionTime, rec->VolSessionTime);
724             return true; /* ignore error */
725           }
726           if (jr.PoolId != pr.PoolId) {
727             Pmsg3(000,
728                   _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
729                   jr.JobId, jr.PoolId, pr.PoolId);
730             return true; /* ignore error */
731           }
732         }
733         break;
734 
735       case EOS_LABEL:
736         if (bsr && rec->match_stat < 1) {
737           /*
738            * Skipping record, because does not match BootStrapRecord filter
739            */
740           Dmsg0(200, _("EOS_LABEL skipped. Record does not match "
741                        "BootStrapRecord filter.\n"));
742         } else {
743           UnserSessionLabel(&elabel, rec);
744 
745           /*
746            * Create FileSet record
747            */
748           bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
749           bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
750           CreateFilesetRecord(db, &fsr);
751           jr.FileSetId = fsr.FileSetId;
752 
753           mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
754           if (!mjcr) {
755             Pmsg2(000,
756                   _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
757                   rec->VolSessionId, rec->VolSessionTime);
758             break;
759           }
760 
761           /*
762            * Do the final update to the Job record
763            */
764           UpdateJobRecord(db, &jr, &elabel, rec);
765 
766           mjcr->end_time = jr.EndTime;
767           mjcr->setJobStatus(JS_Terminated);
768 
769           /*
770            * Create JobMedia record
771            */
772           mjcr->impl->read_dcr->VolLastIndex = dcr->VolLastIndex;
773           if (mjcr->impl->insert_jobmedia_records) {
774             CreateJobmediaRecord(db, mjcr);
775           }
776           FreeDeviceControlRecord(mjcr->impl->read_dcr);
777           FreeJcr(mjcr);
778         }
779         break;
780 
781       case EOM_LABEL:
782         break;
783 
784       case EOT_LABEL: /* end of all tapes */
785         /*
786          * Wiffle through all jobs still open and close them.
787          */
788         if (update_db) {
789           DeviceControlRecord* mdcr;
790           foreach_dlist (mdcr, dev->attached_dcrs) {
791             JobControlRecord* mjcr = mdcr->jcr;
792             if (!mjcr || mjcr->JobId == 0) { continue; }
793             jr.JobId = mjcr->JobId;
794             jr.JobStatus = JS_ErrorTerminated; /* Mark Job as Error Terimined */
795             jr.JobFiles = mjcr->JobFiles;
796             jr.JobBytes = mjcr->JobBytes;
797             jr.VolSessionId = mjcr->VolSessionId;
798             jr.VolSessionTime = mjcr->VolSessionTime;
799             jr.JobTDate = (utime_t)mjcr->start_time;
800             jr.ClientId = mjcr->ClientId;
801             if (!db->UpdateJobEndRecord(bjcr, &jr)) {
802               Pmsg1(0, _("Could not update job record. ERR=%s\n"),
803                     db->strerror());
804             }
805           }
806         }
807         mr.VolFiles = rec->File;
808         mr.VolBlocks = rec->Block;
809         mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
810         mr.VolMounts++;
811         UpdateMediaRecord(db, &mr);
812         Pmsg3(0,
813               _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"),
814               mr.VolFiles, mr.VolBlocks,
815               edit_uint64_with_commas(mr.VolBytes, ec1));
816         break;
817       default:
818         break;
819     } /* end switch */
820     return true;
821   }
822 
823   mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
824   if (!mjcr) {
825     if (mr.VolJobs > 0) {
826       Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
827             rec->VolSessionId, rec->VolSessionTime);
828     } else {
829       ignored_msgs++;
830     }
831     return true;
832   }
833   dcr = mjcr->impl->read_dcr;
834   if (dcr->VolFirstIndex == 0) { dcr->VolFirstIndex = block->FirstIndex; }
835 
836   /*
837    * File Attributes stream
838    */
839   switch (rec->maskedStream) {
840     case STREAM_UNIX_ATTRIBUTES:
841     case STREAM_UNIX_ATTRIBUTES_EX:
842       if (!UnpackAttributesRecord(bjcr, rec->Stream, rec->data, rec->data_len,
843                                   attr)) {
844         Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
845       }
846 
847       if (verbose > 1) {
848         DecodeStat(attr->attr, &attr->statp, sizeof(attr->statp),
849                    &attr->LinkFI);
850         BuildAttrOutputFnames(bjcr, attr);
851         PrintLsOutput(bjcr, attr);
852       }
853       fr.JobId = mjcr->JobId;
854       fr.FileId = 0;
855       num_files++;
856       if (verbose && (num_files & 0x7FFF) == 0) {
857         char ed1[30], ed2[30], ed3[30], ed4[30];
858         Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
859               edit_uint64_with_commas(num_files, ed1),
860               edit_uint64_with_commas(rec->File, ed2),
861               edit_uint64_with_commas(rec->Block, ed3),
862               edit_uint64_with_commas(mr.VolBytes, ed4));
863       }
864       CreateFileAttributesRecord(db, mjcr, attr->fname, attr->lname, attr->type,
865                                  attr->attr, rec);
866       FreeJcr(mjcr);
867       break;
868 
869     case STREAM_RESTORE_OBJECT:
870       if (!UnpackRestoreObject(bjcr, rec->Stream, rec->data, rec->data_len,
871                                &rop)) {
872         Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
873       }
874       rop.FileIndex = mjcr->FileId;
875       rop.JobId = mjcr->JobId;
876 
877 
878       if (update_db) { db->CreateRestoreObjectRecord(mjcr, &rop); }
879 
880       num_restoreobjects++;
881 
882       FreeJcr(mjcr); /* done using JobControlRecord */
883       break;
884 
885     /*
886      * Data streams
887      */
888     case STREAM_WIN32_DATA:
889     case STREAM_FILE_DATA:
890     case STREAM_SPARSE_DATA:
891     case STREAM_MACOS_FORK_DATA:
892     case STREAM_ENCRYPTED_FILE_DATA:
893     case STREAM_ENCRYPTED_WIN32_DATA:
894     case STREAM_ENCRYPTED_MACOS_FORK_DATA:
895       /*
896        * For encrypted stream, this is an approximation.
897        * The data must be decrypted to know the correct length.
898        */
899       mjcr->JobBytes += rec->data_len;
900       if (rec->maskedStream == STREAM_SPARSE_DATA) {
901         mjcr->JobBytes -= sizeof(uint64_t);
902       }
903 
904       FreeJcr(mjcr); /* done using JobControlRecord */
905       break;
906 
907     case STREAM_GZIP_DATA:
908     case STREAM_COMPRESSED_DATA:
909     case STREAM_ENCRYPTED_FILE_GZIP_DATA:
910     case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
911     case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
912     case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
913       /*
914        * Not correct, we should (decrypt and) expand it.
915        */
916       mjcr->JobBytes += rec->data_len;
917       FreeJcr(mjcr);
918       break;
919 
920     case STREAM_SPARSE_GZIP_DATA:
921     case STREAM_SPARSE_COMPRESSED_DATA:
922       mjcr->JobBytes += rec->data_len -
923                         sizeof(uint64_t); /* Not correct, we should expand it */
924       FreeJcr(mjcr);                      /* done using JobControlRecord */
925       break;
926 
927     /*
928      * Win32 GZIP stream
929      */
930     case STREAM_WIN32_GZIP_DATA:
931     case STREAM_WIN32_COMPRESSED_DATA:
932       mjcr->JobBytes += rec->data_len;
933       FreeJcr(mjcr); /* done using JobControlRecord */
934       break;
935 
936     case STREAM_MD5_DIGEST:
937       BinToBase64(digest, sizeof(digest), (char*)rec->data,
938                   CRYPTO_DIGEST_MD5_SIZE, true);
939       if (verbose > 1) { Pmsg1(000, _("Got MD5 record: %s\n"), digest); }
940       UpdateDigestRecord(db, digest, rec, CRYPTO_DIGEST_MD5);
941       break;
942 
943     case STREAM_SHA1_DIGEST:
944       BinToBase64(digest, sizeof(digest), (char*)rec->data,
945                   CRYPTO_DIGEST_SHA1_SIZE, true);
946       if (verbose > 1) { Pmsg1(000, _("Got SHA1 record: %s\n"), digest); }
947       UpdateDigestRecord(db, digest, rec, CRYPTO_DIGEST_SHA1);
948       break;
949 
950     case STREAM_SHA256_DIGEST:
951       BinToBase64(digest, sizeof(digest), (char*)rec->data,
952                   CRYPTO_DIGEST_SHA256_SIZE, true);
953       if (verbose > 1) { Pmsg1(000, _("Got SHA256 record: %s\n"), digest); }
954       UpdateDigestRecord(db, digest, rec, CRYPTO_DIGEST_SHA256);
955       break;
956 
957     case STREAM_SHA512_DIGEST:
958       BinToBase64(digest, sizeof(digest), (char*)rec->data,
959                   CRYPTO_DIGEST_SHA512_SIZE, true);
960       if (verbose > 1) { Pmsg1(000, _("Got SHA512 record: %s\n"), digest); }
961       UpdateDigestRecord(db, digest, rec, CRYPTO_DIGEST_SHA512);
962       break;
963 
964     case STREAM_ENCRYPTED_SESSION_DATA:
965       // TODO landonf: Investigate crypto support in bscan
966       if (verbose > 1) { Pmsg0(000, _("Got signed digest record\n")); }
967       break;
968 
969     case STREAM_SIGNED_DIGEST:
970       // TODO landonf: Investigate crypto support in bscan
971       if (verbose > 1) { Pmsg0(000, _("Got signed digest record\n")); }
972       break;
973 
974     case STREAM_PROGRAM_NAMES:
975       if (verbose) { Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data); }
976       break;
977 
978     case STREAM_PROGRAM_DATA:
979       if (verbose > 1) { Pmsg0(000, _("Got Prog Data Stream record.\n")); }
980       break;
981 
982     case STREAM_HFSPLUS_ATTRIBUTES:
983       /* Ignore OSX attributes */
984       break;
985 
986     case STREAM_PLUGIN_NAME:
987     case STREAM_PLUGIN_DATA:
988       /* Ignore plugin data */
989       break;
990 
991     case STREAM_UNIX_ACCESS_ACL:  /* Deprecated Standard ACL attributes on UNIX
992                                    */
993     case STREAM_UNIX_DEFAULT_ACL: /* Deprecated Default ACL attributes on UNIX
994                                    */
995     case STREAM_ACL_AIX_TEXT:
996     case STREAM_ACL_DARWIN_ACCESS_ACL:
997     case STREAM_ACL_FREEBSD_DEFAULT_ACL:
998     case STREAM_ACL_FREEBSD_ACCESS_ACL:
999     case STREAM_ACL_HPUX_ACL_ENTRY:
1000     case STREAM_ACL_IRIX_DEFAULT_ACL:
1001     case STREAM_ACL_IRIX_ACCESS_ACL:
1002     case STREAM_ACL_LINUX_DEFAULT_ACL:
1003     case STREAM_ACL_LINUX_ACCESS_ACL:
1004     case STREAM_ACL_TRU64_DEFAULT_ACL:
1005     case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
1006     case STREAM_ACL_TRU64_ACCESS_ACL:
1007     case STREAM_ACL_SOLARIS_ACLENT:
1008     case STREAM_ACL_SOLARIS_ACE:
1009     case STREAM_ACL_AFS_TEXT:
1010     case STREAM_ACL_AIX_AIXC:
1011     case STREAM_ACL_AIX_NFS4:
1012     case STREAM_ACL_FREEBSD_NFS4_ACL:
1013     case STREAM_ACL_HURD_DEFAULT_ACL:
1014     case STREAM_ACL_HURD_ACCESS_ACL:
1015     case STREAM_ACL_PLUGIN:
1016       /* Ignore Unix ACL attributes */
1017       break;
1018 
1019     case STREAM_XATTR_PLUGIN:
1020     case STREAM_XATTR_HURD:
1021     case STREAM_XATTR_IRIX:
1022     case STREAM_XATTR_TRU64:
1023     case STREAM_XATTR_AIX:
1024     case STREAM_XATTR_OPENBSD:
1025     case STREAM_XATTR_SOLARIS_SYS:
1026     case STREAM_XATTR_SOLARIS:
1027     case STREAM_XATTR_DARWIN:
1028     case STREAM_XATTR_FREEBSD:
1029     case STREAM_XATTR_LINUX:
1030     case STREAM_XATTR_NETBSD:
1031       /* Ignore Unix Extended attributes */
1032       break;
1033 
1034     case STREAM_NDMP_SEPARATOR:
1035       /* Ignore NDMP separators */
1036       break;
1037 
1038     default:
1039       Pmsg2(0, _("Unknown stream type!!! stream=%d len=%i\n"), rec->Stream,
1040             rec->data_len);
1041       break;
1042   }
1043 
1044   return true;
1045 }
1046 
1047 /**
1048  * Free the Job Control Record if no one is still using it.
1049  *  Called from main FreeJcr() routine in src/lib/jcr.c so
1050  *  that we can do our Director specific cleanup of the jcr.
1051  */
BscanFreeJcr(JobControlRecord * jcr)1052 static void BscanFreeJcr(JobControlRecord* jcr)
1053 {
1054   Dmsg0(200, "Start bscan FreeJcr\n");
1055 
1056   if (jcr->file_bsock) {
1057     Dmsg0(200, "Close File bsock\n");
1058     jcr->file_bsock->close();
1059     delete jcr->file_bsock;
1060     jcr->file_bsock = NULL;
1061   }
1062 
1063   if (jcr->store_bsock) {
1064     Dmsg0(200, "Close Store bsock\n");
1065     jcr->store_bsock->close();
1066     delete jcr->store_bsock;
1067     jcr->store_bsock = NULL;
1068   }
1069 
1070   if (jcr->RestoreBootstrap) { free(jcr->RestoreBootstrap); }
1071 
1072   if (jcr->impl->dcr) {
1073     FreeDeviceControlRecord(jcr->impl->dcr);
1074     jcr->impl->dcr = NULL;
1075   }
1076 
1077   if (jcr->impl->read_dcr) {
1078     FreeDeviceControlRecord(jcr->impl->read_dcr);
1079     jcr->impl->read_dcr = NULL;
1080   }
1081 
1082   if (jcr->impl) {
1083     delete jcr->impl;
1084     jcr->impl = nullptr;
1085   }
1086 
1087   Dmsg0(200, "End bscan FreeJcr\n");
1088 }
1089 
1090 /**
1091  * We got a File Attributes record on the tape.  Now, lookup the Job
1092  * record, and then create the attributes record.
1093  */
CreateFileAttributesRecord(BareosDb * db,JobControlRecord * mjcr,char * fname,char * lname,int type,char * ap,DeviceRecord * rec)1094 static bool CreateFileAttributesRecord(BareosDb* db,
1095                                        JobControlRecord* mjcr,
1096                                        char* fname,
1097                                        char* lname,
1098                                        int type,
1099                                        char* ap,
1100                                        DeviceRecord* rec)
1101 {
1102   DeviceControlRecord* dcr = mjcr->impl->read_dcr;
1103   ar.fname = fname;
1104   ar.link = lname;
1105   ar.ClientId = mjcr->ClientId;
1106   ar.JobId = mjcr->JobId;
1107   ar.Stream = rec->Stream;
1108   if (type == FT_DELETED) {
1109     ar.FileIndex = 0;
1110   } else {
1111     ar.FileIndex = rec->FileIndex;
1112   }
1113   ar.attr = ap;
1114   if (dcr->VolFirstIndex == 0) { dcr->VolFirstIndex = rec->FileIndex; }
1115   dcr->FileIndex = rec->FileIndex;
1116   mjcr->JobFiles++;
1117 
1118   if (!update_db) { return true; }
1119 
1120   if (!db->CreateFileAttributesRecord(bjcr, &ar)) {
1121     Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"),
1122           db->strerror());
1123     return false;
1124   }
1125   mjcr->FileId = ar.FileId;
1126 
1127   if (verbose > 1) { Pmsg1(000, _("Created File record: %s\n"), fname); }
1128 
1129   return true;
1130 }
1131 
1132 /**
1133  * For each Volume we see, we create a Medium record
1134  */
CreateMediaRecord(BareosDb * db,MediaDbRecord * mr,Volume_Label * vl)1135 static bool CreateMediaRecord(BareosDb* db, MediaDbRecord* mr, Volume_Label* vl)
1136 {
1137   struct date_time dt;
1138   struct tm tm;
1139 
1140   /*
1141    * We mark Vols as Archive to keep them from being re-written
1142    */
1143   bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
1144   mr->VolRetention = 365 * 3600 * 24; /* 1 year */
1145   mr->Enabled = 1;
1146   if (vl->VerNum >= 11) {
1147     mr->set_first_written = true; /* Save FirstWritten during update_media */
1148     mr->FirstWritten = BtimeToUtime(vl->write_btime);
1149     mr->LabelDate = BtimeToUtime(vl->label_btime);
1150   } else {
1151     /*
1152      * DEPRECATED DO NOT USE
1153      */
1154     dt.julian_day_number = vl->write_date;
1155     dt.julian_day_fraction = vl->write_time;
1156     TmDecode(&dt, &tm);
1157     mr->FirstWritten = mktime(&tm);
1158     dt.julian_day_number = vl->label_date;
1159     dt.julian_day_fraction = vl->label_time;
1160     TmDecode(&dt, &tm);
1161     mr->LabelDate = mktime(&tm);
1162   }
1163   lasttime = mr->LabelDate;
1164 
1165   if (mr->VolJobs == 0) { mr->VolJobs = 1; }
1166 
1167   if (mr->VolMounts == 0) { mr->VolMounts = 1; }
1168 
1169   if (!update_db) { return true; }
1170 
1171   if (!db->CreateMediaRecord(bjcr, mr)) {
1172     Pmsg1(000, _("Could not create media record. ERR=%s\n"), db->strerror());
1173     return false;
1174   }
1175   if (!db->UpdateMediaRecord(bjcr, mr)) {
1176     Pmsg1(000, _("Could not update media record. ERR=%s\n"), db->strerror());
1177     return false;
1178   }
1179   if (verbose) {
1180     Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
1181   }
1182 
1183   return true;
1184 }
1185 
1186 /**
1187  * Called at end of media to update it
1188  */
UpdateMediaRecord(BareosDb * db,MediaDbRecord * mr)1189 static bool UpdateMediaRecord(BareosDb* db, MediaDbRecord* mr)
1190 {
1191   if (!update_db && !update_vol_info) { return true; }
1192 
1193   mr->LastWritten = lasttime;
1194   if (!db->UpdateMediaRecord(bjcr, mr)) {
1195     Pmsg1(000, _("Could not update media record. ERR=%s\n"), db->strerror());
1196     return false;
1197   }
1198 
1199   if (verbose) {
1200     Pmsg1(000, _("Updated Media record at end of Volume: %s\n"),
1201           mr->VolumeName);
1202   }
1203 
1204   return true;
1205 }
1206 
CreatePoolRecord(BareosDb * db,PoolDbRecord * pr)1207 static bool CreatePoolRecord(BareosDb* db, PoolDbRecord* pr)
1208 {
1209   pr->NumVols++;
1210   pr->UseCatalog = 1;
1211   pr->VolRetention = 355 * 3600 * 24; /* 1 year */
1212 
1213   if (!update_db) { return true; }
1214 
1215   if (!db->CreatePoolRecord(bjcr, pr)) {
1216     Pmsg1(000, _("Could not create pool record. ERR=%s\n"), db->strerror());
1217     return false;
1218   }
1219 
1220   if (verbose) {
1221     Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
1222   }
1223 
1224   return true;
1225 }
1226 
1227 /**
1228  * Called from SOS to create a client for the current Job
1229  */
CreateClientRecord(BareosDb * db,ClientDbRecord * cr)1230 static bool CreateClientRecord(BareosDb* db, ClientDbRecord* cr)
1231 {
1232   /*
1233    * Note, update_db can temporarily be set false while
1234    * updating the database, so we must ensure that ClientId is non-zero.
1235    */
1236   if (!update_db) {
1237     cr->ClientId = 0;
1238     if (!db->GetClientRecord(bjcr, cr)) {
1239       Pmsg1(0, _("Could not get Client record. ERR=%s\n"), db->strerror());
1240       return false;
1241     }
1242 
1243     return true;
1244   }
1245 
1246   if (!db->CreateClientRecord(bjcr, cr)) {
1247     Pmsg1(000, _("Could not create Client record. ERR=%s\n"), db->strerror());
1248     return false;
1249   }
1250 
1251   if (verbose) {
1252     Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
1253   }
1254 
1255   return true;
1256 }
1257 
CreateFilesetRecord(BareosDb * db,FileSetDbRecord * fsr)1258 static bool CreateFilesetRecord(BareosDb* db, FileSetDbRecord* fsr)
1259 {
1260   if (!update_db) { return true; }
1261 
1262   fsr->FileSetId = 0;
1263   if (fsr->MD5[0] == 0) {
1264     fsr->MD5[0] = ' '; /* Equivalent to nothing */
1265     fsr->MD5[1] = 0;
1266   }
1267 
1268   if (db->GetFilesetRecord(bjcr, fsr)) {
1269     if (verbose) {
1270       Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
1271     }
1272   } else {
1273     if (!db->CreateFilesetRecord(bjcr, fsr)) {
1274       Pmsg2(000, _("Could not create FileSet record \"%s\". ERR=%s\n"),
1275             fsr->FileSet, db->strerror());
1276       return false;
1277     }
1278 
1279     if (verbose) {
1280       Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
1281     }
1282   }
1283 
1284   return true;
1285 }
1286 
1287 /**
1288  * Simulate the two calls on the database to create the Job record and
1289  * to update it when the Job actually begins running.
1290  */
CreateJobRecord(BareosDb * db,JobDbRecord * jr,Session_Label * label,DeviceRecord * rec)1291 static JobControlRecord* CreateJobRecord(BareosDb* db,
1292                                          JobDbRecord* jr,
1293                                          Session_Label* label,
1294                                          DeviceRecord* rec)
1295 {
1296   JobControlRecord* mjcr;
1297   struct date_time dt;
1298   struct tm tm;
1299 
1300   jr->JobId = label->JobId;
1301   jr->JobType = label->JobType;
1302   jr->JobLevel = label->JobLevel;
1303   jr->JobStatus = JS_Created;
1304   bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1305   bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1306   if (label->VerNum >= 11) {
1307     jr->SchedTime = BtimeToUnix(label->write_btime);
1308   } else {
1309     dt.julian_day_number = label->write_date;
1310     dt.julian_day_fraction = label->write_time;
1311     TmDecode(&dt, &tm);
1312     jr->SchedTime = mktime(&tm);
1313   }
1314 
1315   jr->StartTime = jr->SchedTime;
1316   jr->JobTDate = (utime_t)jr->SchedTime;
1317   jr->VolSessionId = rec->VolSessionId;
1318   jr->VolSessionTime = rec->VolSessionTime;
1319 
1320   /* Now create a JobControlRecord as if starting the Job */
1321   mjcr = create_jcr(jr, rec, label->JobId);
1322 
1323   if (!update_db) { return mjcr; }
1324 
1325   /*
1326    * This creates the bare essentials
1327    */
1328   if (!db->CreateJobRecord(bjcr, jr)) {
1329     Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db->strerror());
1330     return mjcr;
1331   }
1332 
1333   /*
1334    * This adds the client, StartTime, JobTDate, ...
1335    */
1336   if (!db->UpdateJobStartRecord(bjcr, jr)) {
1337     Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db->strerror());
1338     return mjcr;
1339   }
1340 
1341   Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"),
1342         jr->JobId, label->JobId);
1343   mjcr->JobId = jr->JobId; /* set new JobId */
1344 
1345   return mjcr;
1346 }
1347 
1348 /**
1349  * Simulate the database call that updates the Job at Job termination time.
1350  */
UpdateJobRecord(BareosDb * db,JobDbRecord * jr,Session_Label * elabel,DeviceRecord * rec)1351 static bool UpdateJobRecord(BareosDb* db,
1352                             JobDbRecord* jr,
1353                             Session_Label* elabel,
1354                             DeviceRecord* rec)
1355 {
1356   struct date_time dt;
1357   struct tm tm;
1358   JobControlRecord* mjcr;
1359 
1360   mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1361   if (!mjcr) {
1362     Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1363           rec->VolSessionId, rec->VolSessionTime);
1364     return false;
1365   }
1366 
1367   if (elabel->VerNum >= 11) {
1368     jr->EndTime = BtimeToUnix(elabel->write_btime);
1369   } else {
1370     dt.julian_day_number = elabel->write_date;
1371     dt.julian_day_fraction = elabel->write_time;
1372     TmDecode(&dt, &tm);
1373     jr->EndTime = mktime(&tm);
1374   }
1375 
1376   lasttime = jr->EndTime;
1377   mjcr->end_time = jr->EndTime;
1378 
1379   jr->JobId = mjcr->JobId;
1380   jr->JobStatus = elabel->JobStatus;
1381   mjcr->JobStatus = elabel->JobStatus;
1382   jr->JobFiles = elabel->JobFiles;
1383   if (jr->JobFiles > 0) { /* If we found files, force PurgedFiles */
1384     jr->PurgedFiles = 0;
1385   }
1386   jr->JobBytes = elabel->JobBytes;
1387   jr->VolSessionId = rec->VolSessionId;
1388   jr->VolSessionTime = rec->VolSessionTime;
1389   jr->JobTDate = (utime_t)mjcr->start_time;
1390   jr->ClientId = mjcr->ClientId;
1391 
1392   if (!update_db) {
1393     FreeJcr(mjcr);
1394     return true;
1395   }
1396 
1397   if (!db->UpdateJobEndRecord(bjcr, jr)) {
1398     Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,
1399           db->strerror());
1400     FreeJcr(mjcr);
1401     return false;
1402   }
1403 
1404   if (verbose) {
1405     Pmsg3(
1406         000,
1407         _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"),
1408         jr->JobId, job_level_to_str(mjcr->getJobLevel()), jr->JobStatus);
1409   }
1410 
1411   if (verbose > 1) {
1412     const char* TermMsg;
1413     static char term_code[70];
1414     char sdt[50], edt[50];
1415     char ec1[30], ec2[30], ec3[30];
1416 
1417     switch (mjcr->JobStatus) {
1418       case JS_Terminated:
1419         TermMsg = _("Backup OK");
1420         break;
1421       case JS_Warnings:
1422         TermMsg = _("Backup OK -- with warnings");
1423         break;
1424       case JS_FatalError:
1425       case JS_ErrorTerminated:
1426         TermMsg = _("*** Backup Error ***");
1427         break;
1428       case JS_Canceled:
1429         TermMsg = _("Backup Canceled");
1430         break;
1431       default:
1432         TermMsg = term_code;
1433         sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1434         break;
1435     }
1436     bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1437     bstrftime(edt, sizeof(edt), mjcr->end_time);
1438     Pmsg15(000,
1439            _("%s\n"
1440              "JobId:                  %d\n"
1441              "Job:                    %s\n"
1442              "FileSet:                %s\n"
1443              "Backup Level:           %s\n"
1444              "Client:                 %s\n"
1445              "Start time:             %s\n"
1446              "End time:               %s\n"
1447              "Files Written:          %s\n"
1448              "Bytes Written:          %s\n"
1449              "Volume Session Id:      %d\n"
1450              "Volume Session Time:    %d\n"
1451              "Last Volume Bytes:      %s\n"
1452              "Bareos binary info:     %s\n"
1453              "Termination:            %s\n\n"),
1454            edt, mjcr->JobId, mjcr->Job, mjcr->impl->fileset_name,
1455            job_level_to_str(mjcr->getJobLevel()), mjcr->client_name, sdt, edt,
1456            edit_uint64_with_commas(mjcr->JobFiles, ec1),
1457            edit_uint64_with_commas(mjcr->JobBytes, ec2), mjcr->VolSessionId,
1458            mjcr->VolSessionTime, edit_uint64_with_commas(mr.VolBytes, ec3),
1459            kBareosVersionStrings.BinaryInfo, TermMsg);
1460   }
1461   FreeJcr(mjcr);
1462 
1463   return true;
1464 }
1465 
CreateJobmediaRecord(BareosDb * db,JobControlRecord * mjcr)1466 static bool CreateJobmediaRecord(BareosDb* db, JobControlRecord* mjcr)
1467 {
1468   JobMediaDbRecord jmr;
1469   DeviceControlRecord* dcr = mjcr->impl->read_dcr;
1470 
1471   dcr->EndBlock = dev->EndBlock;
1472   dcr->EndFile = dev->EndFile;
1473   dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
1474 
1475   jmr.JobId = mjcr->JobId;
1476   jmr.MediaId = mr.MediaId;
1477   jmr.FirstIndex = dcr->VolFirstIndex;
1478   jmr.LastIndex = dcr->VolLastIndex;
1479   jmr.StartFile = dcr->StartFile;
1480   jmr.EndFile = dcr->EndFile;
1481   jmr.StartBlock = dcr->StartBlock;
1482   jmr.EndBlock = dcr->EndBlock;
1483 
1484   if (!update_db) { return true; }
1485 
1486   if (!db->CreateJobmediaRecord(bjcr, &jmr)) {
1487     Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db->strerror());
1488     return false;
1489   }
1490   if (verbose) {
1491     Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"), jmr.JobId,
1492           jmr.MediaId);
1493   }
1494 
1495   return true;
1496 }
1497 
1498 /**
1499  * Simulate the database call that updates the MD5/SHA1 record
1500  */
UpdateDigestRecord(BareosDb * db,char * digest,DeviceRecord * rec,int type)1501 static bool UpdateDigestRecord(BareosDb* db,
1502                                char* digest,
1503                                DeviceRecord* rec,
1504                                int type)
1505 {
1506   JobControlRecord* mjcr;
1507 
1508   mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1509   if (!mjcr) {
1510     if (mr.VolJobs > 0) {
1511       Pmsg2(000,
1512             _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1513             rec->VolSessionId, rec->VolSessionTime);
1514     } else {
1515       ignored_msgs++;
1516     }
1517     return false;
1518   }
1519 
1520   if (!update_db || mjcr->FileId == 0) {
1521     FreeJcr(mjcr);
1522     return true;
1523   }
1524 
1525   if (!db->AddDigestToFileRecord(bjcr, mjcr->FileId, digest, type)) {
1526     Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"),
1527           db->strerror());
1528     FreeJcr(mjcr);
1529     return false;
1530   }
1531 
1532   if (verbose > 1) { Pmsg0(000, _("Updated MD5/SHA1 record\n")); }
1533   FreeJcr(mjcr);
1534 
1535   return true;
1536 }
1537 
1538 /**
1539  * Create a JobControlRecord as if we are really starting the job
1540  */
create_jcr(JobDbRecord * jr,DeviceRecord * rec,uint32_t JobId)1541 static JobControlRecord* create_jcr(JobDbRecord* jr,
1542                                     DeviceRecord* rec,
1543                                     uint32_t JobId)
1544 {
1545   JobControlRecord* jobjcr;
1546   /*
1547    * Transfer as much as possible to the Job JobControlRecord. Most important is
1548    *   the JobId and the ClientId.
1549    */
1550   jobjcr = new_jcr(BscanFreeJcr);
1551   jobjcr->impl = new JobControlRecordPrivate;
1552   jobjcr->setJobType(jr->JobType);
1553   jobjcr->setJobLevel(jr->JobLevel);
1554   jobjcr->JobStatus = jr->JobStatus;
1555   bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1556   jobjcr->JobId = JobId; /* this is JobId on tape */
1557   jobjcr->sched_time = jr->SchedTime;
1558   jobjcr->start_time = jr->StartTime;
1559   jobjcr->VolSessionId = rec->VolSessionId;
1560   jobjcr->VolSessionTime = rec->VolSessionTime;
1561   jobjcr->ClientId = jr->ClientId;
1562   jobjcr->impl->dcr = jobjcr->impl->read_dcr = new DeviceControlRecord;
1563   SetupNewDcrDevice(jobjcr, jobjcr->impl->dcr, dev, NULL);
1564 
1565   return jobjcr;
1566 }
1567