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