1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * Bacula Catalog Database Create record interface routines
21  *
22  *    Written by Kern Sibbald, March 2000
23  *
24  */
25 
26 #include  "bacula.h"
27 
28 static const int dbglevel = 100;
29 
30 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
31 
32 #include  "cats.h"
33 
34 /* -----------------------------------------------------------------------
35  *
36  *   Generic Routines (or almost generic)
37  *
38  * -----------------------------------------------------------------------
39  */
40 
41 /** Create a new record for the Job
42  *  Returns: false on failure
43  *          true  on success
44  */
bdb_create_job_record(JCR * jcr,JOB_DBR * jr)45 bool BDB::bdb_create_job_record(JCR *jcr, JOB_DBR *jr)
46 {
47    POOL_MEM buf;
48    char dt[MAX_TIME_LENGTH], dt2[MAX_TIME_LENGTH];
49    time_t stime, starttime;
50    struct tm tm;
51    bool ok;
52    int len;
53    utime_t JobTDate;
54    char ed1[30],ed2[30];
55    char esc_job[MAX_ESCAPE_NAME_LENGTH];
56    char esc_name[MAX_ESCAPE_NAME_LENGTH];
57 
58    bdb_lock();
59 
60    stime = jr->SchedTime;
61    starttime = jr->StartTime;
62    ASSERT(stime != 0);
63 
64    (void)localtime_r(&stime, &tm);
65    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
66 
67    (void)localtime_r(&starttime, &tm);
68    strftime(dt2, sizeof(dt2), "%Y-%m-%d %H:%M:%S", &tm);
69    JobTDate = (utime_t)stime;
70 
71    len = strlen(jcr->comment);  /* TODO: use jr instead of jcr to get comment */
72    buf.check_size(len*2+1);
73    bdb_escape_string(jcr, buf.c_str(), jcr->comment, len);
74 
75    bdb_escape_string(jcr, esc_job, jr->Job, strlen(jr->Job));
76    bdb_escape_string(jcr, esc_name, jr->Name, strlen(jr->Name));
77 
78    /* Must create it */
79    Mmsg(cmd,
80 "INSERT INTO Job (Job,Name,Type,Level,JobStatus,StartTime,SchedTime,JobTDate,"
81                  "ClientId,Comment) "
82 "VALUES ('%s','%s','%c','%c','%c','%s','%s',%s,%s,'%s')",
83            esc_job, esc_name, (char)(jr->JobType), (char)(jr->JobLevel),
84           (char)(jr->JobStatus), dt2, dt, edit_uint64(JobTDate, ed1),
85            edit_int64(jr->ClientId, ed2), buf.c_str());
86 
87    if ((jr->JobId = sql_insert_autokey_record(cmd, NT_("Job"))) == 0) {
88       Mmsg2(&errmsg, _("Create DB Job record %s failed. ERR=%s\n"),
89             cmd, sql_strerror());
90       ok = false;
91    } else {
92       ok = true;
93    }
94    bdb_unlock();
95    return ok;
96 }
97 
98 
99 /** Create a JobMedia record for medium used this job
100  *  Returns: false on failure
101  *          true  on success
102  */
bdb_create_jobmedia_record(JCR * jcr,JOBMEDIA_DBR * jm)103 bool BDB::bdb_create_jobmedia_record(JCR *jcr, JOBMEDIA_DBR *jm)
104 {
105    bool ok = true;
106    int count;
107    char ed1[50], ed2[50];
108 
109    bdb_lock();
110 
111    /* Now get count for VolIndex */
112    Mmsg(cmd, "SELECT MAX(VolIndex) from JobMedia WHERE JobId=%s",
113         edit_int64(jm->JobId, ed1));
114    count = get_sql_record_max(jcr, this);
115 
116    if (count < 0) {
117       count = 0;
118    }
119    count++;
120 
121    Mmsg(cmd,
122         "INSERT INTO JobMedia (JobId,MediaId,FirstIndex,LastIndex,"
123         "StartFile,EndFile,StartBlock,EndBlock,VolIndex) "
124         "VALUES (%s,%s,%u,%u,%u,%u,%u,%u,%u)",
125         edit_int64(jm->JobId, ed1),
126         edit_int64(jm->MediaId, ed2),
127         jm->FirstIndex, jm->LastIndex,
128         jm->StartFile, jm->EndFile, jm->StartBlock, jm->EndBlock,count);
129 
130    Dmsg0(300, cmd);
131    if (!InsertDB(jcr, cmd)) {
132       Mmsg2(&errmsg, _("Create JobMedia record %s failed: ERR=%s\n"), cmd,
133          sql_strerror());
134       ok = false;
135    } else {
136       /* Worked, now update the Media record with the EndFile and EndBlock */
137       Mmsg(cmd,
138            "UPDATE Media SET EndFile=%lu, EndBlock=%lu WHERE MediaId=%lu",
139            jm->EndFile, jm->EndBlock, jm->MediaId);
140       if (!UpdateDB(jcr, cmd, false)) {
141          Mmsg2(&errmsg, _("Update Media record %s failed: ERR=%s\n"), cmd,
142               sql_strerror());
143          ok = false;
144       }
145    }
146    bdb_unlock();
147    Dmsg0(300, "Return from JobMedia\n");
148    return ok;
149 }
150 
151 
152 /** Create a fileMedia record for File index
153  *  Returns: false on failure
154  *          true  on success
155  */
bdb_create_filemedia_record(JCR * jcr,FILEMEDIA_DBR * fm)156 bool BDB::bdb_create_filemedia_record(JCR *jcr, FILEMEDIA_DBR *fm)
157 {
158    bool ok = true;
159    char ed1[50], ed2[50];
160 
161    bdb_lock();
162 
163    Mmsg(cmd,
164         "INSERT INTO FileMedia (JobId,MediaId,FileIndex,BlockAddress,RecordNo,"
165         "FileOffset) "
166         "VALUES (%s,%s,%u,%lld,%u,%lld)",
167         edit_int64(fm->JobId, ed1),
168         edit_int64(fm->MediaId, ed2),
169         fm->FileIndex, fm->BlockAddress,
170         fm->RecordNo, fm->FileOffset);
171 
172    Dmsg0(300, cmd);
173    if (!InsertDB(jcr, cmd)) {
174       Mmsg2(&errmsg, _("Create FileMedia record %s failed: ERR=%s\n"), cmd,
175          sql_strerror());
176       ok = false;
177    }
178    bdb_unlock();
179    return ok;
180 }
181 
182 /** Create Unique Pool record
183  *  Returns: false on failure
184  *          true  on success
185  */
bdb_create_pool_record(JCR * jcr,POOL_DBR * pr)186 bool BDB::bdb_create_pool_record(JCR *jcr, POOL_DBR *pr)
187 {
188    bool stat;
189    char ed1[30], ed2[30], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50];
190    char esc_name[MAX_ESCAPE_NAME_LENGTH];
191    char esc_lf[MAX_ESCAPE_NAME_LENGTH];
192    char esc_type[MAX_ESCAPE_NAME_LENGTH];
193 
194    /* The pooltype should match the case when we insert it to the catalog
195     *  backup -> Backup, BACKUP -> Backup
196     */
197    ucfirst(esc_type, pr->PoolType, sizeof(esc_type));
198 
199    Dmsg0(200, "In create pool\n");
200    bdb_lock();
201    bdb_escape_string(jcr, esc_name, pr->Name, strlen(pr->Name));
202    bdb_escape_string(jcr, esc_lf, pr->LabelFormat, strlen(pr->LabelFormat));
203    Mmsg(cmd, "SELECT PoolId,Name FROM Pool WHERE Name='%s'", esc_name);
204    Dmsg1(200, "selectpool: %s\n", cmd);
205 
206    if (QueryDB(jcr, cmd)) {
207       if (sql_num_rows() > 0) {
208          Mmsg1(&errmsg, _("pool record %s already exists\n"), pr->Name);
209          sql_free_result();
210          bdb_unlock();
211          Dmsg1(200, "%s", errmsg); /* pool already exists */
212          return false;
213       }
214       sql_free_result();
215    }
216 
217    /* Must create it */
218    Mmsg(cmd,
219 "INSERT INTO Pool (Name,NumVols,MaxVols,UseOnce,UseCatalog,"
220 "AcceptAnyVolume,AutoPrune,Recycle,VolRetention,VolUseDuration,"
221 "MaxVolJobs,MaxVolFiles,MaxVolBytes,PoolType,LabelType,LabelFormat,"
222 "RecyclePoolId,ScratchPoolId,ActionOnPurge,CacheRetention,MaxPoolBytes) "
223 "VALUES ('%s',%u,%u,%d,%d,%d,%d,%d,%s,%s,%u,%u,%s,'%s',%d,'%s',%s,%s,%d,%s,%s)",
224                   esc_name,
225                   pr->NumVols, pr->MaxVols,
226                   pr->UseOnce, pr->UseCatalog,
227                   pr->AcceptAnyVolume,
228                   pr->AutoPrune, pr->Recycle,
229                   edit_uint64(pr->VolRetention, ed1),
230                   edit_uint64(pr->VolUseDuration, ed2),
231                   pr->MaxVolJobs, pr->MaxVolFiles,
232                   edit_uint64(pr->MaxVolBytes, ed3),
233                   esc_type, pr->LabelType, esc_lf,
234                   edit_int64(pr->RecyclePoolId,ed4),
235                   edit_int64(pr->ScratchPoolId,ed5),
236                   pr->ActionOnPurge,
237                   edit_uint64(pr->CacheRetention, ed6),
238                   edit_int64(pr->MaxPoolBytes, ed7)
239       );
240    Dmsg1(200, "Create Pool: %s\n", cmd);
241    if ((pr->PoolId = sql_insert_autokey_record(cmd, NT_("Pool"))) == 0) {
242       Mmsg2(&errmsg, _("Create db Pool record %s failed: ERR=%s\n"),
243             cmd, sql_strerror());
244       stat = false;
245    } else {
246       stat = true;
247    }
248    bdb_unlock();
249    return stat;
250 }
251 
252 /**
253  * Create Unique Device record
254  * Returns: false on failure
255  *          true  on success
256  */
bdb_create_device_record(JCR * jcr,DEVICE_DBR * dr)257 bool BDB::bdb_create_device_record(JCR *jcr, DEVICE_DBR *dr)
258 {
259    bool ok;
260    char ed1[30], ed2[30];
261    char esc[MAX_ESCAPE_NAME_LENGTH];
262 
263    Dmsg0(200, "In create Device\n");
264    bdb_lock();
265    bdb_escape_string(jcr, esc, dr->Name, strlen(dr->Name));
266    Mmsg(cmd, "SELECT DeviceId,Name FROM Device WHERE Name='%s'", esc);
267    Dmsg1(200, "selectdevice: %s\n", cmd);
268 
269    if (QueryDB(jcr, cmd)) {
270       if (sql_num_rows() > 0) {
271          Mmsg1(&errmsg, _("Device record %s already exists\n"), dr->Name);
272          sql_free_result();
273          bdb_unlock();
274          return false;
275       }
276       sql_free_result();
277    }
278 
279    /* Must create it */
280    Mmsg(cmd,
281 "INSERT INTO Device (Name,MediaTypeId,StorageId) VALUES ('%s',%s,%s)",
282                   esc,
283                   edit_uint64(dr->MediaTypeId, ed1),
284                   edit_int64(dr->StorageId, ed2));
285    Dmsg1(200, "Create Device: %s\n", cmd);
286    if ((dr->DeviceId = sql_insert_autokey_record(cmd, NT_("Device"))) == 0) {
287       Mmsg2(&errmsg, _("Create db Device record %s failed: ERR=%s\n"),
288             cmd, sql_strerror());
289       ok = false;
290    } else {
291       ok = true;
292    }
293    bdb_unlock();
294    return ok;
295 }
296 
297 
298 
299 /**
300  * Create a Unique record for Storage -- no duplicates
301  * Returns: false on failure
302  *          true  on success with id in sr->StorageId
303  */
bdb_create_storage_record(JCR * jcr,STORAGE_DBR * sr)304 bool BDB::bdb_create_storage_record(JCR *jcr, STORAGE_DBR *sr)
305 {
306    SQL_ROW row;
307    bool ok;
308    char esc[MAX_ESCAPE_NAME_LENGTH];
309 
310    bdb_lock();
311    bdb_escape_string(jcr, esc, sr->Name, strlen(sr->Name));
312    Mmsg(cmd, "SELECT StorageId,AutoChanger FROM Storage WHERE Name='%s'",esc);
313 
314    sr->StorageId = 0;
315    sr->created = false;
316    /* Check if it already exists */
317    if (QueryDB(jcr, cmd)) {
318       /* If more than one, report error, but return first row */
319       if (sql_num_rows() > 1) {
320          Mmsg1(&errmsg, _("More than one Storage record!: %d\n"), sql_num_rows());
321          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
322       }
323       if (sql_num_rows() >= 1) {
324          if ((row = sql_fetch_row()) == NULL) {
325             Mmsg1(&errmsg, _("error fetching Storage row: %s\n"), sql_strerror());
326             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
327             sql_free_result();
328             bdb_unlock();
329             return false;
330          }
331          sr->StorageId = str_to_int64(row[0]);
332          sr->AutoChanger = atoi(row[1]);   /* bool */
333          sql_free_result();
334          bdb_unlock();
335          return true;
336       }
337       sql_free_result();
338    }
339 
340    /* Must create it */
341    Mmsg(cmd, "INSERT INTO Storage (Name,AutoChanger)"
342         " VALUES ('%s',%d)", esc, sr->AutoChanger);
343 
344    if ((sr->StorageId = sql_insert_autokey_record(cmd, NT_("Storage"))) == 0) {
345       Mmsg2(&errmsg, _("Create DB Storage record %s failed. ERR=%s\n"),
346             cmd, sql_strerror());
347       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
348       ok = false;
349    } else {
350       sr->created = true;
351       ok = true;
352    }
353    bdb_unlock();
354    return ok;
355 }
356 
357 
358 /**
359  * Create Unique MediaType record
360  * Returns: false on failure
361  *          true  on success
362  */
bdb_create_mediatype_record(JCR * jcr,MEDIATYPE_DBR * mr)363 bool BDB::bdb_create_mediatype_record(JCR *jcr, MEDIATYPE_DBR *mr)
364 {
365    bool stat;
366    char esc[MAX_ESCAPE_NAME_LENGTH];
367 
368    Dmsg0(200, "In create mediatype\n");
369    bdb_lock();
370    bdb_escape_string(jcr, esc, mr->MediaType, strlen(mr->MediaType));
371    Mmsg(cmd, "SELECT MediaTypeId,MediaType FROM MediaType WHERE MediaType='%s'", esc);
372    Dmsg1(200, "selectmediatype: %s\n", cmd);
373 
374    if (QueryDB(jcr, cmd)) {
375       if (sql_num_rows() > 0) {
376          Mmsg1(&errmsg, _("mediatype record %s already exists\n"), mr->MediaType);
377          sql_free_result();
378          bdb_unlock();
379          return false;
380       }
381       sql_free_result();
382    }
383 
384    /* Must create it */
385    Mmsg(cmd,
386 "INSERT INTO MediaType (MediaType,ReadOnly) "
387 "VALUES ('%s',%d)",
388                   mr->MediaType,
389                   mr->ReadOnly);
390    Dmsg1(200, "Create mediatype: %s\n", cmd);
391    if ((mr->MediaTypeId = sql_insert_autokey_record(cmd, NT_("MediaType"))) == 0) {
392       Mmsg2(&errmsg, _("Create db mediatype record %s failed: ERR=%s\n"),
393             cmd, sql_strerror());
394       stat = false;
395    } else {
396       stat = true;
397    }
398    bdb_unlock();
399    return stat;
400 }
401 
402 
403 /**
404  * Create Media record. VolumeName and non-zero Slot must be unique
405  *
406  * Returns: 0 on failure
407  *          1 on success
408  */
bdb_create_media_record(JCR * jcr,MEDIA_DBR * mr)409 int BDB::bdb_create_media_record(JCR *jcr, MEDIA_DBR *mr)
410 {
411    int stat;
412    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50];
413    char ed9[50], ed10[50], ed11[50], ed12[50], ed13[50], ed14[50];
414    struct tm tm;
415    char esc_name[MAX_ESCAPE_NAME_LENGTH];
416    char esc_mtype[MAX_ESCAPE_NAME_LENGTH];
417    char esc_status[MAX_ESCAPE_NAME_LENGTH];
418 
419 
420    bdb_lock();
421    bdb_escape_string(jcr, esc_name, mr->VolumeName, strlen(mr->VolumeName));
422    bdb_escape_string(jcr, esc_mtype, mr->MediaType, strlen(mr->MediaType));
423    bdb_escape_string(jcr, esc_status, mr->VolStatus, strlen(mr->VolStatus));
424 
425    Mmsg(cmd, "SELECT MediaId FROM Media WHERE VolumeName='%s'", esc_name);
426    Dmsg1(500, "selectpool: %s\n", cmd);
427 
428    if (QueryDB(jcr, cmd)) {
429       if (sql_num_rows() > 0) {
430          Mmsg1(&errmsg, _("Volume \"%s\" already exists.\n"), mr->VolumeName);
431          sql_free_result();
432          bdb_unlock();
433          return 0;
434       }
435       sql_free_result();
436    }
437 
438    /* Must create it */
439    Mmsg(cmd,
440 "INSERT INTO Media (VolumeName,MediaType,MediaTypeId,PoolId,MaxVolBytes,"
441 "VolCapacityBytes,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
442 "VolStatus,Slot,VolBytes,InChanger,VolReadTime,VolWriteTime,VolType,"
443 "VolParts,VolCloudParts,LastPartBytes,"
444 "EndFile,EndBlock,LabelType,StorageId,DeviceId,LocationId,"
445 "ScratchPoolId,RecyclePoolId,Enabled,ActionOnPurge,CacheRetention)"
446 "VALUES ('%s','%s',0,%u,%s,%s,%d,%s,%s,%u,%u,'%s',%d,%s,%d,%s,%s,%d,"
447         "%d,%d,'%s',%d,%d,%d,%s,%s,%s,%s,%s,%d,%d,%s)",
448           esc_name,
449           esc_mtype, mr->PoolId,
450           edit_uint64(mr->MaxVolBytes,ed1),
451           edit_uint64(mr->VolCapacityBytes, ed2),
452           mr->Recycle,
453           edit_uint64(mr->VolRetention, ed3),
454           edit_uint64(mr->VolUseDuration, ed4),
455           mr->MaxVolJobs,
456           mr->MaxVolFiles,
457           esc_status,
458           mr->Slot,
459           edit_uint64(mr->VolBytes, ed5),
460           mr->InChanger,
461           edit_int64(mr->VolReadTime, ed6),
462           edit_int64(mr->VolWriteTime, ed7),
463           mr->VolType,
464           mr->VolParts,
465           mr->VolCloudParts,
466           edit_uint64(mr->LastPartBytes, ed8),
467           mr->EndFile,
468           mr->EndBlock,
469           mr->LabelType,
470           edit_int64(mr->StorageId, ed9),
471           edit_int64(mr->DeviceId, ed10),
472           edit_int64(mr->LocationId, ed11),
473           edit_int64(mr->ScratchPoolId, ed12),
474           edit_int64(mr->RecyclePoolId, ed13),
475           mr->Enabled,
476           mr->ActionOnPurge,
477           edit_uint64(mr->CacheRetention, ed14)
478           );
479 
480 
481    Dmsg1(500, "Create Volume: %s\n", cmd);
482    if ((mr->MediaId = sql_insert_autokey_record(cmd, NT_("Media"))) == 0) {
483       Mmsg2(&errmsg, _("Create DB Media record %s failed. ERR=%s\n"),
484             cmd, sql_strerror());
485       stat = 0;
486    } else {
487       stat = 1;
488       if (mr->set_label_date) {
489          char dt[MAX_TIME_LENGTH];
490          if (mr->LabelDate == 0) {
491             mr->LabelDate = time(NULL);
492          }
493          (void)localtime_r(&mr->LabelDate, &tm);
494          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
495          Mmsg(cmd, "UPDATE Media SET LabelDate='%s' "
496               "WHERE MediaId=%lu", dt, mr->MediaId);
497          stat = UpdateDB(jcr, cmd, false);
498       }
499       /*
500        * Make sure that if InChanger is non-zero any other identical slot
501        *   has InChanger zero.
502        */
503       db_make_inchanger_unique(jcr, this, mr);
504    }
505 
506    bdb_unlock();
507    return stat;
508 }
509 
510 /**
511  * Create a Unique record for the client -- no duplicates
512  * Returns: 0 on failure
513  *          1 on success with id in cr->ClientId
514  */
bdb_create_client_record(JCR * jcr,CLIENT_DBR * cr)515 int BDB::bdb_create_client_record(JCR *jcr, CLIENT_DBR *cr)
516 {
517    SQL_ROW row;
518    int stat;
519    char ed1[50], ed2[50];
520    char esc_name[MAX_ESCAPE_NAME_LENGTH];
521    char esc_uname[MAX_ESCAPE_NAME_LENGTH];
522 
523    bdb_lock();
524    bdb_escape_string(jcr, esc_name, cr->Name, strlen(cr->Name));
525    bdb_escape_string(jcr, esc_uname, cr->Uname, strlen(cr->Uname));
526    Mmsg(cmd, "SELECT ClientId,Uname,AutoPrune,"
527         "FileRetention,JobRetention FROM Client WHERE Name='%s'",esc_name);
528 
529    cr->ClientId = 0;
530    if (QueryDB(jcr, cmd)) {
531       /* If more than one, report error, but return first row */
532       if (sql_num_rows() > 1) {
533          Mmsg1(&errmsg, _("More than one Client!: %d\n"), sql_num_rows());
534          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
535       }
536       if (sql_num_rows() >= 1) {
537          if ((row = sql_fetch_row()) == NULL) {
538             Mmsg1(&errmsg, _("error fetching Client row: %s\n"), sql_strerror());
539             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
540             sql_free_result();
541             bdb_unlock();
542             return 0;
543          }
544          cr->ClientId = str_to_int64(row[0]);
545          if (row[1]) {
546             bstrncpy(cr->Uname, row[1], sizeof(cr->Uname));
547          } else {
548             cr->Uname[0] = 0;         /* no name */
549          }
550          cr->AutoPrune = str_to_int64(row[2]);
551          cr->FileRetention = str_to_int64(row[3]);
552          cr->JobRetention = str_to_int64(row[4]);
553          sql_free_result();
554          bdb_unlock();
555          return 1;
556       }
557       sql_free_result();
558    }
559 
560    /* Must create it */
561    Mmsg(cmd, "INSERT INTO Client (Name,Uname,AutoPrune,"
562 "FileRetention,JobRetention) VALUES "
563 "('%s','%s',%d,%s,%s)", esc_name, esc_uname, cr->AutoPrune,
564       edit_uint64(cr->FileRetention, ed1),
565       edit_uint64(cr->JobRetention, ed2));
566 
567    if ((cr->ClientId = sql_insert_autokey_record(cmd, NT_("Client"))) == 0) {
568       Mmsg2(&errmsg, _("Create DB Client record %s failed. ERR=%s\n"),
569             cmd, sql_strerror());
570       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
571       stat = 0;
572    } else {
573       stat = 1;
574    }
575    bdb_unlock();
576    return stat;
577 }
578 
579 
580 /** Create a Unique record for the Path -- no duplicates */
bdb_create_path_record(JCR * jcr,ATTR_DBR * ar)581 int BDB::bdb_create_path_record(JCR *jcr, ATTR_DBR *ar)
582 {
583    SQL_ROW row;
584    int stat;
585 
586    errmsg[0] = 0;
587    esc_name = check_pool_memory_size(esc_name, 2*pnl+2);
588    bdb_escape_string(jcr, esc_name, path, pnl);
589 
590    if (cached_path_id != 0 && cached_path_len == pnl &&
591        strcmp(cached_path, path) == 0) {
592       ar->PathId = cached_path_id;
593       return 1;
594    }
595 
596    Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
597 
598    if (QueryDB(jcr, cmd)) {
599       if (sql_num_rows() > 1) {
600          char ed1[30];
601          Mmsg2(&errmsg, _("More than one Path!: %s for path: %s\n"),
602             edit_uint64(sql_num_rows(), ed1), path);
603          Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
604       }
605       /* Even if there are multiple paths, take the first one */
606       if (sql_num_rows() >= 1) {
607          if ((row = sql_fetch_row()) == NULL) {
608             Mmsg1(&errmsg, _("error fetching row: %s\n"), sql_strerror());
609             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
610             sql_free_result();
611             ar->PathId = 0;
612             ASSERT2(ar->PathId,
613                     "Your Path table is broken. "
614                     "Please, use dbcheck to correct it.");
615             return 0;
616          }
617          ar->PathId = str_to_int64(row[0]);
618          sql_free_result();
619          /* Cache path */
620          if (ar->PathId != cached_path_id) {
621             cached_path_id = ar->PathId;
622             cached_path_len = pnl;
623             pm_strcpy(cached_path, path);
624          }
625          ASSERT(ar->PathId);
626          return 1;
627       }
628       sql_free_result();
629    }
630 
631    Mmsg(cmd, "INSERT INTO Path (Path) VALUES ('%s')", esc_name);
632 
633    if ((ar->PathId = sql_insert_autokey_record(cmd, NT_("Path"))) == 0) {
634       Mmsg2(&errmsg, _("Create db Path record %s failed. ERR=%s\n"),
635          cmd, sql_strerror());
636       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
637       ar->PathId = 0;
638       stat = 0;
639    } else {
640       stat = 1;
641    }
642 
643    /* Cache path */
644    if (stat && ar->PathId != cached_path_id) {
645       cached_path_id = ar->PathId;
646       cached_path_len = pnl;
647       pm_strcpy(cached_path, path);
648    }
649    return stat;
650 }
651 
652 /**
653  * Create a Unique record for the counter -- no duplicates
654  * Returns: 0 on failure
655  *          1 on success with counter filled in
656  */
bdb_create_counter_record(JCR * jcr,COUNTER_DBR * cr)657 int BDB::bdb_create_counter_record(JCR *jcr, COUNTER_DBR *cr)
658 {
659    char esc[MAX_ESCAPE_NAME_LENGTH];
660    COUNTER_DBR mcr;
661    int stat;
662    bdb_lock();
663    memset(&mcr, 0, sizeof(mcr));
664    bstrncpy(mcr.Counter, cr->Counter, sizeof(mcr.Counter));
665    if (bdb_get_counter_record(jcr, &mcr)) {
666       /* If the counter definition changed, we must update the record
667        */
668       if (mcr.MinValue != cr->MinValue ||
669           mcr.MaxValue != cr->MaxValue ||
670           strcmp(mcr.WrapCounter, cr->WrapCounter) != 0)
671       {
672          /* With the update, the current value can be wrong, we need
673           * to adjust it
674           */
675          if (mcr.CurrentValue > 0) {
676             if (cr->MinValue > mcr.CurrentValue) {
677                cr->CurrentValue = cr->MinValue;
678 
679             } else if (cr->MaxValue < mcr.CurrentValue) {
680                cr->CurrentValue = cr->MaxValue;
681 
682             } else {
683                cr->CurrentValue = mcr.CurrentValue;
684             }
685          }
686          Dmsg3(dbglevel, "org: MinValue=%ld MaxValue=%ld CurrentValue=%ld\n",
687                mcr.MinValue, mcr.MaxValue, mcr.CurrentValue);
688          Dmsg3(dbglevel, "new: MinValue=%ld MaxValue=%ld CurrentValue=%ld\n",
689                cr->MinValue, cr->MaxValue, cr->CurrentValue);
690          stat = bdb_update_counter_record(jcr, cr);
691 
692       } else {
693          memcpy(cr, &mcr, sizeof(COUNTER_DBR));
694          stat = 1;
695       }
696       bdb_unlock();
697       return stat;
698    }
699 
700    bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
701 
702    /* Must create it */
703    Mmsg(cmd, insert_counter_values[bdb_get_type_index()],
704         esc, cr->MinValue, cr->MaxValue, cr->CurrentValue,
705         cr->WrapCounter);
706 
707    if (!InsertDB(jcr, cmd)) {
708       Mmsg2(&errmsg, _("Create DB Counters record %s failed. ERR=%s\n"),
709             cmd, sql_strerror());
710       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
711       stat = 0;
712    } else {
713       stat = 1;
714    }
715    bdb_unlock();
716    return stat;
717 }
718 
719 /**
720  * Create a FileSet record. This record is unique in the
721  *  name and the MD5 signature of the include/exclude sets.
722  *  Returns: 0 on failure
723  *           1 on success with FileSetId in record
724  */
bdb_create_fileset_record(JCR * jcr,FILESET_DBR * fsr)725 bool BDB::bdb_create_fileset_record(JCR *jcr, FILESET_DBR *fsr)
726 {
727    SQL_ROW row;
728    bool stat;
729    struct tm tm;
730    char esc_fs[MAX_ESCAPE_NAME_LENGTH];
731    char esc_md5[MAX_ESCAPE_NAME_LENGTH];
732 
733    /* TODO: Escape FileSet and MD5 */
734    bdb_lock();
735    fsr->created = false;
736    bdb_escape_string(jcr, esc_fs, fsr->FileSet, strlen(fsr->FileSet));
737    bdb_escape_string(jcr, esc_md5, fsr->MD5, strlen(fsr->MD5));
738    Mmsg(cmd, "SELECT FileSetId,CreateTime FROM FileSet WHERE "
739                   "FileSet='%s' AND MD5='%s'", esc_fs, esc_md5);
740 
741    fsr->FileSetId = 0;
742    if (QueryDB(jcr, cmd)) {
743       if (sql_num_rows() > 1) {
744          Mmsg1(&errmsg, _("More than one FileSet!: %d\n"), sql_num_rows());
745          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
746       }
747       if (sql_num_rows() >= 1) {
748          if ((row = sql_fetch_row()) == NULL) {
749             Mmsg1(&errmsg, _("error fetching FileSet row: ERR=%s\n"), sql_strerror());
750             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
751             sql_free_result();
752             bdb_unlock();
753             return false;
754          }
755          fsr->FileSetId = str_to_int64(row[0]);
756          if (row[1] == NULL) {
757             fsr->cCreateTime[0] = 0;
758          } else {
759             bstrncpy(fsr->cCreateTime, row[1], sizeof(fsr->cCreateTime));
760          }
761          sql_free_result();
762          bdb_unlock();
763          return true;
764       }
765       sql_free_result();
766    }
767 
768    if (fsr->CreateTime == 0 && fsr->cCreateTime[0] == 0) {
769       fsr->CreateTime = time(NULL);
770    }
771    (void)localtime_r(&fsr->CreateTime, &tm);
772    strftime(fsr->cCreateTime, sizeof(fsr->cCreateTime), "%Y-%m-%d %H:%M:%S", &tm);
773 
774    /* Must create it */
775       Mmsg(cmd, "INSERT INTO FileSet (FileSet,MD5,CreateTime) "
776 "VALUES ('%s','%s','%s')", esc_fs, esc_md5, fsr->cCreateTime);
777 
778    if ((fsr->FileSetId = sql_insert_autokey_record(cmd, NT_("FileSet"))) == 0) {
779       Mmsg2(&errmsg, _("Create DB FileSet record %s failed. ERR=%s\n"),
780             cmd, sql_strerror());
781       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
782       stat = false;
783    } else {
784       fsr->created = true;
785       stat = true;
786    }
787 
788    bdb_unlock();
789    return stat;
790 }
791 
792 
793 /**
794  *  struct stat
795  *  {
796  *      dev_t         st_dev;       * device *
797  *      ino_t         st_ino;       * inode *
798  *      mode_t        st_mode;      * protection *
799  *      nlink_t       st_nlink;     * number of hard links *
800  *      uid_t         st_uid;       * user ID of owner *
801  *      gid_t         st_gid;       * group ID of owner *
802  *      dev_t         st_rdev;      * device type (if inode device) *
803  *      off_t         st_size;      * total size, in bytes *
804  *      unsigned long st_blksize;   * blocksize for filesystem I/O *
805  *      unsigned long st_blocks;    * number of blocks allocated *
806  *      time_t        st_atime;     * time of last access *
807  *      time_t        st_mtime;     * time of last modification *
808  *      time_t        st_ctime;     * time of last inode change *
809  *  };
810  */
811 
812 /* For maintenance, we can put batch mode in hold */
813 static bool batch_mode_enabled = true;
814 
bdb_disable_batch_insert(bool enabled)815 void bdb_disable_batch_insert(bool enabled)
816 {
817    batch_mode_enabled = enabled;
818 }
819 
820 /*
821  * All sql_batch_xx functions are used to do bulk batch
822  *  insert in File/Filename/Path tables.
823  *
824  *  To sum up :
825  *   - bulk load a temp table
826  *   - table before that to avoid possible duplicate inserts with concurrent update)
827  *   - insert missing paths into path with another single query
828  *   - then insert the join between the temp and path tables into file.
829  */
830 
831 /*
832  *  Returns true if OK
833  *          false if failed
834  */
bdb_write_batch_file_records(JCR * jcr)835 bool bdb_write_batch_file_records(JCR *jcr)
836 {
837    bool retval = false;
838    int JobStatus = jcr->JobStatus;
839 
840    if (!jcr->batch_started) {         /* no files to backup ? */
841       Dmsg0(50,"db_write_batch_file_records: no files\n");
842       return true;
843    }
844 
845    if (job_canceled(jcr)) {
846       goto bail_out;
847    }
848 
849    jcr->JobStatus = JS_AttrInserting;
850 
851    /* Check if batch mode is on hold */
852    while (!batch_mode_enabled) {
853       Dmsg0(50, "batch mode is on hold\n");
854       bmicrosleep(10, 0);
855 
856       if (job_canceled(jcr)) {
857          goto bail_out;
858       }
859    }
860 
861    Dmsg1(50,"db_write_batch_file_records changes=%u\n",jcr->db_batch->changes);
862 
863    if (!jcr->db_batch->sql_batch_end(jcr, NULL)) {
864       Jmsg1(jcr, M_FATAL, 0, "Batch end %s\n", jcr->db_batch->errmsg);
865       goto bail_out;
866    }
867    if (job_canceled(jcr)) {
868       goto bail_out;
869    }
870 
871    /* We have to lock tables */
872    if (!jcr->db_batch->bdb_sql_query(batch_lock_path_query[jcr->db_batch->bdb_get_type_index()], NULL, NULL)) {
873       Jmsg1(jcr, M_FATAL, 0, "Lock Path table %s\n", jcr->db_batch->errmsg);
874       goto bail_out;
875    }
876 
877    if (!jcr->db_batch->bdb_sql_query(batch_fill_path_query[jcr->db_batch->bdb_get_type_index()], NULL, NULL)) {
878       Jmsg1(jcr, M_FATAL, 0, "Fill Path table %s\n",jcr->db_batch->errmsg);
879       jcr->db_batch->bdb_sql_query(batch_unlock_tables_query[jcr->db_batch->bdb_get_type_index()], NULL, NULL);
880       goto bail_out;
881    }
882 
883    if (!jcr->db_batch->bdb_sql_query(batch_unlock_tables_query[jcr->db_batch->bdb_get_type_index()], NULL, NULL)) {
884       Jmsg1(jcr, M_FATAL, 0, "Unlock Path table %s\n", jcr->db_batch->errmsg);
885       goto bail_out;
886    }
887 
888    if (!jcr->db_batch->bdb_sql_query(
889 "INSERT INTO File (FileIndex, JobId, PathId, Filename, LStat, MD5, DeltaSeq) "
890     "SELECT batch.FileIndex, batch.JobId, Path.PathId, "
891            "batch.Name, batch.LStat, batch.MD5, batch.DeltaSeq "
892       "FROM batch JOIN Path ON (batch.Path = Path.Path) ", NULL, NULL))
893    {
894       Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg);
895       goto bail_out;
896    }
897 
898    jcr->JobStatus = JobStatus;    /* reset entry status */
899    retval = true;
900 
901 bail_out:
902    jcr->db_batch->bdb_sql_query("DROP TABLE IF EXISTS batch", NULL,NULL);
903    jcr->batch_started = false;
904 
905    return retval;
906 }
907 
908 /**
909  * Create File record in BDB
910  *
911  *  In order to reduce database size, we store the File attributes, the Path
912  *  separately.  In principle, there is a single Path record, no matter how
913  *  many times it occurs.  This is this subroutine, we separate the file and
914  *  the path and fill temporary tables with this three records.
915  *
916  *  Note: all routines that call this expect to be able to call
917  *    db_strerror(mdb) to get the error message, so the error message
918  *    MUST be edited into errmsg before returning an error status.
919  */
bdb_create_batch_file_attributes_record(JCR * jcr,ATTR_DBR * ar)920 bool BDB::bdb_create_batch_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
921 {
922    ASSERT(ar->FileType != FT_BASE);
923    Dmsg2(dbglevel, "FileIndex=%d Fname=%s\n", ar->FileIndex, ar->fname);
924    Dmsg0(dbglevel, "put_file_into_catalog\n");
925 
926    if (jcr->batch_started && jcr->db_batch->changes > 500000) {
927       bdb_write_batch_file_records(jcr);
928       jcr->db_batch->changes = 0;
929    }
930 
931    /* Open the dedicated connexion */
932    if (!jcr->batch_started) {
933       if (!bdb_open_batch_connexion(jcr)) {
934          return false;     /* error already printed */
935       }
936       if (!jcr->db_batch->sql_batch_start(jcr)) {
937          Mmsg1(&errmsg,
938               "Can't start batch mode: ERR=%s", jcr->db_batch->bdb_strerror());
939          Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
940          return false;
941       }
942       jcr->batch_started = true;
943    }
944 
945    split_path_and_file(jcr, jcr->db_batch, ar->fname);
946 
947    return jcr->db_batch->sql_batch_insert(jcr, ar);
948 }
949 
950 /**
951  * Create File record in BDB
952  *
953  *  In order to reduce database size, we store the File attributes and the Path
954  *  separately.  In principle, there is a single Path record, no matter how
955  *  many times it occurs.  This is this subroutine, we separate the file and
956  *  the path and create two database records.
957  */
bdb_create_file_attributes_record(JCR * jcr,ATTR_DBR * ar)958 bool BDB::bdb_create_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
959 {
960    bdb_lock();
961    Dmsg2(dbglevel, "FileIndex=%d Fname=%s\n", ar->FileIndex, ar->fname);
962    Dmsg0(dbglevel, "put_file_into_catalog\n");
963 
964    split_path_and_file(jcr, this, ar->fname);
965 
966    if (!bdb_create_path_record(jcr, ar)) {
967       goto bail_out;
968    }
969    Dmsg1(dbglevel, "db_create_path_record: %s\n", esc_name);
970 
971    esc_name = check_pool_memory_size(esc_name, 2*fnl+2);
972    bdb_escape_string(jcr, esc_name, fname, fnl);
973    ar->Filename = esc_name;
974 
975    /* Now create master File record */
976    if (!bdb_create_file_record(jcr, ar)) {
977       goto bail_out;
978    }
979    Dmsg0(dbglevel, "db_create_file_record OK\n");
980 
981    Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s Filename=%s\n", path, fname, ar->Filename);
982    bdb_unlock();
983    return true;
984 
985 bail_out:
986    bdb_unlock();
987    return false;
988 }
989 /**
990  * This is the master File entry containing the attributes.
991  *  The filename and path records have already been created.
992  */
bdb_create_file_record(JCR * jcr,ATTR_DBR * ar)993 int BDB::bdb_create_file_record(JCR *jcr, ATTR_DBR *ar)
994 {
995    int stat;
996    static const char *no_digest = "0";
997    const char *digest;
998 
999    ASSERT(ar->JobId);
1000    ASSERT(ar->PathId);
1001    ASSERT(ar->Filename != NULL);
1002 
1003    if (ar->Digest == NULL || ar->Digest[0] == 0) {
1004       digest = no_digest;
1005    } else {
1006       digest = ar->Digest;
1007    }
1008 
1009    /* Must create it */
1010    Mmsg(cmd,
1011         "INSERT INTO File (FileIndex,JobId,PathId,Filename,"
1012         "LStat,MD5,DeltaSeq) VALUES (%d,%u,%u,'%s','%s','%s',%u)",
1013         ar->FileIndex, ar->JobId, ar->PathId, ar->Filename,
1014         ar->attr, digest, ar->DeltaSeq);
1015 
1016    if ((ar->FileId = sql_insert_autokey_record(cmd, NT_("File"))) == 0) {
1017       Mmsg2(&errmsg, _("Create db File record %s failed. ERR=%s"),
1018          cmd, sql_strerror());
1019       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
1020       stat = 0;
1021    } else {
1022       stat = 1;
1023    }
1024    return stat;
1025 }
1026 
1027 /**
1028  * Create file attributes record, or base file attributes record
1029  */
bdb_create_attributes_record(JCR * jcr,ATTR_DBR * ar)1030 bool BDB::bdb_create_attributes_record(JCR *jcr, ATTR_DBR *ar)
1031 {
1032    bool ret;
1033 
1034    Dmsg2(dbglevel, "FileIndex=%d Fname=%s\n", ar->FileIndex, ar->fname);
1035    errmsg[0] = 0;
1036    /*
1037     * Make sure we have an acceptable attributes record.
1038     */
1039    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
1040          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
1041       Mmsg1(&errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
1042          ar->Stream);
1043       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
1044       return false;
1045    }
1046 
1047    if (ar->FileType != FT_BASE) {
1048       if (batch_insert_available()) {
1049          ret = bdb_create_batch_file_attributes_record(jcr, ar);
1050          /* Error message already printed */
1051       } else {
1052          ret = bdb_create_file_attributes_record(jcr, ar);
1053       }
1054    } else if (jcr->HasBase) {
1055       ret = bdb_create_base_file_attributes_record(jcr, ar);
1056    } else {
1057       Mmsg0(&errmsg, _("Cannot Copy/Migrate job using BaseJob.\n"));
1058       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
1059       ret = true;               /* in copy/migration what do we do ? */
1060    }
1061 
1062    return ret;
1063 }
1064 
1065 /**
1066  * Create Base File record in BDB
1067  *
1068  */
bdb_create_base_file_attributes_record(JCR * jcr,ATTR_DBR * ar)1069 bool BDB::bdb_create_base_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
1070 {
1071    bool ret;
1072 
1073    Dmsg1(dbglevel, "create_base_file Fname=%s\n", ar->fname);
1074    Dmsg0(dbglevel, "put_base_file_into_catalog\n");
1075 
1076    bdb_lock();
1077    split_path_and_file(jcr, this, ar->fname);
1078 
1079    esc_name = check_pool_memory_size(esc_name, fnl*2+1);
1080    bdb_escape_string(jcr, esc_name, fname, fnl);
1081 
1082    esc_path = check_pool_memory_size(esc_path, pnl*2+1);
1083    bdb_escape_string(jcr, esc_path, path, pnl);
1084 
1085    Mmsg(cmd, "INSERT INTO basefile%lld (Path, Name) VALUES ('%s','%s')",
1086         (uint64_t)jcr->JobId, esc_path, esc_name);
1087 
1088    ret = InsertDB(jcr, cmd);
1089    bdb_unlock();
1090 
1091    return ret;
1092 }
1093 
1094 /**
1095  * Cleanup the base file temporary tables
1096  */
db_cleanup_base_file(JCR * jcr,BDB * mdb)1097 static void db_cleanup_base_file(JCR *jcr, BDB *mdb)
1098 {
1099    POOL_MEM buf(PM_MESSAGE);
1100    Mmsg(buf, "DROP TABLE IF EXISTS new_basefile%lld", (uint64_t) jcr->JobId);
1101    mdb->bdb_sql_query(buf.c_str(), NULL, NULL);
1102 
1103    Mmsg(buf, "DROP TABLE IF EXISTS basefile%lld", (uint64_t) jcr->JobId);
1104    mdb->bdb_sql_query(buf.c_str(), NULL, NULL);
1105 }
1106 
1107 /**
1108  * Put all base file seen in the backup to the BaseFile table
1109  * and cleanup temporary tables
1110  */
bdb_commit_base_file_attributes_record(JCR * jcr)1111 bool BDB::bdb_commit_base_file_attributes_record(JCR *jcr)
1112 {
1113    bool ret;
1114    char ed1[50];
1115 
1116    bdb_lock();
1117 
1118    Mmsg(cmd,
1119   "INSERT INTO BaseFiles (BaseJobId, JobId, FileId, FileIndex) "
1120    "SELECT B.JobId AS BaseJobId, %s AS JobId, "
1121           "B.FileId, B.FileIndex "
1122      "FROM basefile%s AS A, new_basefile%s AS B "
1123     "WHERE A.Path = B.Path "
1124       "AND A.Name = B.Name "
1125     "ORDER BY B.FileId",
1126         edit_uint64(jcr->JobId, ed1), ed1, ed1);
1127    ret = bdb_sql_query(cmd, NULL, NULL);
1128    /*
1129     * Display error now, because the subsequent cleanup destroys the
1130     *  error message from the above query.
1131     */
1132    if (!ret) {
1133       Jmsg1(jcr, M_FATAL, 0, "%s", jcr->db->bdb_strerror());
1134    }
1135    jcr->nb_base_files_used = sql_affected_rows();
1136    db_cleanup_base_file(jcr, this);
1137 
1138    bdb_unlock();
1139    return ret;
1140 }
1141 
1142 /**
1143  * Find the last "accurate" backup state with Base jobs
1144  * 1) Get all files with jobid in list (F subquery)
1145  * 2) Take only the last version of each file (Temp subquery) => accurate list is ok
1146  * 3) Put the result in a temporary table for the end of job
1147  *
1148  */
bdb_create_base_file_list(JCR * jcr,char * jobids)1149 bool BDB::bdb_create_base_file_list(JCR *jcr, char *jobids)
1150 {
1151    POOL_MEM buf;
1152    bool ret = false;
1153 
1154    bdb_lock();
1155 
1156    if (!*jobids) {
1157       Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1158       goto bail_out;
1159    }
1160 
1161    Mmsg(cmd, create_temp_basefile[bdb_get_type_index()], (uint64_t) jcr->JobId);
1162    if (!bdb_sql_query(cmd, NULL, NULL)) {
1163       goto bail_out;
1164    }
1165    Mmsg(buf, select_recent_version[bdb_get_type_index()], jobids, jobids);
1166    Mmsg(cmd, create_temp_new_basefile[bdb_get_type_index()], (uint64_t)jcr->JobId, buf.c_str());
1167 
1168    ret = bdb_sql_query(cmd, NULL, NULL);
1169 bail_out:
1170    bdb_unlock();
1171    return ret;
1172 }
1173 
1174 /**
1175  * Create Restore Object record in BDB
1176  *
1177  */
bdb_create_restore_object_record(JCR * jcr,ROBJECT_DBR * ro)1178 bool BDB::bdb_create_restore_object_record(JCR *jcr, ROBJECT_DBR *ro)
1179 {
1180    bool stat;
1181    int plug_name_len;
1182    POOLMEM *esc_plug_name = get_pool_memory(PM_MESSAGE);
1183 
1184    bdb_lock();
1185 
1186    Dmsg1(dbglevel, "Oname=%s\n", ro->object_name);
1187    Dmsg0(dbglevel, "put_object_into_catalog\n");
1188 
1189    fnl = strlen(ro->object_name);
1190    esc_name = check_pool_memory_size(esc_name, fnl*2+1);
1191    bdb_escape_string(jcr, esc_name, ro->object_name, fnl);
1192 
1193    bdb_escape_object(jcr, ro->object, ro->object_len);
1194 
1195    plug_name_len = strlen(ro->plugin_name);
1196    esc_plug_name = check_pool_memory_size(esc_plug_name, plug_name_len*2+1);
1197    bdb_escape_string(jcr, esc_plug_name, ro->plugin_name, plug_name_len);
1198 
1199    Mmsg(cmd,
1200         "INSERT INTO RestoreObject (ObjectName,PluginName,RestoreObject,"
1201         "ObjectLength,ObjectFullLength,ObjectIndex,ObjectType,"
1202         "ObjectCompression,FileIndex,JobId) "
1203         "VALUES ('%s','%s','%s',%d,%d,%d,%d,%d,%d,%u)",
1204         esc_name, esc_plug_name, esc_obj,
1205         ro->object_len, ro->object_full_len, ro->object_index,
1206         ro->FileType, ro->object_compression, ro->FileIndex, ro->JobId);
1207 
1208    ro->RestoreObjectId = sql_insert_autokey_record(cmd, NT_("RestoreObject"));
1209    if (ro->RestoreObjectId == 0) {
1210       Mmsg2(&errmsg, _("Create db Object record %s failed. ERR=%s"),
1211          cmd, sql_strerror());
1212       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
1213       stat = false;
1214    } else {
1215       stat = true;
1216    }
1217    bdb_unlock();
1218    free_pool_memory(esc_plug_name);
1219    return stat;
1220 }
1221 
bdb_create_snapshot_record(JCR * jcr,SNAPSHOT_DBR * snap)1222 bool BDB::bdb_create_snapshot_record(JCR *jcr, SNAPSHOT_DBR *snap)
1223 {
1224    bool status = false;
1225    char esc_name[MAX_ESCAPE_NAME_LENGTH];
1226    POOLMEM *esc_vol = get_pool_memory(PM_MESSAGE);
1227    POOLMEM *esc_dev = get_pool_memory(PM_MESSAGE);
1228    POOLMEM *esc_type = get_pool_memory(PM_MESSAGE);
1229    POOLMEM *esc_client = get_pool_memory(PM_MESSAGE);
1230    POOLMEM *esc_fs = get_pool_memory(PM_MESSAGE);
1231    char esc_comment[MAX_ESCAPE_NAME_LENGTH];
1232    char dt[MAX_TIME_LENGTH], ed1[50], ed2[50];
1233    time_t stime;
1234    struct tm tm;
1235 
1236    bdb_lock();
1237 
1238    esc_vol = check_pool_memory_size(esc_vol, strlen(snap->Volume) * 2 + 1);
1239    bdb_escape_string(jcr, esc_vol, snap->Volume, strlen(snap->Volume));
1240 
1241    esc_dev = check_pool_memory_size(esc_dev, strlen(snap->Device) * 2 + 1);
1242    bdb_escape_string(jcr, esc_dev, snap->Device, strlen(snap->Device));
1243 
1244    esc_type = check_pool_memory_size(esc_type, strlen(snap->Type) * 2 + 1);
1245    bdb_escape_string(jcr, esc_type, snap->Type, strlen(snap->Type));
1246 
1247    bdb_escape_string(jcr, esc_comment, snap->Comment, strlen(snap->Comment));
1248 
1249    if (*snap->Client) {
1250       bdb_escape_string(jcr, esc_name, snap->Client, strlen(snap->Client));
1251       Mmsg(esc_client, "(SELECT ClientId FROM Client WHERE Name='%s')", esc_name);
1252 
1253    } else {
1254       Mmsg(esc_client, "%d", snap->ClientId);
1255    }
1256 
1257    if (*snap->FileSet) {
1258       bdb_escape_string(jcr, esc_name, snap->FileSet, strlen(snap->FileSet));
1259       Mmsg(esc_fs, "(SELECT FileSetId FROM FileSet WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1)", esc_name);
1260 
1261    } else {
1262       Mmsg(esc_fs, "%d", snap->FileSetId);
1263    }
1264 
1265    bdb_escape_string(jcr, esc_name, snap->Name, strlen(snap->Name));
1266 
1267    stime = snap->CreateTDate;
1268    (void)localtime_r(&stime, &tm);
1269    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
1270 
1271    Mmsg(cmd, "INSERT INTO Snapshot "
1272         "(Name, JobId, CreateTDate, CreateDate, ClientId, FileSetId, Volume, Device, Type, Retention, Comment) "
1273  "VALUES ('%s', %s, %d, '%s', %s, %s, '%s', '%s', '%s', %s, '%s')",
1274         esc_name, edit_uint64(snap->JobId, ed2), stime, dt, esc_client, esc_fs, esc_vol,
1275         esc_dev, esc_type, edit_int64(snap->Retention, ed1), esc_comment);
1276 
1277    if (bdb_sql_query(cmd, NULL, NULL)) {
1278       snap->SnapshotId = sql_insert_autokey_record(cmd, NT_("Snapshot"));
1279       status = true;
1280    }
1281 
1282    bdb_unlock();
1283 
1284    free_pool_memory(esc_vol);
1285    free_pool_memory(esc_dev);
1286    free_pool_memory(esc_type);
1287    free_pool_memory(esc_client);
1288    free_pool_memory(esc_fs);
1289 
1290    return status;
1291 }
1292 
bdb_create_events_record(JCR * jcr,EVENTS_DBR * event)1293 int BDB::bdb_create_events_record(JCR *jcr, EVENTS_DBR *event)
1294 {
1295    bool status = false;
1296    int len;
1297    POOL_MEM tmp, type, from, source, time, text;
1298    char dt[MAX_TIME_LENGTH];
1299 
1300    bdb_lock();
1301    if (!is_name_valid(event->EventsCode, tmp.handle(), "")) {
1302       Mmsg(errmsg, "Invalid EventsCode %s", tmp.c_str());
1303       goto bail_out;
1304    }
1305 
1306    if (!is_name_valid(event->EventsType, tmp.handle(), "")) {
1307       Mmsg(errmsg, "Invalid EventsType %s", tmp.c_str());
1308       goto bail_out;
1309    }
1310    len = strlen(event->EventsType);
1311    type.check_size(len*2+1);
1312    db_escape_string(jcr, this, type.c_str(), event->EventsType, len);
1313 
1314    if (!is_name_valid(event->EventsSource, tmp.handle(), "*-.,:")) { /* Add *None* */
1315       Mmsg(errmsg, "Invalid EventsSource %s", tmp.c_str());
1316       goto bail_out;
1317    }
1318    len = strlen(event->EventsSource);
1319    source.check_size(len*2+1);
1320    db_escape_string(jcr, this, source.c_str(), event->EventsSource, len);
1321 
1322    if (!is_name_valid(event->EventsDaemon, tmp.handle())) {
1323       Mmsg(errmsg, "Invalid EventsDaemon %s", tmp.c_str());
1324       goto bail_out;
1325    }
1326    len = strlen(event->EventsDaemon);
1327    from.check_size(len*2+1);
1328    db_escape_string(jcr, this, from.c_str(), event->EventsDaemon, len);
1329 
1330    len = strlen(event->EventsText);
1331    text.check_size(len*2+1);
1332    db_escape_string(jcr, this, text.c_str(), event->EventsText, len);
1333 
1334    bstrutime(dt, sizeof(dt), event->EventsTime);
1335    Mmsg(cmd, "INSERT INTO Events "
1336         "(EventsDaemon, EventsCode, EventsType, EventsSource, EventsRef, EventsTime, EventsText) "
1337         "VALUES ('%s', '%s', '%s', '%s', '0x%p', '%s', '%s')", from.c_str(), event->EventsCode,
1338         type.c_str(), source.c_str(), event->EventsRef, dt, text.c_str());
1339 
1340    if (bdb_sql_query(cmd, NULL, NULL)) {
1341       status = true;
1342    }
1343 
1344 bail_out:
1345    bdb_unlock();
1346    return status;
1347 }
1348 
1349 
bdb_create_log_record(JCR * jcr,JobId_t jobid,utime_t mtime,char * msg)1350 bool BDB::bdb_create_log_record(JCR *jcr, JobId_t jobid, utime_t mtime, char *msg)
1351 {
1352    bool ret;
1353    POOLMEM *cmd = get_pool_memory(PM_MESSAGE);
1354    POOLMEM *esc_msg = get_pool_memory(PM_MESSAGE);
1355    char dt[MAX_TIME_LENGTH], ed1[50];
1356    int len = strlen(msg) + 1;
1357 
1358    esc_msg = check_pool_memory_size(esc_msg, len*2+1);
1359    bdb_escape_string(jcr, esc_msg, msg, len);
1360    bstrutime(dt, sizeof(dt), mtime);
1361    Mmsg(cmd, "INSERT INTO Log (JobId, Time, LogText) VALUES (%s,'%s','%s')",
1362         edit_int64(jcr->JobId, ed1), dt, esc_msg);
1363 
1364    ret = bdb_sql_query(cmd, NULL, NULL);
1365 
1366    free_pool_memory(cmd);
1367    free_pool_memory(esc_msg);
1368    return ret;
1369 }
1370 
1371 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
1372