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  *
21  *   Bacula Director -- newvol.c -- creates new Volumes in
22  *    catalog Media table from the LabelFormat specification.
23  *
24  *     Kern Sibbald, May MMI
25  *
26  *    This routine runs as a thread and must be thread reentrant.
27  *
28  *  Basic tasks done here:
29  *      If possible create a new Media entry
30  *
31  */
32 
33 #include "bacula.h"
34 #include "dird.h"
35 
36 /* Forward referenced functions */
37 static bool create_simple_name(JCR *jcr, MEDIA_DBR *mr, POOL_DBR *pr);
38 static bool perform_full_name_substitution(JCR *jcr, MEDIA_DBR *mr, POOL_DBR *pr);
39 
40 
41 /*
42  * Automatic Volume name creation using the LabelFormat
43  *
44  *  The media record must have the PoolId filled in when
45  *   calling this routine.
46  */
newVolume(JCR * jcr,MEDIA_DBR * mr,STORE * store,POOL_MEM & errmsg)47 bool newVolume(JCR *jcr, MEDIA_DBR *mr, STORE *store, POOL_MEM &errmsg)
48 {
49    POOL_DBR pr;
50 
51    bmemset(&pr, 0, sizeof(pr));
52 
53    /* See if we can create a new Volume */
54    db_lock(jcr->db);
55    pr.PoolId = mr->PoolId;
56 
57    if (!db_get_pool_numvols(jcr, jcr->db, &pr)) {
58       goto bail_out;
59    }
60 
61    if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
62       Mmsg(errmsg, "Maximum Volumes exceeded for Pool %s", pr.Name);
63       Dmsg1(90, "Too many volumes for Pool %s\n", pr.Name);
64       goto bail_out;
65    }
66 
67    mr->clear();
68    set_pool_dbr_defaults_in_media_dbr(mr, &pr);
69    jcr->VolumeName[0] = 0;
70    bstrncpy(mr->MediaType, jcr->wstore->media_type, sizeof(mr->MediaType));
71    generate_plugin_event(jcr, bDirEventNewVolume); /* return void... */
72    if (jcr->VolumeName[0] && is_volume_name_legal(NULL, jcr->VolumeName)) {
73       bstrncpy(mr->VolumeName, jcr->VolumeName, sizeof(mr->VolumeName));
74       /* Check for special characters */
75    } else if (pr.LabelFormat[0] && pr.LabelFormat[0] != '*') {
76       if (is_volume_name_legal(NULL, pr.LabelFormat)) {
77          /* No special characters, so apply simple algorithm */
78          if (!create_simple_name(jcr, mr, &pr)) {
79             goto bail_out;
80          }
81       } else {  /* try full substitution */
82          /* Found special characters, so try substitution */
83          if (!perform_full_name_substitution(jcr, mr, &pr)) {
84             goto bail_out;
85          }
86          if (!is_volume_name_legal(NULL, mr->VolumeName)) {
87             Mmsg(errmsg, _("Illegal character in Volume name"));
88             Jmsg(jcr, M_ERROR, 0, _("Illegal character in Volume name \"%s\"\n"),
89                  mr->VolumeName);
90             goto bail_out;
91          }
92       }
93    } else {
94       goto bail_out;
95    }
96    pr.NumVols++;
97    mr->Enabled = 1;
98    set_storageid_in_mr(store, mr);
99    if (db_create_media_record(jcr, jcr->db, mr) &&
100        db_update_pool_record(jcr, jcr->db, &pr)) {
101       Jmsg(jcr, M_INFO, 0, _("Created new Volume=\"%s\", Pool=\"%s\", MediaType=\"%s\" in catalog.\n"),
102            mr->VolumeName, pr.Name, mr->MediaType);
103       Dmsg1(90, "Created new Volume=%s\n", mr->VolumeName);
104       db_unlock(jcr->db);
105       return true;
106    } else {
107       Mmsg(errmsg, "%s", db_strerror(jcr->db));
108       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
109    }
110 
111 bail_out:
112    db_unlock(jcr->db);
113    return false;
114 }
115 
create_simple_name(JCR * jcr,MEDIA_DBR * mr,POOL_DBR * pr)116 static bool create_simple_name(JCR *jcr, MEDIA_DBR *mr, POOL_DBR *pr)
117 {
118    char name[MAXSTRING];
119    char num[20];
120    db_int64_ctx ctx;
121    POOL_MEM query(PM_MESSAGE);
122    char ed1[50];
123 
124    /* See if volume already exists */
125    mr->VolumeName[0] = 0;
126    bstrncpy(name, pr->LabelFormat, sizeof(name));
127    ctx.value = 0;
128    /* TODO: Remove Pool as it is not used in the query */
129    Mmsg(query, "SELECT MAX(MediaId) FROM Media,Pool WHERE Pool.PoolId=%s",
130         edit_int64(pr->PoolId, ed1));
131    if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
132       Jmsg(jcr, M_WARNING, 0, _("SQL failed, but ignored. ERR=%s\n"), db_strerror(jcr->db));
133       ctx.value = pr->NumVols+1;
134    }
135    for (int i=(int)ctx.value+1; i<(int)ctx.value+100; i++) {
136       MEDIA_DBR tmr;
137       sprintf(num, "%04d", i);
138       bstrncpy(tmr.VolumeName, name, sizeof(tmr.VolumeName));
139       bstrncat(tmr.VolumeName, num, sizeof(tmr.VolumeName));
140       if (db_get_media_record(jcr, jcr->db, &tmr)) {
141          Jmsg(jcr, M_WARNING, 0,
142              _("Wanted to create Volume \"%s\", but it already exists. Trying again.\n"),
143              tmr.VolumeName);
144          continue;
145       }
146       bstrncpy(mr->VolumeName, name, sizeof(mr->VolumeName));
147       bstrncat(mr->VolumeName, num, sizeof(mr->VolumeName));
148       break;                    /* Got good name */
149    }
150    if (mr->VolumeName[0] == 0) {
151       Jmsg(jcr, M_ERROR, 0, _("Too many failures. Giving up creating Volume name.\n"));
152       return false;
153    }
154    return true;
155 }
156 
157 /*
158  * Perform full substitution on Label
159  */
perform_full_name_substitution(JCR * jcr,MEDIA_DBR * mr,POOL_DBR * pr)160 static bool perform_full_name_substitution(JCR *jcr, MEDIA_DBR *mr, POOL_DBR *pr)
161 {
162    bool ok = false;
163    POOLMEM *label = get_pool_memory(PM_FNAME);
164    jcr->NumVols = pr->NumVols;
165    if (variable_expansion(jcr, pr->LabelFormat, &label)) {
166       bstrncpy(mr->VolumeName, label, sizeof(mr->VolumeName));
167       ok = true;
168    }
169    free_pool_memory(label);
170    return ok;
171 }
172