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