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