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    pr.PoolBytes = 1;            /* Get the size of the pool */
57 
58    if (!db_get_pool_numvols(jcr, jcr->db, &pr)) {
59       goto bail_out;
60    }
61 
62    if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
63       Mmsg(errmsg, "Maximum Volumes exceeded for Pool %s", pr.Name);
64       Dmsg1(90, "Too many volumes for Pool %s\n", pr.Name);
65       goto bail_out;
66    }
67 
68    if (check_max_pool_bytes(&pr)) {
69       Mmsg(errmsg, "Maximum Pool Bytes exceeded for Pool %s", pr.Name);
70       Dmsg1(90, "Too much bytes for Pool %s\n", pr.Name);
71       goto bail_out;
72    }
73 
74    mr->clear();
75    set_pool_dbr_defaults_in_media_dbr(mr, &pr);
76    jcr->VolumeName[0] = 0;
77    bstrncpy(mr->MediaType, jcr->wstore->media_type, sizeof(mr->MediaType));
78    generate_plugin_event(jcr, bDirEventNewVolume); /* return void... */
79    if (jcr->VolumeName[0] && is_volume_name_legal(NULL, jcr->VolumeName)) {
80       bstrncpy(mr->VolumeName, jcr->VolumeName, sizeof(mr->VolumeName));
81       /* Check for special characters */
82    } else if (pr.LabelFormat[0] && pr.LabelFormat[0] != '*') {
83       if (is_volume_name_legal(NULL, pr.LabelFormat)) {
84          /* No special characters, so apply simple algorithm */
85          if (!create_simple_name(jcr, mr, &pr)) {
86             goto bail_out;
87          }
88       } else {  /* try full substitution */
89          /* Found special characters, so try substitution */
90          if (!perform_full_name_substitution(jcr, mr, &pr)) {
91             goto bail_out;
92          }
93          if (!is_volume_name_legal(NULL, mr->VolumeName)) {
94             Mmsg(errmsg, _("Illegal character in Volume name"));
95             Jmsg(jcr, M_ERROR, 0, _("Illegal character in Volume name \"%s\"\n"),
96                  mr->VolumeName);
97             goto bail_out;
98          }
99       }
100    } else {
101       goto bail_out;
102    }
103    pr.NumVols++;
104    mr->Enabled = 1;
105    set_storageid_in_mr(store, mr);
106    if (db_create_media_record(jcr, jcr->db, mr) &&
107        db_update_pool_record(jcr, jcr->db, &pr)) {
108       Jmsg(jcr, M_INFO, 0, _("Created new Volume=\"%s\", Pool=\"%s\", MediaType=\"%s\" in catalog.\n"),
109            mr->VolumeName, pr.Name, mr->MediaType);
110       Dmsg1(90, "Created new Volume=%s\n", mr->VolumeName);
111       db_unlock(jcr->db);
112       return true;
113    } else {
114       Mmsg(errmsg, "%s", db_strerror(jcr->db));
115       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
116    }
117 
118 bail_out:
119    db_unlock(jcr->db);
120    return false;
121 }
122 
create_simple_name(JCR * jcr,MEDIA_DBR * mr,POOL_DBR * pr)123 static bool create_simple_name(JCR *jcr, MEDIA_DBR *mr, POOL_DBR *pr)
124 {
125    char name[MAXSTRING];
126    char num[20];
127    db_int64_ctx ctx;
128    POOL_MEM query(PM_MESSAGE);
129    char ed1[50];
130 
131    /* See if volume already exists */
132    mr->VolumeName[0] = 0;
133    bstrncpy(name, pr->LabelFormat, sizeof(name));
134    ctx.value = 0;
135    /* TODO: Remove Pool as it is not used in the query */
136    Mmsg(query, "SELECT MAX(MediaId) FROM Media,Pool WHERE Pool.PoolId=%s",
137         edit_int64(pr->PoolId, ed1));
138    if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
139       Jmsg(jcr, M_WARNING, 0, _("SQL failed, but ignored. ERR=%s\n"), db_strerror(jcr->db));
140       ctx.value = pr->NumVols+1;
141    }
142    for (int i=(int)ctx.value+1; i<(int)ctx.value+100; i++) {
143       MEDIA_DBR tmr;
144       sprintf(num, "%04d", i);
145       bstrncpy(tmr.VolumeName, name, sizeof(tmr.VolumeName));
146       bstrncat(tmr.VolumeName, num, sizeof(tmr.VolumeName));
147       if (db_get_media_record(jcr, jcr->db, &tmr)) {
148          Jmsg(jcr, M_WARNING, 0,
149              _("Wanted to create Volume \"%s\", but it already exists. Trying again.\n"),
150              tmr.VolumeName);
151          continue;
152       }
153       bstrncpy(mr->VolumeName, name, sizeof(mr->VolumeName));
154       bstrncat(mr->VolumeName, num, sizeof(mr->VolumeName));
155       break;                    /* Got good name */
156    }
157    if (mr->VolumeName[0] == 0) {
158       Jmsg(jcr, M_ERROR, 0, _("Too many failures. Giving up creating Volume name.\n"));
159       return false;
160    }
161    return true;
162 }
163 
164 /*
165  * Perform full substitution on Label
166  */
perform_full_name_substitution(JCR * jcr,MEDIA_DBR * mr,POOL_DBR * pr)167 static bool perform_full_name_substitution(JCR *jcr, MEDIA_DBR *mr, POOL_DBR *pr)
168 {
169    bool ok = false;
170    POOLMEM *label = get_pool_memory(PM_FNAME);
171    jcr->NumVols = pr->NumVols;
172    if (variable_expansion(jcr, pr->LabelFormat, &label)) {
173       bstrncpy(mr->VolumeName, label, sizeof(mr->VolumeName));
174       ok = true;
175    }
176    free_pool_memory(label);
177    return ok;
178 }
179