Line data Source code
1 : #include "../../../burp.h"
2 : #include "../../../alloc.h"
3 : #include "../../../fsops.h"
4 : #include "../../../hexmap.h"
5 : #include "../../../lock.h"
6 : #include "../../../log.h"
7 : #include "../../../prepend.h"
8 : #include "../../../protocol2/blk.h"
9 : #include "../../../sbuf.h"
10 : #include "../../../strlist.h"
11 : #include "../../sdirs.h"
12 : #include "../backup_phase4.h"
13 : #include "dindex.h"
14 :
15 24 : static int backup_in_progress(const char *fullpath)
16 : {
17 24 : int ret=-1;
18 : struct stat statp;
19 24 : char *working=NULL;
20 24 : char *finishing=NULL;
21 24 : char *dfiles_regenerating=NULL;
22 :
23 24 : if(!(working=prepend_s(fullpath, "working"))
24 24 : || !(finishing=prepend_s(fullpath, "finishing"))
25 24 : || !(dfiles_regenerating=prepend_s(fullpath, "dfiles.regenerating")))
26 : goto end;
27 :
28 48 : if(!lstat(working, &statp)
29 44 : || !lstat(finishing, &statp))
30 : {
31 4 : logp("%s looks like it has a backup in progress.\n",
32 : fullpath);
33 4 : ret=1;
34 4 : goto end;
35 : }
36 40 : if(!lstat(dfiles_regenerating, &statp))
37 : {
38 0 : logp("%s looks like it was interrupted whilst "
39 : "regenerating its dfiles.\n", fullpath);
40 0 : ret=1;
41 0 : goto end;
42 : }
43 : ret=0;
44 : end:
45 24 : if(ret==1)
46 4 : logp("Give up clean up attempt.\n");
47 24 : free_w(&working);
48 24 : free_w(&finishing);
49 24 : free_w(&dfiles_regenerating);
50 24 : return ret;
51 : }
52 :
53 : // Returns 0 on OK, -1 on error, 1 if there were backups already in progress.
54 24 : static int get_dfiles_to_merge(struct sdirs *sdirs, struct strlist **s)
55 : {
56 24 : int i=0;
57 24 : int n=0;
58 24 : int ret=-1;
59 : struct stat statp;
60 24 : char *fullpath=NULL;
61 24 : char *dfiles=NULL;
62 24 : struct dirent **dir=NULL;
63 :
64 24 : if((n=scandir(sdirs->clients, &dir, filter_dot, NULL))<0)
65 : {
66 0 : logp("scandir failed for %s in %s: %s\n",
67 0 : sdirs->clients, __func__, strerror(errno));
68 : goto end;
69 : }
70 44 : for(i=0; i<n; i++)
71 : {
72 48 : free_w(&fullpath);
73 48 : if(!(fullpath=prepend_s(sdirs->clients, dir[i]->d_name)))
74 : goto end;
75 48 : switch(is_dir(fullpath, dir[i]))
76 : {
77 0 : case 0: continue;
78 : case 1: break;
79 0 : default: logp("is_dir(%s): %s\n",
80 0 : fullpath, strerror(errno));
81 : goto end;
82 : }
83 :
84 48 : if(strcmp(sdirs->client, fullpath))
85 : {
86 24 : switch(backup_in_progress(fullpath))
87 : {
88 : case 0: break;
89 4 : case 1: ret=1;
90 : default: goto end;
91 : }
92 : }
93 :
94 44 : free_w(&dfiles);
95 44 : if(!(dfiles=prepend_s(fullpath, "dfiles"))
96 88 : || lstat(dfiles, &statp))
97 0 : continue;
98 :
99 : // Have a good entry. Add it to the list.
100 44 : if(strlist_add(s, dfiles, 0))
101 : goto end;
102 : }
103 :
104 : ret=0;
105 : end:
106 24 : free_w(&fullpath);
107 24 : free_w(&dfiles);
108 24 : if(dir)
109 : {
110 48 : for(i=0; i<n; i++)
111 48 : free(dir[i]);
112 24 : free(dir);
113 : }
114 24 : return ret;
115 : }
116 :
117 27 : static int do_unlink(struct blk *oblk, const char *datadir)
118 : {
119 27 : int ret=-1;
120 27 : char *fullpath=NULL;
121 27 : char *savepath=uint64_to_savepathstr(oblk->savepath);
122 27 : if(!(fullpath=prepend_s(datadir, savepath)))
123 : goto end;
124 27 : errno=0;
125 27 : if(unlink(fullpath) && errno!=ENOENT)
126 : {
127 0 : logp("Could not unlink %s: %s\n", fullpath, strerror(errno));
128 : goto end;
129 : }
130 27 : logp("Deleted %s\n", savepath);
131 27 : ret=0;
132 : end:
133 27 : free_w(&fullpath);
134 27 : return ret;
135 : }
136 :
137 : #ifndef UTEST
138 : static
139 : #endif
140 26 : int compare_dindexes_and_unlink_datafiles(const char *dindex_old,
141 : const char *dindex_new, const char *datadir)
142 : {
143 26 : int ret=-1;
144 26 : struct fzp *nzp=NULL;
145 26 : struct fzp *ozp=NULL;
146 : struct iobuf nbuf;
147 : struct iobuf obuf;
148 : struct blk nblk;
149 : struct blk oblk;
150 :
151 26 : iobuf_init(&nbuf);
152 26 : iobuf_init(&obuf);
153 : memset(&nblk, 0, sizeof(struct blk));
154 : memset(&oblk, 0, sizeof(struct blk));
155 :
156 26 : if(!(nzp=fzp_gzopen(dindex_new, "rb"))
157 26 : || !(ozp=fzp_gzopen(dindex_old, "rb")))
158 : goto end;
159 :
160 109 : while(nzp || ozp)
161 : {
162 88 : if(nzp
163 69 : && !nbuf.buf)
164 : {
165 61 : switch(iobuf_fill_from_fzp(&nbuf, nzp))
166 : {
167 21 : case 1: fzp_close(&nzp);
168 21 : break;
169 40 : case 0: if(nbuf.cmd!=CMD_SAVE_PATH)
170 : {
171 0 : logp("unknown cmd in %s: %c\n",
172 : __func__, nbuf.cmd);
173 0 : goto end;
174 : }
175 40 : if(blk_set_from_iobuf_savepath(&nblk,
176 : &nbuf)) goto end;
177 : break;
178 : default: goto end; // Error;
179 : }
180 : }
181 :
182 88 : if(ozp
183 88 : && !obuf.buf)
184 : {
185 87 : switch(iobuf_fill_from_fzp(&obuf, ozp))
186 : {
187 26 : case 1: fzp_close(&ozp);
188 26 : break;
189 61 : case 0: if(obuf.cmd!=CMD_SAVE_PATH)
190 : {
191 0 : logp("unknown cmd in %s: %c\n",
192 : __func__, obuf.cmd);
193 0 : goto end;
194 : }
195 61 : if(blk_set_from_iobuf_savepath(&oblk,
196 : &obuf)) goto end;
197 : break;
198 : default: goto end; // Error;
199 : }
200 : }
201 :
202 88 : if(nbuf.buf && !obuf.buf)
203 : {
204 : // No more from the old file. Time to stop.
205 : break;
206 : }
207 83 : else if(!nbuf.buf && obuf.buf)
208 : {
209 : // No more in the new file. Delete old entry.
210 19 : if(do_unlink(&oblk, datadir))
211 : goto end;
212 19 : iobuf_free_content(&obuf);
213 : }
214 64 : else if(!nbuf.buf && !obuf.buf)
215 : {
216 21 : continue;
217 : }
218 43 : else if(nblk.savepath==oblk.savepath)
219 : {
220 : // Same, free both and continue;
221 34 : iobuf_free_content(&nbuf);
222 34 : iobuf_free_content(&obuf);
223 : }
224 9 : else if(nblk.savepath<oblk.savepath)
225 : {
226 : // Only in the new file.
227 1 : iobuf_free_content(&nbuf);
228 : }
229 : else
230 : {
231 : // Only in the old file.
232 8 : if(do_unlink(&oblk, datadir))
233 : goto end;
234 8 : iobuf_free_content(&obuf);
235 : }
236 : }
237 :
238 :
239 : ret=0;
240 : end:
241 26 : iobuf_free_content(&nbuf);
242 26 : iobuf_free_content(&obuf);
243 26 : fzp_close(&nzp);
244 26 : fzp_close(&ozp);
245 26 : return ret;
246 : }
247 :
248 32 : int delete_unused_data_files(struct sdirs *sdirs, int resume)
249 : {
250 32 : int ret=-1;
251 32 : uint64_t fcount=0;
252 : char hfile[32];
253 32 : char *hlinks=NULL;
254 32 : char *fullpath=NULL;
255 32 : char *cindex_tmp=NULL;
256 32 : char *cindex_new=NULL;
257 32 : char *dindex_tmp=NULL;
258 32 : char *dindex_new=NULL;
259 32 : char *dindex_old=NULL;
260 32 : struct strlist *s=NULL;
261 32 : struct strlist *slist=NULL;
262 : struct stat statp;
263 32 : struct lock *lock=NULL;
264 :
265 32 : if(!sdirs)
266 : {
267 1 : logp("No sdirs passed to %s\n", __func__);
268 1 : goto end;
269 : }
270 :
271 31 : if(resume)
272 : {
273 : // Cannot do it on a resume, or it will delete files that are
274 : // referenced in the backup we are resuming.
275 7 : logp("Not attempting to clean up unused data files\n");
276 7 : logp("because %s is resuming\n", sdirs->clients);
277 7 : ret=0;
278 7 : goto end;
279 : }
280 :
281 24 : if(!(lock=lock_alloc_and_init(sdirs->champ_dindex_lock)))
282 : goto end;
283 24 : lock_get(lock);
284 24 : switch(lock->status)
285 : {
286 : case GET_LOCK_GOT:
287 : break;
288 : default:
289 0 : logp("Could not get %s\n", sdirs->champ_dindex_lock);
290 0 : logp("This should not happen.\n");
291 0 : goto end;
292 : }
293 :
294 24 : logp("Attempting to clean up unused data files %s\n", sdirs->clients);
295 :
296 : // Get all lists of files in all backups.
297 24 : switch(get_dfiles_to_merge(sdirs, &slist))
298 : {
299 : case 0:
300 : break; // OK.
301 : case 1:
302 : // Backups are in progress, cannot continue.
303 : // But do not return an error.
304 4 : ret=0;
305 : default:
306 : goto end; // Error.
307 : }
308 :
309 20 : if(!(dindex_tmp=prepend_s(sdirs->data, "dindex.tmp"))
310 20 : || !(dindex_old=prepend_s(sdirs->data, "dindex")))
311 : goto end;
312 :
313 : // Get a list of the files that have been created since last time.
314 : // (this enables us to clean up data files that were created for
315 : // interrupted backups).
316 20 : if(!(cindex_tmp=prepend_s(sdirs->cfiles, "cindex.tmp"))
317 20 : || recursive_delete(cindex_tmp))
318 : goto end;
319 40 : if(!lstat(sdirs->cfiles, &statp))
320 : {
321 20 : if(mkdir(cindex_tmp, 0777)
322 20 : || !(cindex_new=prepend_s(cindex_tmp, "cindex"))
323 20 : || merge_files_in_dir_no_fcount(cindex_new,
324 20 : sdirs->cfiles, merge_dindexes))
325 : goto end;
326 40 : if(!lstat(cindex_new, &statp))
327 : {
328 40 : if(lstat(dindex_old, &statp))
329 : {
330 : // The dindex file does not exist.
331 : // Just rename cindex_new.
332 16 : if(do_rename(cindex_new, dindex_old))
333 : goto end;
334 : }
335 : else
336 : {
337 : // Merge it into the previous list of files
338 : // from all backups.
339 4 : if(merge_dindexes(dindex_tmp,
340 : dindex_old, cindex_new)
341 4 : || do_rename(dindex_tmp, dindex_old))
342 : goto end;
343 : }
344 : }
345 : }
346 :
347 : // Create a directory of hardlinks to each list of files.
348 20 : if(!(hlinks=prepend_s(dindex_tmp, "hlinks"))
349 20 : || recursive_delete(dindex_tmp)
350 20 : || mkdir(dindex_tmp, 0777)
351 20 : || mkdir(hlinks, 0777))
352 : goto end;
353 60 : for(s=slist; s; s=s->next)
354 : {
355 40 : snprintf(hfile, sizeof(hfile), "%08" PRIX64, fcount++);
356 40 : free_w(&fullpath);
357 40 : if(!(fullpath=prepend_s(hlinks, hfile)))
358 : goto end;
359 40 : if(link(s->path, fullpath))
360 : {
361 0 : logp("Could not hardlink %s to %s: %s\n",
362 0 : fullpath, s->path, strerror(errno));
363 0 : goto end;
364 : }
365 : }
366 :
367 : // Create a single list of files in all backups.
368 20 : if(!(dindex_new=prepend_s(dindex_tmp, "dindex")))
369 : goto end;
370 20 : if(merge_files_in_dir(dindex_new,
371 : dindex_tmp, "hlinks", fcount, merge_dindexes))
372 : goto end;
373 :
374 40 : if(!lstat(dindex_new, &statp))
375 : {
376 40 : if(!lstat(dindex_old, &statp)
377 20 : && compare_dindexes_and_unlink_datafiles(dindex_old,
378 20 : dindex_new, sdirs->data))
379 : goto end;
380 20 : if(do_rename(dindex_new, dindex_old))
381 : goto end;
382 :
383 : // No longer need the current cfiles directory.
384 20 : if(recursive_delete(sdirs->cfiles))
385 : goto end;
386 : }
387 :
388 : ret=0;
389 : end:
390 32 : strlists_free(&slist);
391 32 : if(cindex_tmp) recursive_delete(cindex_tmp);
392 32 : if(dindex_tmp) recursive_delete(dindex_tmp);
393 32 : lock_release(lock);
394 32 : lock_free(&lock);
395 32 : free_w(&fullpath);
396 32 : free_w(&hlinks);
397 32 : free_w(&cindex_tmp);
398 32 : free_w(&cindex_new);
399 32 : free_w(&dindex_tmp);
400 32 : free_w(&dindex_new);
401 32 : free_w(&dindex_old);
402 32 : return ret;
403 : }
|