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