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