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