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 Delete record interface routines
21 *
22 * Written by Kern Sibbald, December 2000-2014
23 */
24
25 #include "bacula.h"
26
27 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
28 #include "cats.h"
29
30 /* -----------------------------------------------------------------------
31 *
32 * Generic Routines (or almost generic)
33 *
34 * -----------------------------------------------------------------------
35 */
36
37 /*
38 * Delete Pool record, must also delete all associated
39 * Media records.
40 *
41 * Returns: 0 on error
42 * 1 on success
43 * PoolId = number of Pools deleted (should be 1)
44 * NumVols = number of Media records deleted
45 */
bdb_delete_pool_record(JCR * jcr,POOL_DBR * pr)46 int BDB::bdb_delete_pool_record(JCR *jcr, POOL_DBR *pr)
47 {
48 SQL_ROW row;
49 char esc[MAX_ESCAPE_NAME_LENGTH];
50
51 bdb_lock();
52 bdb_escape_string(jcr, esc, pr->Name, strlen(pr->Name));
53 Mmsg(cmd, "SELECT PoolId FROM Pool WHERE Name='%s'", esc);
54 Dmsg1(10, "selectpool: %s\n", cmd);
55
56 pr->PoolId = pr->NumVols = 0;
57
58 if (QueryDB(jcr, cmd)) {
59 int nrows = sql_num_rows();
60 if (nrows == 0) {
61 Mmsg(errmsg, _("No pool record %s exists\n"), pr->Name);
62 sql_free_result();
63 bdb_unlock();
64 return 0;
65 } else if (nrows != 1) {
66 Mmsg(errmsg, _("Expecting one pool record, got %d\n"), nrows);
67 sql_free_result();
68 bdb_unlock();
69 return 0;
70 }
71 if ((row = sql_fetch_row()) == NULL) {
72 Mmsg1(&errmsg, _("Error fetching row %s\n"), sql_strerror());
73 bdb_unlock();
74 return 0;
75 }
76 pr->PoolId = str_to_int64(row[0]);
77 sql_free_result();
78 }
79
80 /* Delete Media owned by this pool */
81 Mmsg(cmd,
82 "DELETE FROM Media WHERE Media.PoolId = %d", pr->PoolId);
83
84 pr->NumVols = DeleteDB(jcr, cmd);
85 Dmsg1(200, "Deleted %d Media records\n", pr->NumVols);
86
87 /* Delete Pool */
88 Mmsg(cmd,
89 "DELETE FROM Pool WHERE Pool.PoolId = %d", pr->PoolId);
90 pr->PoolId = DeleteDB(jcr, cmd);
91 Dmsg1(200, "Deleted %d Pool records\n", pr->PoolId);
92
93 bdb_unlock();
94 return 1;
95 }
96
97 #define MAX_DEL_LIST_LEN 1000000
98
99 struct s_del_ctx {
100 JobId_t *JobId;
101 int num_ids; /* ids stored */
102 int max_ids; /* size of array */
103 int num_del; /* number deleted */
104 int tot_ids; /* total to process */
105 };
106
107 /*
108 * Called here to make in memory list of JobIds to be
109 * deleted. The in memory list will then be transversed
110 * to issue the SQL DELETE commands. Note, the list
111 * is allowed to get to MAX_DEL_LIST_LEN to limit the
112 * maximum malloc'ed memory.
113 */
delete_handler(void * ctx,int num_fields,char ** row)114 static int delete_handler(void *ctx, int num_fields, char **row)
115 {
116 struct s_del_ctx *del = (struct s_del_ctx *)ctx;
117
118 if (del->num_ids == MAX_DEL_LIST_LEN) {
119 return 1;
120 }
121 if (del->num_ids == del->max_ids) {
122 del->max_ids = (del->max_ids * 3) / 2;
123 del->JobId = (JobId_t *)brealloc(del->JobId, sizeof(JobId_t) *
124 del->max_ids);
125 }
126 del->JobId[del->num_ids++] = (JobId_t)str_to_int64(row[0]);
127 return 0;
128 }
129
130
131 /*
132 * This routine will purge (delete) all records
133 * associated with a particular Volume. It will
134 * not delete the media record itself.
135 * TODO: This function is broken and it doesn't purge
136 * File, BaseFiles, Log, ...
137 * We call it from relabel and delete volume=, both ensure
138 * that the volume is properly purged.
139 */
do_media_purge(BDB * mdb,MEDIA_DBR * mr)140 static int do_media_purge(BDB *mdb, MEDIA_DBR *mr)
141 {
142 POOLMEM *query = get_pool_memory(PM_MESSAGE);
143 struct s_del_ctx del;
144 char ed1[50];
145 int i;
146
147 del.num_ids = 0;
148 del.tot_ids = 0;
149 del.num_del = 0;
150 del.max_ids = 0;
151 Mmsg(mdb->cmd, "SELECT JobId from JobMedia WHERE MediaId=%lu", mr->MediaId);
152 del.max_ids = mr->VolJobs;
153 if (del.max_ids < 100) {
154 del.max_ids = 100;
155 } else if (del.max_ids > MAX_DEL_LIST_LEN) {
156 del.max_ids = MAX_DEL_LIST_LEN;
157 }
158 del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);
159 mdb->bdb_sql_query(mdb->cmd, delete_handler, (void *)&del);
160
161 for (i=0; i < del.num_ids; i++) {
162 Dmsg1(400, "Delete JobId=%d\n", del.JobId[i]);
163 Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(del.JobId[i], ed1));
164 mdb->bdb_sql_query(query, NULL, (void *)NULL);
165 Mmsg(query, "DELETE FROM File WHERE JobId=%s", edit_int64(del.JobId[i], ed1));
166 mdb->bdb_sql_query(query, NULL, (void *)NULL);
167 Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", edit_int64(del.JobId[i], ed1));
168 mdb->bdb_sql_query(query, NULL, (void *)NULL);
169 }
170 free(del.JobId);
171 free_pool_memory(query);
172 return 1;
173 }
174
175 /* Delete Media record and all records that
176 * are associated with it.
177 */
bdb_delete_media_record(JCR * jcr,MEDIA_DBR * mr)178 int BDB::bdb_delete_media_record(JCR *jcr, MEDIA_DBR *mr)
179 {
180 bdb_lock();
181 if (mr->MediaId == 0 && !bdb_get_media_record(jcr, mr)) {
182 bdb_unlock();
183 return 0;
184 }
185 /* Do purge if not already purged */
186 if (strcmp(mr->VolStatus, "Purged") != 0) {
187 /* Delete associated records */
188 do_media_purge(this, mr);
189 }
190
191 Mmsg(cmd, "DELETE FROM Media WHERE MediaId=%lu", mr->MediaId);
192 bdb_sql_query(cmd, NULL, (void *)NULL);
193 bdb_unlock();
194 return 1;
195 }
196
197 /*
198 * Purge all records associated with a
199 * media record. This does not delete the
200 * media record itself. But the media status
201 * is changed to "Purged".
202 */
bdb_purge_media_record(JCR * jcr,MEDIA_DBR * mr)203 int BDB::bdb_purge_media_record(JCR *jcr, MEDIA_DBR *mr)
204 {
205 bdb_lock();
206 if (mr->MediaId == 0 && !bdb_get_media_record(jcr, mr)) {
207 bdb_unlock();
208 return 0;
209 }
210 /* Delete associated records */
211 do_media_purge(this, mr); /* Note, always purge */
212
213 /* Mark Volume as purged */
214 strcpy(mr->VolStatus, "Purged");
215 if (!bdb_update_media_record(jcr, mr)) {
216 bdb_unlock();
217 return 0;
218 }
219
220 bdb_unlock();
221 return 1;
222 }
223
224 /* Delete Snapshot record */
bdb_delete_snapshot_record(JCR * jcr,SNAPSHOT_DBR * sr)225 int BDB::bdb_delete_snapshot_record(JCR *jcr, SNAPSHOT_DBR *sr)
226 {
227 bdb_lock();
228 if (sr->SnapshotId == 0 && !bdb_get_snapshot_record(jcr, sr)) {
229 bdb_unlock();
230 return 0;
231 }
232
233 Mmsg(cmd, "DELETE FROM Snapshot WHERE SnapshotId=%d", sr->SnapshotId);
234 bdb_sql_query(cmd, NULL, (void *)NULL);
235 bdb_unlock();
236 return 1;
237 }
238
239 /* Delete Client record */
bdb_delete_client_record(JCR * jcr,CLIENT_DBR * cr)240 int BDB::bdb_delete_client_record(JCR *jcr, CLIENT_DBR *cr)
241 {
242 bdb_lock();
243 if (cr->ClientId == 0 && !bdb_get_client_record(jcr, cr)) {
244 bdb_unlock();
245 return 0;
246 }
247
248 Mmsg(cmd, "DELETE FROM Client WHERE ClientId=%d", cr->ClientId);
249 bdb_sql_query(cmd, NULL, (void *)NULL);
250 bdb_unlock();
251 return 1;
252 }
253
254 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
255