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