1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2016 Planets Communications B.V.
6    Copyright (C) 2013-2017 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 Sibbald, March 2000
25  */
26 /**
27  * @file
28  * BAREOS Catalog Database Get record interface routines
29  *
30  * Note, these routines generally get a record by id or
31  * by name.  If more logic is involved, the routine
32  * should be in find.c
33  */
34 
35 #include "include/bareos.h"
36 
37 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
38 
39 #include "cats.h"
40 #include "sql.h"
41 #include "lib/edit.h"
42 #include "lib/volume_session_info.h"
43 
44 /* -----------------------------------------------------------------------
45  *
46  *   Generic Routines (or almost generic)
47  *
48  * -----------------------------------------------------------------------
49  */
50 
51 /* Forward referenced functions */
52 
53 /**
54  * Given a full filename (with path), look up the File record
55  * (with attributes) in the database.
56  *
57  *  Returns: 0 on failure
58  *           1 on success with the File record in FileDbRecord
59  */
GetFileAttributesRecord(JobControlRecord * jcr,char * filename,JobDbRecord * jr,FileDbRecord * fdbr)60 bool BareosDb::GetFileAttributesRecord(JobControlRecord* jcr,
61                                        char* filename,
62                                        JobDbRecord* jr,
63                                        FileDbRecord* fdbr)
64 {
65   bool retval;
66   Dmsg1(100, "db_get_file_attributes_record filename=%s \n", filename);
67 
68   DbLock(this);
69 
70   SplitPathAndFile(jcr, filename);
71   fdbr->PathId = GetPathRecord(jcr);
72   retval = GetFileRecord(jcr, jr, fdbr);
73 
74   DbUnlock(this);
75   return retval;
76 }
77 
78 /**
79  * Get a File record
80  * Returns: false on failure
81  *          true on success
82  *
83  *  DO NOT use Jmsg in this routine.
84  *
85  *  Note in this routine, we do not use Jmsg because it may be
86  *    called to get attributes of a non-existent file, which is
87  *    "normal" if a new file is found during Verify.
88  *
89  *  The following is a bit of a kludge: because we always backup a
90  *    directory entry, we can end up with two copies of the directory
91  *    in the backup. One is when we encounter the directory and find
92  *    we cannot recurse into it, and the other is when we find an
93  *    explicit mention of the directory. This can also happen if the
94  *    use includes the directory twice.  In this case, Verify
95  *    VolumeToCatalog fails because we have two copies in the catalog,
96  *    and only the first one is marked (twice).  So, when calling from Verify,
97  *    VolumeToCatalog jr is not NULL and we know jr->FileIndex is the fileindex
98  *    of the version of the directory/file we actually want and do
99  *    a more explicit SQL search.
100  */
GetFileRecord(JobControlRecord * jcr,JobDbRecord * jr,FileDbRecord * fdbr)101 bool BareosDb::GetFileRecord(JobControlRecord* jcr,
102                              JobDbRecord* jr,
103                              FileDbRecord* fdbr)
104 {
105   bool retval = false;
106   SQL_ROW row;
107   char ed1[50], ed2[50], ed3[50];
108   int num_rows;
109 
110   esc_name = CheckPoolMemorySize(esc_name, 2 * fnl + 2);
111   EscapeString(jcr, esc_name, fname, fnl);
112 
113   if (jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG) {
114     Mmsg(cmd,
115          "SELECT FileId, LStat, MD5, Fhinfo, Fhnode FROM File,Job WHERE "
116          "File.JobId=Job.JobId AND File.PathId=%s AND "
117          "File.Name='%s' AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND "
118          "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
119          edit_int64(fdbr->PathId, ed1), esc_name,
120          edit_int64(jr->ClientId, ed3));
121   } else if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
122     Mmsg(cmd,
123          "SELECT FileId, LStat, MD5, Fhinfo, Fhnode FROM File WHERE "
124          "File.JobId=%s AND File.PathId=%s AND "
125          "File.Name='%s' AND File.FileIndex=%u",
126          edit_int64(fdbr->JobId, ed1), edit_int64(fdbr->PathId, ed2), esc_name,
127          jr->FileIndex);
128   } else {
129     Mmsg(cmd,
130          "SELECT FileId, LStat, MD5, Fhinfo, Fhnode FROM File WHERE "
131          "File.JobId=%s AND File.PathId=%s AND "
132          "File.Name='%s'",
133          edit_int64(fdbr->JobId, ed1), edit_int64(fdbr->PathId, ed2), esc_name);
134   }
135   Dmsg3(450, "Get_file_record JobId=%u Filename=%s PathId=%u\n", fdbr->JobId,
136         esc_name, fdbr->PathId);
137 
138   Dmsg1(100, "Query=%s\n", cmd);
139 
140   if (QUERY_DB(jcr, cmd)) {
141     num_rows = SqlNumRows();
142     Dmsg1(050, "GetFileRecord num_rows=%d\n", num_rows);
143     if (num_rows >= 1) {
144       if ((row = SqlFetchRow()) == NULL) {
145         Mmsg1(errmsg, _("Error fetching row: %s\n"), sql_strerror());
146       } else {
147         fdbr->FileId = (FileId_t)str_to_int64(row[0]);
148         bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
149         bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
150         retval = true;
151         if (num_rows > 1) {
152           Mmsg3(errmsg,
153                 _("GetFileRecord want 1 got rows=%d PathId=%s Filename=%s\n"),
154                 num_rows, edit_int64(fdbr->PathId, ed1), esc_name);
155           Dmsg1(000, "=== Problem!  %s", errmsg);
156         }
157       }
158     } else {
159       Mmsg2(errmsg, _("File record for PathId=%s Filename=%s not found.\n"),
160             edit_int64(fdbr->PathId, ed1), esc_name);
161     }
162     SqlFreeResult();
163   } else {
164     Mmsg(errmsg, _("File record not found in Catalog.\n"));
165   }
166   return retval;
167 }
168 
169 
170 /**
171  * Get path record
172  * Returns: 0 on failure
173  *          PathId on success
174  *
175  *   DO NOT use Jmsg in this routine (see notes for GetFileRecord)
176  */
GetPathRecord(JobControlRecord * jcr)177 int BareosDb::GetPathRecord(JobControlRecord* jcr)
178 {
179   SQL_ROW row;
180   DBId_t PathId = 0;
181   int num_rows;
182 
183   esc_name = CheckPoolMemorySize(esc_name, 2 * pnl + 2);
184   EscapeString(jcr, esc_name, path, pnl);
185 
186   if (cached_path_id != 0 && cached_path_len == pnl &&
187       bstrcmp(cached_path, path)) {
188     return cached_path_id;
189   }
190 
191   Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
192   if (QUERY_DB(jcr, cmd)) {
193     char ed1[30];
194     num_rows = SqlNumRows();
195     if (num_rows > 1) {
196       Mmsg2(errmsg, _("More than one Path!: %s for path: %s\n"),
197             edit_uint64(num_rows, ed1), path);
198       Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
199     }
200     /* Even if there are multiple paths, take the first one */
201     if (num_rows >= 1) {
202       if ((row = SqlFetchRow()) == NULL) {
203         Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
204       } else {
205         PathId = str_to_int64(row[0]);
206         if (PathId <= 0) {
207           Mmsg2(errmsg, _("Get DB path record %s found bad record: %s\n"), cmd,
208                 edit_int64(PathId, ed1));
209           PathId = 0;
210         } else {
211           /*
212            * Cache path
213            */
214           if (PathId != cached_path_id) {
215             cached_path_id = PathId;
216             cached_path_len = pnl;
217             PmStrcpy(cached_path, path);
218           }
219         }
220       }
221     } else {
222       Mmsg1(errmsg, _("Path record: %s not found.\n"), path);
223     }
224     SqlFreeResult();
225   } else {
226     Mmsg(errmsg, _("Path record: %s not found in Catalog.\n"), path);
227   }
228   return PathId;
229 }
230 
GetPathRecord(JobControlRecord * jcr,const char * new_path)231 int BareosDb::GetPathRecord(JobControlRecord* jcr, const char* new_path)
232 {
233   PmStrcpy(path, new_path);
234   pnl = strlen(path);
235   return GetPathRecord(jcr);
236 }
237 
238 /**
239  * Get Job record for given JobId or Job name
240  * Returns: false on failure
241  *          true  on success
242  */
GetJobRecord(JobControlRecord * jcr,JobDbRecord * jr)243 bool BareosDb::GetJobRecord(JobControlRecord* jcr, JobDbRecord* jr)
244 {
245   bool retval = false;
246   SQL_ROW row;
247   char ed1[50];
248   char esc[MAX_ESCAPE_NAME_LENGTH];
249 
250   DbLock(this);
251   if (jr->JobId == 0) {
252     EscapeString(jcr, esc, jr->Job, strlen(jr->Job));
253     Mmsg(cmd,
254          "SELECT VolSessionId,VolSessionTime,"
255          "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
256          "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
257          "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
258          "FROM Job WHERE Job='%s'",
259          esc);
260   } else {
261     Mmsg(cmd,
262          "SELECT VolSessionId,VolSessionTime,"
263          "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
264          "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
265          "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
266          "FROM Job WHERE JobId=%s",
267          edit_int64(jr->JobId, ed1));
268   }
269 
270   if (!QUERY_DB(jcr, cmd)) { goto bail_out; }
271   if ((row = SqlFetchRow()) == NULL) {
272     Mmsg1(errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
273     SqlFreeResult();
274     goto bail_out;
275   }
276 
277   jr->VolSessionId = str_to_uint64(row[0]);
278   jr->VolSessionTime = str_to_uint64(row[1]);
279   jr->PoolId = str_to_int64(row[2]);
280   bstrncpy(jr->cStartTime, (row[3] != NULL) ? row[3] : "",
281            sizeof(jr->cStartTime));
282   bstrncpy(jr->cEndTime, (row[4] != NULL) ? row[4] : "", sizeof(jr->cEndTime));
283   jr->JobFiles = str_to_int64(row[5]);
284   jr->JobBytes = str_to_int64(row[6]);
285   jr->JobTDate = str_to_int64(row[7]);
286   bstrncpy(jr->Job, (row[8] != NULL) ? row[8] : "", sizeof(jr->Job));
287   jr->JobStatus = (row[9] != NULL) ? (int)*row[9] : JS_FatalError;
288   jr->JobType = (row[10] != NULL) ? (int)*row[10] : JT_BACKUP;
289   jr->JobLevel = (row[11] != NULL) ? (int)*row[11] : L_NONE;
290   jr->ClientId = str_to_uint64((row[12] != NULL) ? row[12] : (char*)"");
291   bstrncpy(jr->Name, (row[13] != NULL) ? row[13] : "", sizeof(jr->Name));
292   jr->PriorJobId = str_to_uint64((row[14] != NULL) ? row[14] : (char*)"");
293   bstrncpy(jr->cRealEndTime, (row[15] != NULL) ? row[15] : "",
294            sizeof(jr->cRealEndTime));
295   if (jr->JobId == 0) { jr->JobId = str_to_int64(row[16]); }
296   jr->FileSetId = str_to_int64(row[17]);
297   bstrncpy(jr->cSchedTime, (row[18] != NULL) ? row[18] : "",
298            sizeof(jr->cSchedTime));
299   bstrncpy(jr->cRealEndTime, (row[19] != NULL) ? row[19] : "",
300            sizeof(jr->cRealEndTime));
301   jr->ReadBytes = str_to_int64(row[20]);
302   jr->StartTime = StrToUtime(jr->cStartTime);
303   jr->SchedTime = StrToUtime(jr->cSchedTime);
304   jr->EndTime = StrToUtime(jr->cEndTime);
305   jr->RealEndTime = StrToUtime(jr->cRealEndTime);
306   jr->HasBase = str_to_int64(row[21]);
307   jr->PurgedFiles = str_to_int64(row[22]);
308 
309   SqlFreeResult();
310   retval = true;
311 
312 bail_out:
313   DbUnlock(this);
314   return retval;
315 }
316 
317 /**
318  * Find VolumeNames for a given JobId
319  * Returns: 0 on error or no Volumes found
320  *          number of volumes on success
321  *             Volumes are concatenated in VolumeNames
322  *             separated by a vertical bar (|) in the order
323  *             that they were written.
324  *
325  * Returns: number of volumes on success
326  */
GetJobVolumeNames(JobControlRecord * jcr,JobId_t JobId,POOLMEM * & VolumeNames)327 int BareosDb::GetJobVolumeNames(JobControlRecord* jcr,
328                                 JobId_t JobId,
329                                 POOLMEM*& VolumeNames)
330 {
331   SQL_ROW row;
332   char ed1[50];
333   int retval = 0;
334   int i;
335   int num_rows;
336 
337   DbLock(this);
338 
339   /*
340    * Get one entry per VolumeName, but "sort" by VolIndex
341    */
342   Mmsg(cmd,
343        "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
344        "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
345        "GROUP BY VolumeName "
346        "ORDER BY 2 ASC",
347        edit_int64(JobId, ed1));
348 
349   Dmsg1(130, "VolNam=%s\n", cmd);
350   VolumeNames[0] = '\0';
351   if (QUERY_DB(jcr, cmd)) {
352     num_rows = SqlNumRows();
353     Dmsg1(130, "Num rows=%d\n", num_rows);
354     if (num_rows <= 0) {
355       Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
356       retval = 0;
357     } else {
358       retval = num_rows;
359       for (i = 0; i < retval; i++) {
360         if ((row = SqlFetchRow()) == NULL) {
361           Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i,
362                 sql_strerror());
363           Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
364           retval = 0;
365           break;
366         } else {
367           if (VolumeNames[0] != '\0') { PmStrcat(VolumeNames, "|"); }
368           PmStrcat(VolumeNames, row[0]);
369         }
370       }
371     }
372     SqlFreeResult();
373   } else {
374     Mmsg(errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
375   }
376   DbUnlock(this);
377 
378   return retval;
379 }
380 
381 /**
382  * Find Volume parameters for a given JobId
383  * Returns: 0 on error or no Volumes found
384  *          number of volumes on success
385  *          List of Volumes and start/end file/blocks (malloced structure!)
386  *
387  * Returns: number of volumes on success
388  */
GetJobVolumeParameters(JobControlRecord * jcr,JobId_t JobId,VolumeParameters ** VolParams)389 int BareosDb::GetJobVolumeParameters(JobControlRecord* jcr,
390                                      JobId_t JobId,
391                                      VolumeParameters** VolParams)
392 {
393   SQL_ROW row;
394   char ed1[50];
395   int retval = 0;
396   int i;
397   VolumeParameters* Vols = NULL;
398   int num_rows;
399 
400   DbLock(this);
401   Mmsg(cmd,
402        "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
403        "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,"
404        "Slot,StorageId,InChanger,"
405        "JobBytes"
406        " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
407        " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
408        edit_int64(JobId, ed1));
409 
410   Dmsg1(130, "VolNam=%s\n", cmd);
411   if (QUERY_DB(jcr, cmd)) {
412     num_rows = SqlNumRows();
413     Dmsg1(200, "Num rows=%d\n", num_rows);
414     if (num_rows <= 0) {
415       Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
416       retval = 0;
417     } else {
418       retval = num_rows;
419       DBId_t* SId = NULL;
420       if (retval > 0) {
421         *VolParams = Vols =
422             (VolumeParameters*)malloc(retval * sizeof(VolumeParameters));
423         SId = (DBId_t*)malloc(retval * sizeof(DBId_t));
424       }
425       for (i = 0; i < retval; i++) {
426         if ((row = SqlFetchRow()) == NULL) {
427           Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i,
428                 sql_strerror());
429           Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
430           retval = 0;
431           break;
432         } else {
433           DBId_t StorageId;
434           uint32_t StartBlock, EndBlock, StartFile, EndFile;
435 
436           bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
437           bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
438           Vols[i].FirstIndex = str_to_uint64(row[2]);
439           Vols[i].LastIndex = str_to_uint64(row[3]);
440           StartFile = str_to_uint64(row[4]);
441           EndFile = str_to_uint64(row[5]);
442           StartBlock = str_to_uint64(row[6]);
443           EndBlock = str_to_uint64(row[7]);
444           Vols[i].Slot = str_to_uint64(row[8]);
445           StorageId = str_to_uint64(row[9]);
446           Vols[i].InChanger = str_to_uint64(row[10]);
447           Vols[i].JobBytes = str_to_uint64(row[11]);
448 
449           Vols[i].StartAddr = (((uint64_t)StartFile) << 32) | StartBlock;
450           Vols[i].EndAddr = (((uint64_t)EndFile) << 32) | EndBlock;
451           Vols[i].Storage[0] = 0;
452           SId[i] = StorageId;
453         }
454       }
455       for (i = 0; i < retval; i++) {
456         if (SId[i] != 0) {
457           Mmsg(cmd, "SELECT Name from Storage WHERE StorageId=%s",
458                edit_int64(SId[i], ed1));
459           if (QUERY_DB(jcr, cmd)) {
460             if ((row = SqlFetchRow()) && row[0]) {
461               bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
462             }
463           }
464         }
465       }
466       if (SId) { free(SId); }
467     }
468     SqlFreeResult();
469   }
470   DbUnlock(this);
471   return retval;
472 }
473 
474 /**
475  * Get the number of pool records
476  *
477  * Returns: -1 on failure
478  *          number on success
479  */
GetNumPoolRecords(JobControlRecord * jcr)480 int BareosDb::GetNumPoolRecords(JobControlRecord* jcr)
481 {
482   int retval = 0;
483 
484   DbLock(this);
485   Mmsg(cmd, "SELECT count(*) from Pool");
486   retval = GetSqlRecordMax(jcr);
487   DbUnlock(this);
488 
489   return retval;
490 }
491 
492 /**
493  * This function returns a list of all the Pool record ids.
494  * The caller must free ids if non-NULL.
495  *
496  * Returns 0: on failure
497  *         1: on success
498  */
GetPoolIds(JobControlRecord * jcr,int * num_ids,DBId_t ** ids)499 int BareosDb::GetPoolIds(JobControlRecord* jcr, int* num_ids, DBId_t** ids)
500 {
501   SQL_ROW row;
502   int retval = 0;
503   int i = 0;
504   DBId_t* id;
505 
506   DbLock(this);
507   *ids = NULL;
508   Mmsg(cmd, "SELECT PoolId FROM Pool");
509   if (QUERY_DB(jcr, cmd)) {
510     *num_ids = SqlNumRows();
511     if (*num_ids > 0) {
512       id = (DBId_t*)malloc(*num_ids * sizeof(DBId_t));
513       while ((row = SqlFetchRow()) != NULL) { id[i++] = str_to_uint64(row[0]); }
514       *ids = id;
515     }
516     SqlFreeResult();
517     retval = 1;
518   } else {
519     Mmsg(errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror());
520     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
521     retval = 0;
522   }
523 
524   DbUnlock(this);
525   return retval;
526 }
527 
528 /**
529  * This function returns a list of all the Storage record ids.
530  *  The caller must free ids if non-NULL.
531  *
532  *  Returns 0: on failure
533  *          1: on success
534  */
GetStorageIds(JobControlRecord * jcr,int * num_ids,DBId_t * ids[])535 int BareosDb::GetStorageIds(JobControlRecord* jcr, int* num_ids, DBId_t* ids[])
536 {
537   SQL_ROW row;
538   int retval = 0;
539   int i = 0;
540   DBId_t* id;
541 
542   DbLock(this);
543   *ids = NULL;
544   Mmsg(cmd, "SELECT StorageId FROM Storage");
545   if (QUERY_DB(jcr, cmd)) {
546     *num_ids = SqlNumRows();
547     if (*num_ids > 0) {
548       id = (DBId_t*)malloc(*num_ids * sizeof(DBId_t));
549       while ((row = SqlFetchRow()) != NULL) { id[i++] = str_to_uint64(row[0]); }
550       *ids = id;
551     }
552     SqlFreeResult();
553     retval = 1;
554   } else {
555     Mmsg(errmsg, _("Storage id select failed: ERR=%s\n"), sql_strerror());
556     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
557     retval = 0;
558   }
559 
560   DbUnlock(this);
561   return retval;
562 }
563 
564 /**
565  * This function returns a list of all the Client record ids.
566  * The caller must free ids if non-NULL.
567  *
568  * Returns false: on failure
569  *         true: on success
570  */
GetClientIds(JobControlRecord * jcr,int * num_ids,DBId_t * ids[])571 bool BareosDb::GetClientIds(JobControlRecord* jcr, int* num_ids, DBId_t* ids[])
572 {
573   bool retval = false;
574   SQL_ROW row;
575   int i = 0;
576   DBId_t* id;
577 
578   DbLock(this);
579   *ids = NULL;
580   Mmsg(cmd, "SELECT ClientId FROM Client ORDER BY Name");
581   if (QUERY_DB(jcr, cmd)) {
582     *num_ids = SqlNumRows();
583     if (*num_ids > 0) {
584       id = (DBId_t*)malloc(*num_ids * sizeof(DBId_t));
585       while ((row = SqlFetchRow()) != NULL) { id[i++] = str_to_uint64(row[0]); }
586       *ids = id;
587     }
588     SqlFreeResult();
589     retval = true;
590   } else {
591     Mmsg(errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror());
592     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
593   }
594   DbUnlock(this);
595   return retval;
596 }
597 
598 /**
599  * Get Pool Record
600  * If the PoolId is non-zero, we get its record,
601  * otherwise, we search on the PoolName
602  *
603  * Returns: false on failure
604  *          true on success
605  */
GetPoolRecord(JobControlRecord * jcr,PoolDbRecord * pdbr)606 bool BareosDb::GetPoolRecord(JobControlRecord* jcr, PoolDbRecord* pdbr)
607 {
608   SQL_ROW row;
609   bool ok = false;
610   char ed1[50];
611   int num_rows;
612   char esc[MAX_ESCAPE_NAME_LENGTH];
613 
614   DbLock(this);
615   if (pdbr->PoolId != 0) { /* find by id */
616     Mmsg(
617         cmd,
618         "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
619         "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
620         "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,"
621         "ScratchPoolId,"
622         "ActionOnPurge,MinBlocksize,MaxBlocksize FROM Pool WHERE "
623         "Pool.PoolId=%s",
624         edit_int64(pdbr->PoolId, ed1));
625   } else { /* find by name */
626     EscapeString(jcr, esc, pdbr->Name, strlen(pdbr->Name));
627     Mmsg(
628         cmd,
629         "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
630         "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
631         "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,"
632         "ScratchPoolId,"
633         "ActionOnPurge,MinBlocksize,MaxBlocksize FROM Pool WHERE "
634         "Pool.Name='%s'",
635         esc);
636   }
637   if (QUERY_DB(jcr, cmd)) {
638     num_rows = SqlNumRows();
639     if (num_rows > 1) {
640       char ed1[30];
641       Mmsg1(errmsg, _("More than one Pool!: %s\n"), edit_uint64(num_rows, ed1));
642       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
643     } else if (num_rows == 1) {
644       if ((row = SqlFetchRow()) == NULL) {
645         Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
646         Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
647       } else {
648         pdbr->PoolId = str_to_int64(row[0]);
649         bstrncpy(pdbr->Name, (row[1] != NULL) ? row[1] : "",
650                  sizeof(pdbr->Name));
651         pdbr->NumVols = str_to_int64(row[2]);
652         pdbr->MaxVols = str_to_int64(row[3]);
653         pdbr->UseOnce = str_to_int64(row[4]);
654         pdbr->UseCatalog = str_to_int64(row[5]);
655         pdbr->AcceptAnyVolume = str_to_int64(row[6]);
656         pdbr->AutoPrune = str_to_int64(row[7]);
657         pdbr->Recycle = str_to_int64(row[8]);
658         pdbr->VolRetention = str_to_int64(row[9]);
659         pdbr->VolUseDuration = str_to_int64(row[10]);
660         pdbr->MaxVolJobs = str_to_int64(row[11]);
661         pdbr->MaxVolFiles = str_to_int64(row[12]);
662         pdbr->MaxVolBytes = str_to_uint64(row[13]);
663         bstrncpy(pdbr->PoolType, (row[14] != NULL) ? row[14] : "",
664                  sizeof(pdbr->PoolType));
665         pdbr->LabelType = str_to_int64(row[15]);
666         bstrncpy(pdbr->LabelFormat, (row[16] != NULL) ? row[16] : "",
667                  sizeof(pdbr->LabelFormat));
668         pdbr->RecyclePoolId = str_to_int64(row[17]);
669         pdbr->ScratchPoolId = str_to_int64(row[18]);
670         pdbr->ActionOnPurge = str_to_int32(row[19]);
671         pdbr->MinBlocksize = str_to_int32(row[20]);
672         pdbr->MaxBlocksize = str_to_int32(row[21]);
673         ok = true;
674       }
675     }
676     SqlFreeResult();
677   }
678 
679   if (ok) {
680     uint32_t NumVols;
681 
682     Mmsg(cmd, "SELECT count(*) from Media WHERE PoolId=%s",
683          edit_int64(pdbr->PoolId, ed1));
684     NumVols = GetSqlRecordMax(jcr);
685     Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
686     if (NumVols != pdbr->NumVols) {
687       pdbr->NumVols = NumVols;
688       ok = UpdatePoolRecord(jcr, pdbr);
689     }
690   } else {
691     Mmsg(errmsg, _("Pool record not found in Catalog.\n"));
692   }
693 
694   DbUnlock(this);
695   return ok;
696 }
697 
698 /**
699  * Get Storage Record
700  * If the StorageId is non-zero, we get its record, otherwise, we search on the
701  * StorageName
702  *
703  * Returns: false on failure
704  *          true on success
705  */
GetStorageRecord(JobControlRecord * jcr,StorageDbRecord * sdbr)706 bool BareosDb::GetStorageRecord(JobControlRecord* jcr, StorageDbRecord* sdbr)
707 {
708   SQL_ROW row;
709   bool ok = false;
710   char ed1[50];
711   int num_rows;
712   char esc[MAX_ESCAPE_NAME_LENGTH];
713 
714   DbLock(this);
715   if (sdbr->StorageId != 0) { /* find by id */
716     Mmsg(cmd,
717          "SELECT StorageId,Name,AutoChanger FROM Storage WHERE "
718          "Storage.StorageId=%s",
719          edit_int64(sdbr->StorageId, ed1));
720   } else { /* find by name */
721     EscapeString(jcr, esc, sdbr->Name, strlen(sdbr->Name));
722     Mmsg(cmd,
723          "SELECT StorageId,Name,Autochanger FROM Storage WHERE "
724          "Storage.Name='%s'",
725          esc);
726   }
727   if (QUERY_DB(jcr, cmd)) {
728     num_rows = SqlNumRows();
729     if (num_rows > 1) {
730       char ed1[30];
731 
732       Mmsg1(errmsg, _("More than one Storage!: %s\n"),
733             edit_uint64(num_rows, ed1));
734       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
735     } else if (num_rows == 1) {
736       if ((row = SqlFetchRow()) == NULL) {
737         Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
738         Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
739       } else {
740         sdbr->StorageId = str_to_int64(row[0]);
741         bstrncpy(sdbr->Name, (row[1] != NULL) ? row[1] : "",
742                  sizeof(sdbr->Name));
743         sdbr->AutoChanger = str_to_int64(row[2]);
744         ok = true;
745       }
746     }
747     SqlFreeResult();
748   }
749 
750   DbUnlock(this);
751   return ok;
752 }
753 
754 /**
755  * Get Client Record
756  * If the ClientId is non-zero, we get its record, otherwise, we search on the
757  * Client Name
758  *
759  * Returns: false on failure
760  *          true on success
761  */
GetClientRecord(JobControlRecord * jcr,ClientDbRecord * cdbr)762 bool BareosDb::GetClientRecord(JobControlRecord* jcr, ClientDbRecord* cdbr)
763 {
764   bool retval = false;
765   SQL_ROW row;
766   char ed1[50];
767   int num_rows;
768   char esc[MAX_ESCAPE_NAME_LENGTH];
769 
770   DbLock(this);
771   if (cdbr->ClientId != 0) { /* find by id */
772     Mmsg(cmd,
773          "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
774          "FROM Client WHERE Client.ClientId=%s",
775          edit_int64(cdbr->ClientId, ed1));
776   } else { /* find by name */
777     EscapeString(jcr, esc, cdbr->Name, strlen(cdbr->Name));
778     Mmsg(cmd,
779          "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
780          "FROM Client WHERE Client.Name='%s'",
781          esc);
782   }
783 
784   if (QUERY_DB(jcr, cmd)) {
785     num_rows = SqlNumRows();
786     if (num_rows > 1) {
787       Mmsg1(errmsg, _("More than one Client!: %s\n"),
788             edit_uint64(num_rows, ed1));
789       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
790     } else if (num_rows == 1) {
791       if ((row = SqlFetchRow()) == NULL) {
792         Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
793         Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
794       } else {
795         cdbr->ClientId = str_to_int64(row[0]);
796         bstrncpy(cdbr->Name, (row[1] != NULL) ? row[1] : "",
797                  sizeof(cdbr->Name));
798         bstrncpy(cdbr->Uname, (row[2] != NULL) ? row[2] : "",
799                  sizeof(cdbr->Uname));
800         cdbr->AutoPrune = str_to_int64(row[3]);
801         cdbr->FileRetention = str_to_int64(row[4]);
802         cdbr->JobRetention = str_to_int64(row[5]);
803         retval = true;
804       }
805     } else {
806       Mmsg(errmsg, _("Client record not found in Catalog.\n"));
807     }
808     SqlFreeResult();
809   } else {
810     Mmsg(errmsg, _("Client record not found in Catalog.\n"));
811   }
812 
813   DbUnlock(this);
814   return retval;
815 }
816 
817 /**
818  * Get Counter Record
819  *
820  * Returns: false on failure
821  *          true on success
822  */
GetCounterRecord(JobControlRecord * jcr,CounterDbRecord * cr)823 bool BareosDb::GetCounterRecord(JobControlRecord* jcr, CounterDbRecord* cr)
824 {
825   bool retval = false;
826   SQL_ROW row;
827   int num_rows;
828   char esc[MAX_ESCAPE_NAME_LENGTH];
829 
830   DbLock(this);
831   EscapeString(jcr, esc, cr->Counter, strlen(cr->Counter));
832 
833   FillQuery(SQL_QUERY_select_counter_values, esc);
834   if (QUERY_DB(jcr, cmd)) {
835     num_rows = SqlNumRows();
836 
837     /*
838      * If more than one, report error, but return first row
839      */
840     if (num_rows > 1) {
841       Mmsg1(errmsg, _("More than one Counter!: %d\n"), num_rows);
842       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
843     }
844     if (num_rows >= 1) {
845       if ((row = SqlFetchRow()) == NULL) {
846         Mmsg1(errmsg, _("error fetching Counter row: %s\n"), sql_strerror());
847         Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
848         SqlFreeResult();
849         goto bail_out;
850       }
851       cr->MinValue = str_to_int64(row[0]);
852       cr->MaxValue = str_to_int64(row[1]);
853       cr->CurrentValue = str_to_int64(row[2]);
854       if (row[3]) {
855         bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
856       } else {
857         cr->WrapCounter[0] = 0;
858       }
859       SqlFreeResult();
860       retval = true;
861       goto bail_out;
862     }
863     SqlFreeResult();
864   } else {
865     Mmsg(errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
866   }
867 
868 bail_out:
869   DbUnlock(this);
870   return retval;
871 }
872 
873 /**
874  * Get FileSet Record
875  * If the FileSetId is non-zero, we get its record,
876  *  otherwise, we search on the name
877  *
878  * Returns: 0 on failure
879  *          id on success
880  */
GetFilesetRecord(JobControlRecord * jcr,FileSetDbRecord * fsr)881 int BareosDb::GetFilesetRecord(JobControlRecord* jcr, FileSetDbRecord* fsr)
882 {
883   SQL_ROW row;
884   int retval = 0;
885   char ed1[50];
886   int num_rows;
887   char esc[MAX_ESCAPE_NAME_LENGTH];
888 
889   DbLock(this);
890   if (fsr->FileSetId != 0) { /* find by id */
891     Mmsg(cmd,
892          "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
893          "WHERE FileSetId=%s",
894          edit_int64(fsr->FileSetId, ed1));
895   } else { /* find by name */
896     EscapeString(jcr, esc, fsr->FileSet, strlen(fsr->FileSet));
897     Mmsg(cmd,
898          "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
899          "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1",
900          esc);
901   }
902 
903   if (QUERY_DB(jcr, cmd)) {
904     num_rows = SqlNumRows();
905     if (num_rows > 1) {
906       char ed1[30];
907       Mmsg1(errmsg, _("Error got %s FileSets but expected only one!\n"),
908             edit_uint64(num_rows, ed1));
909       SqlDataSeek(num_rows - 1);
910     }
911     if ((row = SqlFetchRow()) == NULL) {
912       Mmsg1(errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
913     } else {
914       fsr->FileSetId = str_to_int64(row[0]);
915       bstrncpy(fsr->FileSet, (row[1] != NULL) ? row[1] : "",
916                sizeof(fsr->FileSet));
917       bstrncpy(fsr->MD5, (row[2] != NULL) ? row[2] : "", sizeof(fsr->MD5));
918       bstrncpy(fsr->cCreateTime, (row[3] != NULL) ? row[3] : "",
919                sizeof(fsr->cCreateTime));
920       retval = fsr->FileSetId;
921     }
922     SqlFreeResult();
923   } else {
924     Mmsg(errmsg, _("FileSet record not found in Catalog.\n"));
925   }
926   DbUnlock(this);
927   return retval;
928 }
929 
930 /**
931  * Get the number of Media records
932  *
933  * Returns: -1 on failure
934  *          number on success
935  */
GetNumMediaRecords(JobControlRecord * jcr)936 int BareosDb::GetNumMediaRecords(JobControlRecord* jcr)
937 {
938   int retval = 0;
939 
940   DbLock(this);
941   Mmsg(cmd, "SELECT count(*) from Media");
942   retval = GetSqlRecordMax(jcr);
943   DbUnlock(this);
944   return retval;
945 }
946 
PrepareMediaSqlQuery(JobControlRecord * jcr,MediaDbRecord * mr,PoolMem & volumes)947 bool BareosDb::PrepareMediaSqlQuery(JobControlRecord* jcr,
948                                     MediaDbRecord* mr,
949                                     PoolMem& volumes)
950 {
951   bool ok = true;
952   char ed1[50];
953   char esc[MAX_NAME_LENGTH * 2 + 1];
954   PoolMem buf(PM_MESSAGE);
955 
956   Mmsg(cmd,
957        "SELECT DISTINCT MediaId FROM Media WHERE Recycle=%d AND Enabled=%d ",
958        mr->Recycle, mr->Enabled);
959 
960   if (*mr->MediaType) {
961     EscapeString(jcr, esc, mr->MediaType, strlen(mr->MediaType));
962     Mmsg(buf, "AND MediaType='%s' ", esc);
963     PmStrcat(cmd, buf.c_str());
964   }
965 
966   if (mr->StorageId) {
967     Mmsg(buf, "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1));
968     PmStrcat(cmd, buf.c_str());
969   }
970 
971   if (mr->PoolId) {
972     Mmsg(buf, "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1));
973     PmStrcat(cmd, buf.c_str());
974   }
975 
976   if (mr->VolBytes) {
977     Mmsg(buf, "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
978     PmStrcat(cmd, buf.c_str());
979   }
980 
981   if (*mr->VolStatus) {
982     EscapeString(jcr, esc, mr->VolStatus, strlen(mr->VolStatus));
983     Mmsg(buf, "AND VolStatus = '%s' ", esc);
984     PmStrcat(cmd, buf.c_str());
985   }
986 
987   if (volumes.strlen() > 0) {
988     /* extra list of volumes given */
989     Mmsg(buf, "AND VolumeName IN (%s) ", volumes.c_str());
990     PmStrcat(cmd, buf.c_str());
991   } else if (*mr->VolumeName) {
992     /* single volume given in media record */
993     EscapeString(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
994     Mmsg(buf, "AND VolumeName = '%s' ", esc);
995     PmStrcat(cmd, buf.c_str());
996   }
997 
998   Dmsg1(100, "query=%s\n", cmd);
999 
1000   return ok;
1001 }
1002 
1003 /**
1004  * This function creates a sql query string at cmd to return a list of all the
1005  * Media records for the current Pool, the correct Media Type, Recyle, Enabled,
1006  * StorageId, VolBytes and volumes or VolumeName if specified. Comma separated
1007  * list of volumes takes precedence over VolumeName. The caller must free ids if
1008  * non-NULL.
1009  */
GetMediaIds(JobControlRecord * jcr,MediaDbRecord * mr,PoolMem & volumes,int * num_ids,DBId_t * ids[])1010 bool BareosDb::GetMediaIds(JobControlRecord* jcr,
1011                            MediaDbRecord* mr,
1012                            PoolMem& volumes,
1013                            int* num_ids,
1014                            DBId_t* ids[])
1015 {
1016   bool ok = false;
1017   SQL_ROW row;
1018   int i = 0;
1019   DBId_t* id;
1020 
1021   DbLock(this);
1022   *ids = NULL;
1023 
1024   if (!PrepareMediaSqlQuery(jcr, mr, volumes)) {
1025     Mmsg(errmsg, _("Media id select failed: invalid parameter"));
1026     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1027     goto bail_out;
1028   }
1029 
1030   if (!QUERY_DB(jcr, cmd)) {
1031     Mmsg(errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror());
1032     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1033     goto bail_out;
1034   }
1035 
1036   *num_ids = SqlNumRows();
1037   if (*num_ids > 0) {
1038     id = (DBId_t*)malloc(*num_ids * sizeof(DBId_t));
1039     while ((row = SqlFetchRow()) != NULL) { id[i++] = str_to_uint64(row[0]); }
1040     *ids = id;
1041   }
1042   SqlFreeResult();
1043   ok = true;
1044 bail_out:
1045   DbUnlock(this);
1046   return ok;
1047 }
1048 
1049 
1050 /**
1051  * This function returns a list of all the DBIds that are returned for the
1052  * query.
1053  *
1054  * Returns false: on failure
1055  *         true:  on success
1056  */
GetQueryDbids(JobControlRecord * jcr,PoolMem & query,dbid_list & ids)1057 bool BareosDb::GetQueryDbids(JobControlRecord* jcr,
1058                              PoolMem& query,
1059                              dbid_list& ids)
1060 {
1061   SQL_ROW row;
1062   int i = 0;
1063   bool ok = false;
1064 
1065   DbLock(this);
1066   ids.num_ids = 0;
1067   if (QUERY_DB(jcr, query.c_str())) {
1068     ids.num_ids = SqlNumRows();
1069     if (ids.num_ids > 0) {
1070       if (ids.max_ids < ids.num_ids) {
1071         free(ids.DBId);
1072         ids.DBId = (DBId_t*)malloc(ids.num_ids * sizeof(DBId_t));
1073       }
1074       while ((row = SqlFetchRow()) != NULL) {
1075         ids.DBId[i++] = str_to_uint64(row[0]);
1076       }
1077     }
1078     SqlFreeResult();
1079     ok = true;
1080   } else {
1081     Mmsg(errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror());
1082     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1083     ok = false;
1084   }
1085   DbUnlock(this);
1086   return ok;
1087 }
1088 
1089 
1090 /**
1091  * Get Media Record
1092  *
1093  * Returns: false: on failure
1094  *          true:  on success
1095  */
GetMediaRecord(JobControlRecord * jcr,MediaDbRecord * mr)1096 bool BareosDb::GetMediaRecord(JobControlRecord* jcr, MediaDbRecord* mr)
1097 {
1098   bool retval = false;
1099   SQL_ROW row;
1100   char ed1[50];
1101   int num_rows;
1102   char esc[MAX_ESCAPE_NAME_LENGTH];
1103 
1104   DbLock(this);
1105   if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
1106     Mmsg(cmd, "SELECT count(*) from Media");
1107     mr->MediaId = GetSqlRecordMax(jcr);
1108     retval = true;
1109     goto bail_out;
1110   }
1111   if (mr->MediaId != 0) { /* find by id */
1112     Mmsg(cmd,
1113          "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
1114          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1115          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1116          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1117          "EndFile,EndBlock,LabelType,LabelDate,StorageId,"
1118          "Enabled,LocationId,RecycleCount,InitialWrite,"
1119          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,"
1120          "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize "
1121          "FROM Media WHERE MediaId=%s",
1122          edit_int64(mr->MediaId, ed1));
1123   } else { /* find by name */
1124     EscapeString(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1125     Mmsg(cmd,
1126          "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
1127          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1128          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1129          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1130          "EndFile,EndBlock,LabelType,LabelDate,StorageId,"
1131          "Enabled,LocationId,RecycleCount,InitialWrite,"
1132          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,"
1133          "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize "
1134          "FROM Media WHERE VolumeName='%s'",
1135          esc);
1136   }
1137 
1138   if (QUERY_DB(jcr, cmd)) {
1139     char ed1[50];
1140     num_rows = SqlNumRows();
1141     if (num_rows > 1) {
1142       Mmsg1(errmsg, _("More than one Volume!: %s\n"),
1143             edit_uint64(num_rows, ed1));
1144       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1145     } else if (num_rows == 1) {
1146       if ((row = SqlFetchRow()) == NULL) {
1147         Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1148         Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1149       } else {
1150         /* return values */
1151         mr->MediaId = str_to_int64(row[0]);
1152         bstrncpy(mr->VolumeName, (row[1] != NULL) ? row[1] : "",
1153                  sizeof(mr->VolumeName));
1154         mr->VolJobs = str_to_int64(row[2]);
1155         mr->VolFiles = str_to_int64(row[3]);
1156         mr->VolBlocks = str_to_int64(row[4]);
1157         mr->VolBytes = str_to_uint64(row[5]);
1158         mr->VolMounts = str_to_int64(row[6]);
1159         mr->VolErrors = str_to_int64(row[7]);
1160         mr->VolWrites = str_to_int64(row[8]);
1161         mr->MaxVolBytes = str_to_uint64(row[9]);
1162         mr->VolCapacityBytes = str_to_uint64(row[10]);
1163         bstrncpy(mr->MediaType, (row[11] != NULL) ? row[11] : "",
1164                  sizeof(mr->MediaType));
1165         bstrncpy(mr->VolStatus, (row[12] != NULL) ? row[12] : "",
1166                  sizeof(mr->VolStatus));
1167         mr->PoolId = str_to_int64(row[13]);
1168         mr->VolRetention = str_to_uint64(row[14]);
1169         mr->VolUseDuration = str_to_uint64(row[15]);
1170         mr->MaxVolJobs = str_to_int64(row[16]);
1171         mr->MaxVolFiles = str_to_int64(row[17]);
1172         mr->Recycle = str_to_int64(row[18]);
1173         mr->Slot = str_to_int64(row[19]);
1174         bstrncpy(mr->cFirstWritten, (row[20] != NULL) ? row[20] : "",
1175                  sizeof(mr->cFirstWritten));
1176         mr->FirstWritten = (time_t)StrToUtime(mr->cFirstWritten);
1177         bstrncpy(mr->cLastWritten, (row[21] != NULL) ? row[21] : "",
1178                  sizeof(mr->cLastWritten));
1179         mr->LastWritten = (time_t)StrToUtime(mr->cLastWritten);
1180         mr->InChanger = str_to_uint64(row[22]);
1181         mr->EndFile = str_to_uint64(row[23]);
1182         mr->EndBlock = str_to_uint64(row[24]);
1183         mr->LabelType = str_to_int64(row[25]);
1184         bstrncpy(mr->cLabelDate, (row[26] != NULL) ? row[26] : "",
1185                  sizeof(mr->cLabelDate));
1186         mr->LabelDate = (time_t)StrToUtime(mr->cLabelDate);
1187         mr->StorageId = str_to_int64(row[27]);
1188         mr->Enabled = str_to_int64(row[28]);
1189         mr->LocationId = str_to_int64(row[29]);
1190         mr->RecycleCount = str_to_int64(row[30]);
1191         bstrncpy(mr->cInitialWrite, (row[31] != NULL) ? row[31] : "",
1192                  sizeof(mr->cInitialWrite));
1193         mr->InitialWrite = (time_t)StrToUtime(mr->cInitialWrite);
1194         mr->ScratchPoolId = str_to_int64(row[32]);
1195         mr->RecyclePoolId = str_to_int64(row[33]);
1196         mr->VolReadTime = str_to_int64(row[34]);
1197         mr->VolWriteTime = str_to_int64(row[35]);
1198         mr->ActionOnPurge = str_to_int32(row[36]);
1199         bstrncpy(mr->EncrKey, (row[37] != NULL) ? row[37] : "",
1200                  sizeof(mr->EncrKey));
1201         mr->MinBlocksize = str_to_int32(row[38]);
1202         mr->MaxBlocksize = str_to_int32(row[39]);
1203         retval = true;
1204       }
1205     } else {
1206       if (mr->MediaId != 0) {
1207         Mmsg1(errmsg, _("Media record MediaId=%s not found.\n"),
1208               edit_int64(mr->MediaId, ed1));
1209       } else {
1210         Mmsg1(errmsg, _("Media record for Volume \"%s\" not found.\n"),
1211               mr->VolumeName);
1212       }
1213     }
1214     SqlFreeResult();
1215   } else {
1216     if (mr->MediaId != 0) {
1217       Mmsg(errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1218            mr->MediaId);
1219     } else {
1220       Mmsg(errmsg, _("Media record for Vol=%s not found in Catalog.\n"),
1221            mr->VolumeName);
1222     }
1223   }
1224 
1225 bail_out:
1226   DbUnlock(this);
1227   return retval;
1228 }
1229 
1230 /**
1231  * Remove all MD5 from a query (can save lot of memory with many files)
1232  */
strip_md5(char * q)1233 static void strip_md5(char* q)
1234 {
1235   char* p = q;
1236   while ((p = strstr(p, ", MD5"))) { memset(p, ' ', 5 * sizeof(char)); }
1237 }
1238 
1239 /**
1240  * Find the last "accurate" backup state (that can take deleted files in
1241  * account)
1242  * 1) Get all files with jobid in list (F subquery)
1243  *    Get all files in BaseFiles with jobid in list
1244  * 2) Take only the last version of each file (Temp subquery) => accurate list
1245  *    is ok
1246  * 3) Join the result to file table to get fileindex, jobid and lstat
1247  * information
1248  *
1249  * TODO: See if we can do the SORT only if needed (as an argument)
1250  */
GetFileList(JobControlRecord * jcr,char * jobids,bool use_md5,bool use_delta,DB_RESULT_HANDLER * ResultHandler,void * ctx)1251 bool BareosDb::GetFileList(JobControlRecord* jcr,
1252                            char* jobids,
1253                            bool use_md5,
1254                            bool use_delta,
1255                            DB_RESULT_HANDLER* ResultHandler,
1256                            void* ctx)
1257 {
1258   PoolMem query(PM_MESSAGE);
1259   PoolMem query2(PM_MESSAGE);
1260 
1261   if (!*jobids) {
1262     DbLock(this);
1263     Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1264     DbUnlock(this);
1265     return false;
1266   }
1267 
1268   if (use_delta) {
1269     FillQuery(query2, SQL_QUERY_select_recent_version_with_basejob_and_delta,
1270               jobids, jobids, jobids, jobids);
1271   } else {
1272     FillQuery(query2, SQL_QUERY_select_recent_version_with_basejob, jobids,
1273               jobids, jobids, jobids);
1274   }
1275 
1276   /*
1277    * BootStrapRecord code is optimized for JobId sorted, with Delta, we need to
1278    * get them ordered by date. JobTDate and JobId can be mixed if using Copy or
1279    * Migration
1280    */
1281   Mmsg(query,
1282        "SELECT Path.Path, T1.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, "
1283        "MD5, Fhinfo, Fhnode "
1284        "FROM ( %s ) AS T1 "
1285        "JOIN Path ON (Path.PathId = T1.PathId) "
1286        "WHERE FileIndex > 0 "
1287        "ORDER BY T1.JobTDate, FileIndex ASC", /* Return sorted by JobTDate */
1288                                               /* FileIndex for restore code */
1289        query2.c_str());
1290 
1291   if (!use_md5) { strip_md5(query.c_str()); }
1292 
1293   Dmsg1(100, "q=%s\n", query.c_str());
1294 
1295   return BigSqlQuery(query.c_str(), ResultHandler, ctx);
1296 }
1297 
1298 /**
1299  * This procedure gets the base jobid list used by jobids,
1300  */
GetUsedBaseJobids(JobControlRecord * jcr,POOLMEM * jobids,db_list_ctx * result)1301 bool BareosDb::GetUsedBaseJobids(JobControlRecord* jcr,
1302                                  POOLMEM* jobids,
1303                                  db_list_ctx* result)
1304 {
1305   PoolMem query(PM_MESSAGE);
1306 
1307   Mmsg(query,
1308        "SELECT DISTINCT BaseJobId "
1309        "  FROM Job JOIN BaseFiles USING (JobId) "
1310        " WHERE Job.HasBase = 1 "
1311        "   AND Job.JobId IN (%s) ",
1312        jobids);
1313   return SqlQueryWithHandler(query.c_str(), DbListHandler, result);
1314 }
1315 
1316 /**
1317  * The decision do change an incr/diff was done before
1318  * Full : do nothing
1319  * Differential : get the last full id
1320  * Incremental : get the last full + last diff + last incr(s) ids
1321  *
1322  * If you specify jr->StartTime, it will be used to limit the search
1323  * in the time. (usually now)
1324  *
1325  * If you specify jr->limit, it will be used to limit the list of jobids
1326  * to a that number
1327  *
1328  * TODO: look and merge from ua_restore.c
1329  */
AccurateGetJobids(JobControlRecord * jcr,JobDbRecord * jr,db_list_ctx * jobids)1330 bool BareosDb::AccurateGetJobids(JobControlRecord* jcr,
1331                                  JobDbRecord* jr,
1332                                  db_list_ctx* jobids)
1333 {
1334   bool retval = false;
1335   char clientid[50], jobid[50], filesetid[50];
1336   char date[MAX_TIME_LENGTH];
1337   PoolMem query(PM_MESSAGE);
1338 
1339   /* Take the current time as upper limit if nothing else specified */
1340   utime_t StartTime = (jr->StartTime) ? jr->StartTime : time(NULL);
1341 
1342   bstrutime(date, sizeof(date), StartTime + 1);
1343   jobids->reset();
1344 
1345   /*
1346    * First, find the last good Full backup for this job/client/fileset
1347    */
1348   FillQuery(query, SQL_QUERY_create_temp_accurate_jobids,
1349             edit_uint64(jcr->JobId, jobid), edit_uint64(jr->ClientId, clientid),
1350             date, edit_uint64(jr->FileSetId, filesetid));
1351 
1352   if (!SqlQuery(query.c_str())) { goto bail_out; }
1353 
1354   if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1355     /*
1356      * Now, find the last differential backup after the last full
1357      */
1358     Mmsg(query,
1359          "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, "
1360          "PurgedFiles) "
1361          "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1362          "FROM Job JOIN FileSet USING (FileSetId) "
1363          "WHERE ClientId = %s "
1364          "AND JobFiles > 0 "
1365          "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1366          "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC "
1367          "LIMIT 1) "
1368          "AND StartTime < '%s' "
1369          "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = "
1370          "%s) "
1371          "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1372          jobid, clientid, jobid, date, filesetid);
1373 
1374     if (!SqlQuery(query.c_str())) { goto bail_out; }
1375 
1376     /*
1377      * We just have to take all incremental after the last Full/Diff
1378      *
1379      * If we are doing always incremental, we need to limit the search to
1380      * only include incrementals that are older than (now -
1381      * AlwaysIncrementalInterval) and leave AlwaysIncrementalNumber incrementals
1382      */
1383     Mmsg(query,
1384          "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, "
1385          "PurgedFiles) "
1386          "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1387          "FROM Job JOIN FileSet USING (FileSetId) "
1388          "WHERE ClientId = %s "
1389          "AND JobFiles > 0 "
1390          "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1391          "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC "
1392          "LIMIT 1) "
1393          "AND StartTime < '%s' "
1394          "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = "
1395          "%s) "
1396          "ORDER BY Job.JobTDate DESC ",
1397          jobid, clientid, jobid, date, filesetid);
1398     if (!SqlQuery(query.c_str())) { goto bail_out; }
1399   }
1400 
1401   /*
1402    * Build a jobid list ie: 1,2,3,4
1403    */
1404   if (jr->limit) {
1405     Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate LIMIT %d", jobid,
1406          jr->limit);
1407   } else {
1408     Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1409   }
1410   SqlQueryWithHandler(query.c_str(), DbListHandler, jobids);
1411   Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list);
1412   retval = true;
1413 
1414 bail_out:
1415   Mmsg(query, "DROP TABLE btemp3%s", jobid);
1416   SqlQuery(query.c_str());
1417   return retval;
1418 }
1419 
GetBaseFileList(JobControlRecord * jcr,bool use_md5,DB_RESULT_HANDLER * ResultHandler,void * ctx)1420 bool BareosDb::GetBaseFileList(JobControlRecord* jcr,
1421                                bool use_md5,
1422                                DB_RESULT_HANDLER* ResultHandler,
1423                                void* ctx)
1424 {
1425   PoolMem query(PM_MESSAGE);
1426 
1427   Mmsg(query,
1428        "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5, "
1429        "Fhinfo, Fhnode "
1430        "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1431        (uint64_t)jcr->JobId);
1432 
1433   if (!use_md5) { strip_md5(query.c_str()); }
1434   return BigSqlQuery(query.c_str(), ResultHandler, ctx);
1435 }
1436 
GetBaseJobid(JobControlRecord * jcr,JobDbRecord * jr,JobId_t * jobid)1437 bool BareosDb::GetBaseJobid(JobControlRecord* jcr,
1438                             JobDbRecord* jr,
1439                             JobId_t* jobid)
1440 {
1441   PoolMem query(PM_MESSAGE);
1442   utime_t StartTime;
1443   db_int64_ctx lctx;
1444   char date[MAX_TIME_LENGTH];
1445   char esc[MAX_ESCAPE_NAME_LENGTH];
1446   bool retval = false;
1447   // char clientid[50], filesetid[50];
1448 
1449   *jobid = 0;
1450   lctx.count = 0;
1451   lctx.value = 0;
1452 
1453   StartTime = (jr->StartTime) ? jr->StartTime : time(NULL);
1454   bstrutime(date, sizeof(date), StartTime + 1);
1455   EscapeString(jcr, esc, jr->Name, strlen(jr->Name));
1456 
1457   /* we can take also client name, fileset, etc... */
1458 
1459   Mmsg(query,
1460        "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
1461        "FROM Job "
1462        // "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
1463        "WHERE Job.Name = '%s' "
1464        "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' "
1465        //    "AND FileSet.FileSet= '%s' "
1466        //    "AND Client.Name = '%s' "
1467        "AND StartTime<'%s' "
1468        "ORDER BY Job.JobTDate DESC LIMIT 1",
1469        esc,
1470        //      edit_uint64(jr->ClientId, clientid),
1471        //      edit_uint64(jr->FileSetId, filesetid));
1472        date);
1473 
1474   Dmsg1(10, "GetBaseJobid q=%s\n", query.c_str());
1475   if (!SqlQueryWithHandler(query.c_str(), db_int64_handler, &lctx)) {
1476     goto bail_out;
1477   }
1478   *jobid = (JobId_t)lctx.value;
1479 
1480   Dmsg1(10, "GetBaseJobid=%lld\n", *jobid);
1481   retval = true;
1482 
1483 bail_out:
1484   return retval;
1485 }
1486 
1487 /**
1488  * Get JobIds associated with a volume
1489  */
GetVolumeJobids(JobControlRecord * jcr,MediaDbRecord * mr,db_list_ctx * lst)1490 bool BareosDb::GetVolumeJobids(JobControlRecord* jcr,
1491                                MediaDbRecord* mr,
1492                                db_list_ctx* lst)
1493 {
1494   char ed1[50];
1495   bool retval;
1496 
1497   DbLock(this);
1498   Mmsg(cmd, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s",
1499        edit_int64(mr->MediaId, ed1));
1500   retval = SqlQueryWithHandler(cmd, DbListHandler, lst);
1501   DbUnlock(this);
1502   return retval;
1503 }
1504 
1505 /**
1506  * This function returns the sum of all the Clients JobBytes.
1507  *
1508  * Returns false: on failure
1509  *         true: on success
1510  */
get_quota_jobbytes(JobControlRecord * jcr,JobDbRecord * jr,utime_t JobRetention)1511 bool BareosDb::get_quota_jobbytes(JobControlRecord* jcr,
1512                                   JobDbRecord* jr,
1513                                   utime_t JobRetention)
1514 {
1515   SQL_ROW row;
1516   int num_rows;
1517   char dt[MAX_TIME_LENGTH];
1518   char ed1[50], ed2[50];
1519   bool retval = false;
1520   time_t now, schedtime;
1521 
1522   /*
1523    * Determine the first schedtime we are interested in.
1524    */
1525   now = time(NULL);
1526   schedtime = now - JobRetention;
1527 
1528   /*
1529    * Bugfix, theres a small timing bug in the scheduler.
1530    * Add 5 seconds to the schedtime to ensure the
1531    * last job from the job retention gets excluded.
1532    */
1533   schedtime += 5;
1534 
1535   bstrutime(dt, sizeof(dt), schedtime);
1536 
1537   DbLock(this);
1538 
1539   FillQuery(SQL_QUERY_get_quota_jobbytes, edit_uint64(jr->ClientId, ed1),
1540             edit_uint64(jr->JobId, ed2), dt);
1541   if (QUERY_DB(jcr, cmd)) {
1542     num_rows = SqlNumRows();
1543     if (num_rows == 1) {
1544       row = SqlFetchRow();
1545       jr->JobSumTotalBytes = str_to_uint64(row[0]);
1546     } else if (num_rows < 1) {
1547       jr->JobSumTotalBytes = 0;
1548     }
1549     SqlFreeResult();
1550     retval = true;
1551   } else {
1552     Mmsg(errmsg, _("JobBytes sum select failed: ERR=%s\n"), sql_strerror());
1553     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1554   }
1555 
1556   DbUnlock(this);
1557   return retval;
1558 }
1559 
1560 /**
1561  * This function returns the sum of all the Clients JobBytes of non failed jobs.
1562  *
1563  * Returns false: on failure
1564  *         true: on success
1565  */
get_quota_jobbytes_nofailed(JobControlRecord * jcr,JobDbRecord * jr,utime_t JobRetention)1566 bool BareosDb::get_quota_jobbytes_nofailed(JobControlRecord* jcr,
1567                                            JobDbRecord* jr,
1568                                            utime_t JobRetention)
1569 {
1570   SQL_ROW row;
1571   char ed1[50], ed2[50];
1572   int num_rows;
1573   char dt[MAX_TIME_LENGTH];
1574   bool retval = false;
1575   time_t now, schedtime;
1576 
1577   /*
1578    * Determine the first schedtime we are interested in.
1579    */
1580   now = time(NULL);
1581   schedtime = now - JobRetention;
1582 
1583   /*
1584    * Bugfix, theres a small timing bug in the scheduler.
1585    * Add 5 seconds to the schedtime to ensure the
1586    * last job from the job retention gets excluded.
1587    */
1588   schedtime += 5;
1589 
1590   bstrutime(dt, sizeof(dt), schedtime);
1591 
1592   DbLock(this);
1593 
1594   FillQuery(SQL_QUERY_get_quota_jobbytes_nofailed,
1595             edit_uint64(jr->ClientId, ed1), edit_uint64(jr->JobId, ed2), dt);
1596   if (QUERY_DB(jcr, cmd)) {
1597     num_rows = SqlNumRows();
1598     if (num_rows == 1) {
1599       row = SqlFetchRow();
1600       jr->JobSumTotalBytes = str_to_uint64(row[0]);
1601     } else if (num_rows < 1) {
1602       jr->JobSumTotalBytes = 0;
1603     }
1604     SqlFreeResult();
1605     retval = true;
1606   } else {
1607     Mmsg(errmsg, _("JobBytes sum select failed: ERR=%s\n"), sql_strerror());
1608     Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1609   }
1610 
1611   DbUnlock(this);
1612   return retval;
1613 }
1614 
1615 /**
1616  * Fetch the quota value and grace time for a quota.
1617  * Returns false: on failure
1618  *         true: on success
1619  */
GetQuotaRecord(JobControlRecord * jcr,ClientDbRecord * cdbr)1620 bool BareosDb::GetQuotaRecord(JobControlRecord* jcr, ClientDbRecord* cdbr)
1621 {
1622   SQL_ROW row;
1623   char ed1[50];
1624   int num_rows;
1625   bool retval = false;
1626 
1627   DbLock(this);
1628   Mmsg(cmd,
1629        "SELECT GraceTime, QuotaLimit "
1630        "FROM Quota "
1631        "WHERE ClientId = %s",
1632        edit_int64(cdbr->ClientId, ed1));
1633   if (QUERY_DB(jcr, cmd)) {
1634     num_rows = SqlNumRows();
1635     if (num_rows == 1) {
1636       if ((row = SqlFetchRow()) == NULL) {
1637         Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1638         Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1639         SqlFreeResult();
1640       } else {
1641         cdbr->GraceTime = str_to_uint64(row[0]);
1642         cdbr->QuotaLimit = str_to_int64(row[1]);
1643         SqlFreeResult();
1644         retval = true;
1645       }
1646     } else {
1647       Mmsg(errmsg, _("Quota record not found in Catalog.\n"));
1648       SqlFreeResult();
1649     }
1650   } else {
1651     Mmsg(errmsg, _("Quota record not found in Catalog.\n"));
1652   }
1653 
1654   DbUnlock(this);
1655   return retval;
1656 }
1657 
1658 /**
1659  * Fetch the NDMP Dump Level value.
1660  *
1661  * Returns dumplevel on success
1662  *         0: on failure
1663  */
GetNdmpLevelMapping(JobControlRecord * jcr,JobDbRecord * jr,char * filesystem)1664 int BareosDb::GetNdmpLevelMapping(JobControlRecord* jcr,
1665                                   JobDbRecord* jr,
1666                                   char* filesystem)
1667 {
1668   SQL_ROW row;
1669   char ed1[50], ed2[50];
1670   int num_rows;
1671   int dumplevel = 0;
1672 
1673   DbLock(this);
1674 
1675   esc_name = CheckPoolMemorySize(esc_name, strlen(filesystem) * 2 + 1);
1676   EscapeString(jcr, esc_name, filesystem, strlen(filesystem));
1677 
1678   Mmsg(cmd,
1679        "SELECT DumpLevel FROM NDMPLevelMap WHERE "
1680        "ClientId='%s' AND FileSetId='%s' AND FileSystem='%s'",
1681        edit_uint64(jr->ClientId, ed1), edit_uint64(jr->FileSetId, ed2),
1682        esc_name);
1683 
1684   if (QUERY_DB(jcr, cmd)) {
1685     num_rows = SqlNumRows();
1686     if (num_rows == 1) {
1687       if ((row = SqlFetchRow()) == NULL) {
1688         Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1689         Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1690         SqlFreeResult();
1691         goto bail_out;
1692       } else {
1693         dumplevel = str_to_uint64(row[0]);
1694         dumplevel++; /* select next dumplevel */
1695         SqlFreeResult();
1696         goto bail_out;
1697       }
1698     } else {
1699       Mmsg(errmsg, _("NDMP Dump Level record not found in Catalog.\n"));
1700       SqlFreeResult();
1701       goto bail_out;
1702     }
1703   } else {
1704     Mmsg(errmsg, _("NDMP Dump Level record not found in Catalog.\n"));
1705     goto bail_out;
1706   }
1707 
1708 bail_out:
1709   DbUnlock(this);
1710   return dumplevel;
1711 }
1712 
1713 /**
1714  * CountingHandler() with a CountContext* can be used to count the number of
1715  * times that SqlQueryWithHandler() calls the handler.
1716  * This is not neccesarily the number of rows, because the ResultHandler can
1717  * stop processing of further rows by returning non-zero.
1718  */
1719 struct CountContext {
1720   DB_RESULT_HANDLER* handler;
1721   void* ctx;
1722   int count;
1723 
CountContextCountContext1724   CountContext(DB_RESULT_HANDLER* t_handler, void* t_ctx)
1725       : handler(t_handler), ctx(t_ctx), count(0)
1726   {
1727   }
1728 };
1729 
CountingHandler(void * counting_ctx,int num_fields,char ** rows)1730 static int CountingHandler(void* counting_ctx, int num_fields, char** rows)
1731 {
1732   auto* c = static_cast<struct CountContext*>(counting_ctx);
1733   c->count++;
1734   return c->handler(c->ctx, num_fields, rows);
1735 }
1736 
1737 /**
1738  * Fetch NDMP Job Environment based on raw SQL query.
1739  * Returns false: on sql failure or when 0 rows were returned
1740  *         true:  otherwise
1741  */
GetNdmpEnvironmentString(const std::string & query,DB_RESULT_HANDLER * ResultHandler,void * ctx)1742 bool BareosDb::GetNdmpEnvironmentString(const std::string& query,
1743                                         DB_RESULT_HANDLER* ResultHandler,
1744                                         void* ctx)
1745 {
1746   auto myctx = std::unique_ptr<CountContext>(new CountContext(ResultHandler, ctx));
1747   bool status =
1748       SqlQueryWithHandler(query.c_str(), CountingHandler, myctx.get());
1749   Dmsg1(150, "Got %d NDMP environment records\n", myctx->count);
1750   return status && myctx->count > 0;  // no rows means no environment was found
1751 }
1752 
1753 /**
1754  * Fetch the NDMP Job Environment Strings based on JobId only
1755  *
1756  * Returns false: on failure
1757  *         true: on success
1758  */
GetNdmpEnvironmentString(JobId_t JobId,DB_RESULT_HANDLER * ResultHandler,void * ctx)1759 bool BareosDb::GetNdmpEnvironmentString(JobId_t JobId,
1760                                         DB_RESULT_HANDLER* ResultHandler,
1761                                         void* ctx)
1762 {
1763   ASSERT(JobId > 0)
1764   std::string query{"SELECT EnvName, EnvValue FROM NDMPJobEnvironment"};
1765   query += " WHERE JobId=" + std::to_string(JobId);
1766 
1767   return GetNdmpEnvironmentString(query, ResultHandler, ctx);
1768 }
1769 
1770 /**
1771  * Fetch the NDMP Job Environment Strings based on JobId and FileIndex
1772  *
1773  * Returns false: on failure
1774  *         true: on success
1775  */
GetNdmpEnvironmentString(JobId_t JobId,int32_t FileIndex,DB_RESULT_HANDLER * ResultHandler,void * ctx)1776 bool BareosDb::GetNdmpEnvironmentString(JobId_t JobId,
1777                                         int32_t FileIndex,
1778                                         DB_RESULT_HANDLER* ResultHandler,
1779                                         void* ctx)
1780 {
1781   ASSERT(JobId > 0)
1782   std::string query{"SELECT EnvName, EnvValue FROM NDMPJobEnvironment"};
1783   query += " WHERE JobId=" + std::to_string(JobId);
1784   query += " AND FileIndex=" + std::to_string(FileIndex);
1785 
1786   return GetNdmpEnvironmentString(query, ResultHandler, ctx);
1787 }
1788 
1789 
1790 /**
1791  * Fetch the NDMP Job Environment Strings for NDMP_BAREOS Backups
1792  *
1793  * Returns false: on failure
1794  *         true: on success
1795  */
GetNdmpEnvironmentString(const VolumeSessionInfo & vsi,int32_t FileIndex,DB_RESULT_HANDLER * ResultHandler,void * ctx)1796 bool BareosDb::GetNdmpEnvironmentString(const VolumeSessionInfo& vsi,
1797                                         int32_t FileIndex,
1798                                         DB_RESULT_HANDLER* ResultHandler,
1799                                         void* ctx)
1800 {
1801   db_int64_ctx lctx;
1802   std::string query{"SELECT JobId FROM Job"};
1803   query += " WHERE VolSessionId = " + std::to_string(vsi.id);
1804   query += " AND VolSessionTime = " + std::to_string(vsi.time);
1805 
1806   if (SqlQueryWithHandler(query.c_str(), db_int64_handler, &lctx)) {
1807     if (lctx.count == 1) {
1808       /* now lctx.value contains the jobid we restore */
1809       return GetNdmpEnvironmentString(lctx.value, FileIndex, ResultHandler,
1810                                       ctx);
1811     }
1812   }
1813   Dmsg3(
1814       100,
1815       "Got %d JobIds for VolSessionTime=%lld VolSessionId=%lld instead of 1\n",
1816       lctx.count, vsi.time, vsi.id);
1817   return false;
1818 }
1819 
1820 /**
1821  * This function creates a sql query string at cmd to return a list of all the
1822  * Media records for the current Pool, the correct Media Type, Recyle, Enabled,
1823  * StorageId, VolBytes and volumes or VolumeName if specified. Comma separated
1824  * list of volumes takes precedence over VolumeName. The caller must free ids if
1825  * non-NULL.
1826  */
PrepareMediaSqlQuery(JobControlRecord * jcr,MediaDbRecord * mr,PoolMem * querystring,PoolMem & volumes)1827 bool BareosDb::PrepareMediaSqlQuery(JobControlRecord* jcr,
1828                                     MediaDbRecord* mr,
1829                                     PoolMem* querystring,
1830                                     PoolMem& volumes)
1831 {
1832   bool ok = true;
1833   char ed1[50];
1834   char esc[MAX_NAME_LENGTH * 2 + 1];
1835   PoolMem buf(PM_MESSAGE);
1836 
1837   /*
1838    * columns we care of.
1839    * Reduced, to be better displayable.
1840    * Important:
1841    * column 2: pool.name, column 3: storage.name,
1842    * as this is used for ACL handling (counting starts at 0).
1843    */
1844   const char* columns =
1845       "Media.MediaId,"
1846       "Media.VolumeName,"
1847       "Pool.Name AS Pool,"
1848       "Storage.Name AS Storage,"
1849       "Media.MediaType,"
1850       /* "Media.DeviceId," */
1851       /* "Media.FirstWritten, "*/
1852       "Media.LastWritten,"
1853       "Media.VolFiles,"
1854       "Media.VolBytes,"
1855       "Media.VolStatus,"
1856       /* "Media.Recycle AS Recycle," */
1857       "Media.ActionOnPurge,"
1858       /* "Media.VolRetention," */
1859       "Media.Comment";
1860 
1861   Mmsg(querystring,
1862        "SELECT DISTINCT %s FROM Media "
1863        "LEFT JOIN Pool USING(PoolId) "
1864        "LEFT JOIN Storage USING(StorageId) "
1865        "WHERE Media.Recycle=%d AND Media.Enabled=%d ",
1866        columns, mr->Recycle, mr->Enabled);
1867 
1868   if (*mr->MediaType) {
1869     EscapeString(jcr, esc, mr->MediaType, strlen(mr->MediaType));
1870     Mmsg(buf, "AND Media.MediaType='%s' ", esc);
1871     PmStrcat(querystring, buf.c_str());
1872   }
1873 
1874   if (mr->StorageId) {
1875     Mmsg(buf, "AND Media.StorageId=%s ", edit_uint64(mr->StorageId, ed1));
1876     PmStrcat(querystring, buf.c_str());
1877   }
1878 
1879   if (mr->PoolId) {
1880     Mmsg(buf, "AND Media.PoolId=%s ", edit_uint64(mr->PoolId, ed1));
1881     PmStrcat(querystring, buf.c_str());
1882   }
1883 
1884   if (mr->VolBytes) {
1885     Mmsg(buf, "AND Media.VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
1886     PmStrcat(querystring, buf.c_str());
1887   }
1888 
1889   if (*mr->VolStatus) {
1890     EscapeString(jcr, esc, mr->VolStatus, strlen(mr->VolStatus));
1891     Mmsg(buf, "AND Media.VolStatus = '%s' ", esc);
1892     PmStrcat(querystring, buf.c_str());
1893   }
1894 
1895   if (volumes.strlen() > 0) {
1896     /* extra list of volumes given */
1897     Mmsg(buf, "AND Media.VolumeName IN (%s) ", volumes.c_str());
1898     PmStrcat(querystring, buf.c_str());
1899   } else if (*mr->VolumeName) {
1900     /* single volume given in media record */
1901     EscapeString(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1902     Mmsg(buf, "AND Media.VolumeName = '%s' ", esc);
1903     PmStrcat(querystring, buf.c_str());
1904   }
1905 
1906   Dmsg1(100, "query=%s\n", querystring);
1907 
1908   return ok;
1909 }
1910 
1911 /**
1912  * verify that all media use the same storage.
1913  */
VerifyMediaIdsFromSingleStorage(JobControlRecord * jcr,dbid_list & mediaIds)1914 bool BareosDb::VerifyMediaIdsFromSingleStorage(JobControlRecord* jcr,
1915                                                dbid_list& mediaIds)
1916 {
1917   MediaDbRecord mr;
1918   DBId_t storageId = 0;
1919 
1920   for (int i = 0; i < mediaIds.size(); i++) {
1921     memset(&mr, 0, sizeof(mr));
1922     mr.MediaId = mediaIds.get(i);
1923     if (!GetMediaRecord(jcr, &mr)) {
1924       Mmsg1(errmsg, _("Failed to find MediaId=%lld\n"), (uint64_t)mr.MediaId);
1925       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1926       return false;
1927     } else if (i == 0) {
1928       storageId = mr.StorageId;
1929     } else if (storageId != mr.StorageId) {
1930       return false;
1931     }
1932   }
1933   return true;
1934 }
1935 
1936 
1937 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || \
1938           HAVE_DBI */
1939