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: %s\n",
172 : __func__,
173 : iobuf_to_printable(&nbuf));
174 0 : goto end;
175 : }
176 40 : if(blk_set_from_iobuf_savepath(&nblk,
177 : &nbuf)) goto end;
178 : break;
179 : default: goto end; // Error;
180 : }
181 : }
182 :
183 88 : if(ozp
184 88 : && !obuf.buf)
185 : {
186 87 : switch(iobuf_fill_from_fzp(&obuf, ozp))
187 : {
188 26 : case 1: fzp_close(&ozp);
189 26 : break;
190 61 : case 0: if(obuf.cmd!=CMD_SAVE_PATH)
191 : {
192 0 : logp("unknown cmd in %s: %c\n",
193 : __func__, obuf.cmd);
194 0 : goto end;
195 : }
196 61 : if(blk_set_from_iobuf_savepath(&oblk,
197 : &obuf)) goto end;
198 : break;
199 : default: goto end; // Error;
200 : }
201 : }
202 :
203 88 : if(nbuf.buf && !obuf.buf)
204 : {
205 : // No more from the old file. Time to stop.
206 : break;
207 : }
208 83 : else if(!nbuf.buf && obuf.buf)
209 : {
210 : // No more in the new file. Delete old entry.
211 19 : if(do_unlink(&oblk, datadir))
212 : goto end;
213 19 : iobuf_free_content(&obuf);
214 : }
215 64 : else if(!nbuf.buf && !obuf.buf)
216 : {
217 21 : continue;
218 : }
219 43 : else if(nblk.savepath==oblk.savepath)
220 : {
221 : // Same, free both and continue;
222 34 : iobuf_free_content(&nbuf);
223 34 : iobuf_free_content(&obuf);
224 : }
225 9 : else if(nblk.savepath<oblk.savepath)
226 : {
227 : // Only in the new file.
228 1 : iobuf_free_content(&nbuf);
229 : }
230 : else
231 : {
232 : // Only in the old file.
233 8 : if(do_unlink(&oblk, datadir))
234 : goto end;
235 8 : iobuf_free_content(&obuf);
236 : }
237 : }
238 :
239 :
240 : ret=0;
241 : end:
242 26 : iobuf_free_content(&nbuf);
243 26 : iobuf_free_content(&obuf);
244 26 : fzp_close(&nzp);
245 26 : fzp_close(&ozp);
246 26 : return ret;
247 : }
248 :
249 32 : int delete_unused_data_files(struct sdirs *sdirs, int resume)
250 : {
251 32 : int ret=-1;
252 32 : uint64_t fcount=0;
253 : char hfile[32];
254 32 : char *hlinks=NULL;
255 32 : char *fullpath=NULL;
256 32 : char *cindex_tmp=NULL;
257 32 : char *cindex_new=NULL;
258 32 : char *dindex_tmp=NULL;
259 32 : char *dindex_new=NULL;
260 32 : char *dindex_old=NULL;
261 32 : struct strlist *s=NULL;
262 32 : struct strlist *slist=NULL;
263 : struct stat statp;
264 32 : struct lock *lock=NULL;
265 :
266 32 : if(!sdirs)
267 : {
268 1 : logp("No sdirs passed to %s\n", __func__);
269 1 : goto end;
270 : }
271 :
272 31 : if(resume)
273 : {
274 : // Cannot do it on a resume, or it will delete files that are
275 : // referenced in the backup we are resuming.
276 7 : logp("Not attempting to clean up unused data files\n");
277 7 : logp("because %s is resuming\n", sdirs->clients);
278 7 : ret=0;
279 7 : goto end;
280 : }
281 :
282 24 : if(!(lock=lock_alloc_and_init(sdirs->champ_dindex_lock)))
283 : goto end;
284 24 : lock_get(lock);
285 24 : switch(lock->status)
286 : {
287 : case GET_LOCK_GOT:
288 : break;
289 : default:
290 0 : logp("Could not get %s\n", sdirs->champ_dindex_lock);
291 0 : logp("This should not happen.\n");
292 0 : goto end;
293 : }
294 :
295 24 : logp("Attempting to clean up unused data files %s\n", sdirs->clients);
296 :
297 : // Get all lists of files in all backups.
298 24 : switch(get_dfiles_to_merge(sdirs, &slist))
299 : {
300 : case 0:
301 : break; // OK.
302 : case 1:
303 : // Backups are in progress, cannot continue.
304 : // But do not return an error.
305 4 : ret=0;
306 : default:
307 : goto end; // Error.
308 : }
309 :
310 20 : if(!(dindex_tmp=prepend_s(sdirs->data, "dindex.tmp"))
311 20 : || !(dindex_old=prepend_s(sdirs->data, "dindex")))
312 : goto end;
313 :
314 : // Get a list of the files that have been created since last time.
315 : // (this enables us to clean up data files that were created for
316 : // interrupted backups).
317 20 : if(!(cindex_tmp=prepend_s(sdirs->cfiles, "cindex.tmp"))
318 20 : || recursive_delete(cindex_tmp))
319 : goto end;
320 40 : if(!lstat(sdirs->cfiles, &statp))
321 : {
322 20 : if(mkdir(cindex_tmp, 0777)
323 20 : || !(cindex_new=prepend_s(cindex_tmp, "cindex"))
324 20 : || merge_files_in_dir_no_fcount(cindex_new,
325 20 : sdirs->cfiles, merge_dindexes))
326 : goto end;
327 40 : if(!lstat(cindex_new, &statp))
328 : {
329 40 : if(lstat(dindex_old, &statp))
330 : {
331 : // The dindex file does not exist.
332 : // Just rename cindex_new.
333 16 : if(do_rename(cindex_new, dindex_old))
334 : goto end;
335 : }
336 : else
337 : {
338 : // Merge it into the previous list of files
339 : // from all backups.
340 4 : if(merge_dindexes(dindex_tmp,
341 : dindex_old, cindex_new)
342 4 : || do_rename(dindex_tmp, dindex_old))
343 : goto end;
344 : }
345 : }
346 : }
347 :
348 : // Create a directory of hardlinks to each list of files.
349 20 : if(!(hlinks=prepend_s(dindex_tmp, "hlinks"))
350 20 : || recursive_delete(dindex_tmp)
351 20 : || mkdir(dindex_tmp, 0777)
352 20 : || mkdir(hlinks, 0777))
353 : goto end;
354 60 : for(s=slist; s; s=s->next)
355 : {
356 40 : snprintf(hfile, sizeof(hfile), "%08" PRIX64, fcount++);
357 40 : free_w(&fullpath);
358 40 : if(!(fullpath=prepend_s(hlinks, hfile)))
359 : goto end;
360 40 : if(link(s->path, fullpath))
361 : {
362 0 : logp("Could not hardlink %s to %s: %s\n",
363 0 : fullpath, s->path, strerror(errno));
364 0 : goto end;
365 : }
366 : }
367 :
368 : // Create a single list of files in all backups.
369 20 : if(!(dindex_new=prepend_s(dindex_tmp, "dindex")))
370 : goto end;
371 20 : if(merge_files_in_dir(dindex_new,
372 : dindex_tmp, "hlinks", fcount, merge_dindexes))
373 : goto end;
374 :
375 40 : if(!lstat(dindex_new, &statp))
376 : {
377 40 : if(!lstat(dindex_old, &statp)
378 20 : && compare_dindexes_and_unlink_datafiles(dindex_old,
379 20 : dindex_new, sdirs->data))
380 : goto end;
381 20 : if(do_rename(dindex_new, dindex_old))
382 : goto end;
383 :
384 : // No longer need the current cfiles directory.
385 20 : if(recursive_delete(sdirs->cfiles))
386 : goto end;
387 : }
388 :
389 : ret=0;
390 : end:
391 32 : strlists_free(&slist);
392 32 : if(cindex_tmp) recursive_delete(cindex_tmp);
393 32 : if(dindex_tmp) recursive_delete(dindex_tmp);
394 32 : lock_release(lock);
395 32 : lock_free(&lock);
396 32 : free_w(&fullpath);
397 32 : free_w(&hlinks);
398 32 : free_w(&cindex_tmp);
399 32 : free_w(&cindex_new);
400 32 : free_w(&dindex_tmp);
401 32 : free_w(&dindex_new);
402 32 : free_w(&dindex_old);
403 32 : return ret;
404 : }
|