Line data Source code
1 : #include "../burp.h"
2 : #include "../alloc.h"
3 : #include "../asfd.h"
4 : #include "../async.h"
5 : #include "../fsops.h"
6 : #include "../fzp.h"
7 : #include "../handy.h"
8 : #include "../lock.h"
9 : #include "../log.h"
10 : #include "../prepend.h"
11 : #include "backup_phase4.h"
12 : #include "compress.h"
13 : #include "rubble.h"
14 : #include "run_action.h"
15 : #include "sdirs.h"
16 : #include "timestamp.h"
17 : #include "zlibio.h"
18 :
19 : static char *get_resume_path(const char *path)
20 : {
21 0 : return prepend_s(path, "resumed");
22 : }
23 :
24 0 : static int append_to_resume_file(const char *path)
25 : {
26 0 : int ret=-1;
27 0 : char tstmp[48]="";
28 0 : char *resume_path=NULL;
29 :
30 0 : if(timestamp_get_new(/*index*/0,
31 : tstmp, sizeof(tstmp),
32 : /*bufforfile*/NULL, /*bs*/0,
33 : /*format*/NULL))
34 : goto end;
35 0 : if(!(resume_path=get_resume_path(path)))
36 : goto end;
37 0 : ret=timestamp_write(resume_path, tstmp);
38 : end:
39 0 : free_w(&resume_path);
40 0 : return ret;
41 : }
42 :
43 0 : static int resume_count(const char *path)
44 : {
45 0 : int count=-1;
46 0 : char buf[256]="";
47 0 : struct fzp *fzp=NULL;
48 0 : char *resume_path=NULL;
49 :
50 0 : if(!(resume_path=get_resume_path(path)))
51 : goto end;
52 0 : if(!(fzp=fzp_open(resume_path, "rb")))
53 : goto end;
54 : count=0;
55 0 : while(fzp_gets(fzp, buf, sizeof(buf)))
56 0 : count++;
57 : end:
58 0 : fzp_close(&fzp);
59 0 : free_w(&resume_path);
60 0 : return count;
61 : }
62 :
63 0 : static int incexc_matches(const char *fullrealwork, const char *incexc)
64 : {
65 0 : int ret=0;
66 0 : int got=0;
67 0 : struct fzp *fzp=NULL;
68 0 : char buf[4096]="";
69 0 : const char *inc=NULL;
70 0 : char *old_incexc_path=NULL;
71 0 : if(!(old_incexc_path=prepend_s(fullrealwork, "incexc")))
72 : return -1;
73 0 : if(!(fzp=fzp_open(old_incexc_path, "rb")))
74 : {
75 : // Assume that no incexc file could be found because the client
76 : // was on an old version. Assume resume is OK and return 1.
77 : ret=1;
78 : goto end;
79 : }
80 : inc=incexc;
81 0 : while((got=fzp_read(fzp, buf, sizeof(buf)))>0)
82 : {
83 0 : if(strlen(inc)<(size_t)got) break;
84 0 : if(strncmp(buf, inc, got)) break;
85 0 : inc+=got;
86 : }
87 0 : if(inc && strlen(inc)) ret=0;
88 0 : else ret=1;
89 : end:
90 0 : fzp_close(&fzp);
91 0 : free_w(&old_incexc_path);
92 0 : return ret;
93 : }
94 :
95 0 : static int working_delete(
96 : struct async *as,
97 : struct sdirs *sdirs,
98 : struct conf **cconfs
99 : ) {
100 : // Try to remove it and start again.
101 0 : logp("deleting old working directory\n");
102 :
103 0 : if(get_int(cconfs[OPT_N_FAILURE_BACKUP_WORKING_DELETION]))
104 : {
105 : // The status needs to be non-zero in order to send a failure
106 : // notification.
107 0 : int status=1;
108 :
109 : // Need to do notify before actually deleting, so that it grabs
110 : // the not-yet deleted log. Close the log file pointer first.
111 0 : log_fzp_set(NULL, cconfs);
112 :
113 0 : maybe_do_notification(as->asfd, status,
114 0 : sdirs->client, sdirs->current,
115 : "log", "backup", cconfs);
116 : }
117 :
118 0 : if(recursive_delete(sdirs->rworking))
119 : {
120 0 : log_and_send(as->asfd,
121 : "Old working directory is in the way.\n");
122 : return -1;
123 : }
124 : // Get rid of the symlink.
125 0 : unlink(sdirs->working);
126 : return 0;
127 : }
128 :
129 0 : static int working_resume(struct async *as, struct sdirs *sdirs,
130 : const char *incexc, int *resume, struct conf **cconfs)
131 : {
132 0 : if(get_string(cconfs[OPT_SUPER_CLIENT]))
133 : {
134 : // This client is not the original client, resuming might cause
135 : // all sorts of trouble.
136 0 : log_and_send(as->asfd, "Found interrupted backup - not resuming because the connected client is not the original");
137 0 : return -1;
138 : }
139 :
140 0 : logp("Found interrupted backup.\n");
141 :
142 : // Check that the current incexc configuration is the same
143 : // as before.
144 0 : switch(incexc_matches(sdirs->rworking, incexc))
145 : {
146 : case 1:
147 : // Attempt to resume on the next backup.
148 0 : logp("Will resume on the next backup request.\n");
149 0 : *resume=1;
150 0 : return 0;
151 : case 0:
152 0 : logp("Includes/excludes changed since last backup.\n");
153 0 : logp("Will delete instead of resuming.\n");
154 0 : return working_delete(as, sdirs, cconfs);
155 : case -1:
156 : default:
157 : return -1;
158 : }
159 : }
160 :
161 0 : static int get_fullrealwork(struct sdirs *sdirs)
162 : {
163 : struct stat statp;
164 :
165 0 : if(sdirs_get_real_working_from_symlink(sdirs))
166 : return -1;
167 :
168 0 : if(lstat(sdirs->rworking, &statp))
169 : {
170 0 : logp("removing dangling working symlink %s -> %s\n",
171 : sdirs->working, sdirs->rworking);
172 0 : unlink(sdirs->working);
173 0 : free_w(&sdirs->rworking);
174 : }
175 : return 0;
176 : }
177 :
178 0 : static int do_unlink(const char *path)
179 : {
180 0 : if(unlink(path))
181 : {
182 0 : logp("Could not unlink '%s': %s", path, strerror(errno));
183 0 : return -1;
184 : }
185 : return 0;
186 : }
187 :
188 0 : static int fix_log_finishing(struct sdirs *sdirs, struct conf **cconfs)
189 : {
190 0 : int ret=-1;
191 0 : char *path_log=NULL;
192 0 : char *path_log_gz=NULL;
193 0 : char *path_log_tmp=NULL;
194 :
195 0 : if(!(path_log=prepend_s(sdirs->finishing, "log"))
196 0 : || !(path_log_gz=prepend_s(sdirs->finishing, "log.gz"))
197 0 : || !(path_log_tmp=prepend_s(sdirs->finishing, "log.tmp")))
198 : goto end;
199 :
200 0 : if(is_reg_lstat(path_log)==1)
201 : {
202 0 : if(is_reg_lstat(path_log_gz)==1)
203 : {
204 : // If this has happened, either file should be good to
205 : // use. Delete the compressed one, as we will keep
206 : // logging to the uncompressed one.
207 0 : do_unlink(path_log_gz);
208 : goto end;
209 : }
210 : else
211 : {
212 : // Everything is OK.
213 : ret=0;
214 : }
215 : }
216 : else
217 : {
218 0 : if(is_reg_lstat(path_log_gz)==1)
219 : {
220 : // Need to inflate so that we can log to it again,
221 : // and compress it later.
222 0 : if(zlib_inflate(/*asfd*/NULL, path_log_gz,
223 : path_log_tmp, get_cntr(cconfs)))
224 : goto end;
225 0 : if(do_rename(path_log_tmp, path_log))
226 : goto end;
227 0 : do_unlink(path_log_gz);
228 : goto end;
229 : }
230 : else
231 : {
232 0 : logp("Neither %s nor %s exist. That is odd!",
233 : path_log, path_log_gz);
234 0 : ret=0;
235 : }
236 : }
237 : end:
238 0 : if(!ret)
239 : {
240 : // Should be OK to re-open the log file now.
241 0 : if(log_fzp_set(path_log, cconfs))
242 0 : ret=-1;
243 : }
244 :
245 0 : free_w(&path_log);
246 0 : free_w(&path_log_gz);
247 0 : free_w(&path_log_tmp);
248 0 : return ret;
249 : }
250 :
251 0 : static int recover_finishing(struct async *as,
252 : struct sdirs *sdirs, struct conf **cconfs)
253 : {
254 0 : char msg[128]="";
255 0 : struct asfd *asfd=as->asfd;
256 :
257 0 : if(fix_log_finishing(sdirs, cconfs))
258 : return -1;
259 :
260 0 : logp("Found finishing symlink - attempting to complete prior backup!\n");
261 :
262 0 : if(append_to_resume_file(sdirs->finishing))
263 : return -1;
264 :
265 0 : snprintf(msg, sizeof(msg),
266 : "Now finalising previous backup of client. "
267 : "Please try again later.");
268 0 : asfd->write_str(asfd, CMD_ERROR, msg);
269 :
270 : // Need to check whether the log has been compressed. If it hasn't,
271 : // we need to inflate it again.
272 :
273 : // Do not need the client connected any more.
274 : // Disconnect.
275 0 : logp("Disconnect from client.\n");
276 0 : as->asfd_remove(as, asfd);
277 0 : asfd_close(asfd);
278 :
279 0 : if(backup_phase4_server_all(sdirs, cconfs))
280 : {
281 0 : logp("Problem with prior backup. Please check the client log on the server.");
282 0 : return -1;
283 : }
284 :
285 0 : logp("Prior backup completed OK\n");
286 0 : log_fzp_set(NULL, cconfs);
287 0 : compress_filename(sdirs->finishing,
288 : "log", "log.gz", get_int(cconfs[OPT_COMPRESSION]));
289 :
290 : // backup_stats?!
291 :
292 :
293 : // Move the symlink to indicate that we are now in the end
294 : // phase.
295 : // FIX THIS: Check whether the rename race condition is recoverable
296 : // here.
297 0 : if(do_rename(sdirs->finishing, sdirs->current)) return -1;
298 0 : return 0;
299 : }
300 :
301 0 : static void log_recovery_method(struct sdirs *sdirs,
302 : enum recovery_method recovery_method)
303 : {
304 0 : logp("found old working directory: %s\n", sdirs->rworking);
305 0 : logp("working_dir_recovery_method: %s\n",
306 : recovery_method_to_str(recovery_method));
307 0 : }
308 :
309 0 : static int recover_working(struct async *as,
310 : struct sdirs *sdirs, const char *incexc,
311 : int *resume, struct conf **cconfs)
312 : {
313 0 : int ret=-1;
314 0 : char msg[256]="";
315 0 : char *logpath=NULL;
316 : struct stat statp;
317 0 : char *phase1datatmp=NULL;
318 0 : int resume_attempts=0;
319 0 : int max_resume_attempts=get_int(cconfs[OPT_MAX_RESUME_ATTEMPTS]);
320 0 : enum recovery_method recovery_method=get_e_recovery_method(
321 : cconfs[OPT_WORKING_DIR_RECOVERY_METHOD]);
322 :
323 : // The working directory has not finished being populated.
324 : // Check what to do.
325 0 : if(get_fullrealwork(sdirs)) goto end;
326 0 : if(!sdirs->rworking) goto end;
327 :
328 0 : log_recovery_method(sdirs, recovery_method);
329 :
330 0 : if(!(phase1datatmp=get_tmp_filename(sdirs->phase1data)))
331 : goto end;
332 : // If there is still a phase1 tmp file...
333 0 : if(!lstat(phase1datatmp, &statp)
334 0 : ||
335 : // ...or phase1 has not even got underway yet...
336 0 : (lstat(phase1datatmp, &statp)
337 0 : && lstat(sdirs->phase1data, &statp)
338 0 : && lstat(sdirs->changed, &statp)
339 0 : && lstat(sdirs->unchanged, &statp)))
340 : {
341 : // ...phase 1 did not complete - delete everything.
342 0 : logp("Phase 1 has not completed.\n");
343 0 : recovery_method=RECOVERY_METHOD_DELETE;
344 : }
345 : else
346 : {
347 0 : append_to_resume_file(sdirs->working);
348 :
349 0 : if(max_resume_attempts>0)
350 : {
351 0 : logp("max_resume_attempts: %d\n", max_resume_attempts);
352 0 : if((resume_attempts=resume_count(sdirs->working))<0)
353 : goto end;
354 0 : if(resume_attempts > max_resume_attempts)
355 : {
356 0 : logp("no resume attempts remaining, will delete\n");
357 0 : recovery_method=RECOVERY_METHOD_DELETE;
358 : }
359 : else
360 : {
361 0 : logp("resume attempts: %d\n", resume_attempts);
362 0 : logp("remaining resume attempts: %d\n",
363 : max_resume_attempts - resume_attempts);
364 : }
365 : }
366 : }
367 :
368 0 : if(recovery_method==RECOVERY_METHOD_DELETE)
369 : {
370 0 : ret=working_delete(as, sdirs, cconfs);
371 0 : goto end;
372 : }
373 :
374 : // We are not deleting the old working directory - open the log inside
375 : // for appending.
376 0 : if(!(logpath=prepend_s(sdirs->rworking, "log"))
377 0 : || log_fzp_set(logpath, cconfs))
378 : goto end;
379 :
380 0 : switch(recovery_method)
381 : {
382 : case RECOVERY_METHOD_DELETE:
383 : // Dealt with above.
384 : break;
385 : case RECOVERY_METHOD_RESUME:
386 0 : ret=working_resume(as, sdirs, incexc, resume, cconfs);
387 0 : break;
388 : case RECOVERY_METHOD_UNSET:
389 : default:
390 0 : snprintf(msg, sizeof(msg),
391 : "Unknown working_dir_recovery_method: %d\n",
392 : (int)recovery_method);
393 0 : log_and_send(as->asfd, msg);
394 0 : break;
395 : }
396 :
397 : end:
398 0 : free_w(&logpath);
399 0 : free_w(&phase1datatmp);
400 0 : log_fzp_set(NULL, cconfs); // fclose the logfzp
401 0 : return ret;
402 : }
403 :
404 0 : static int recover_currenttmp(struct sdirs *sdirs)
405 : {
406 0 : logp("Found currenttmp symlink\n");
407 0 : switch(is_lnk_valid(sdirs->currenttmp))
408 : {
409 : case 0:
410 0 : logp("But currenttmp is not pointing at something valid.\n");
411 0 : logp("Deleting it.\n");
412 0 : if(append_to_resume_file(sdirs->currenttmp))
413 : return -1;
414 0 : return unlink_w(sdirs->currenttmp, __func__);
415 : case -1:
416 : return -1;
417 : }
418 :
419 0 : switch(is_lnk_lstat(sdirs->current))
420 : {
421 : case 0:
422 0 : logp("But current already exists and is not a symlink!\n");
423 0 : logp("Giving up.\n");
424 : return -1;
425 : case 1:
426 0 : logp("But current symlink already exists!\n");
427 0 : switch(is_lnk_valid(sdirs->current))
428 : {
429 : case 0:
430 0 : logp("But current symlink is not pointing at something valid.\n");
431 0 : logp("Replacing current with currenttmp.\n");
432 0 : if(append_to_resume_file(
433 0 : sdirs->currenttmp))
434 : return -1;
435 0 : return do_rename(sdirs->currenttmp,
436 0 : sdirs->current);
437 : case 1:
438 0 : logp("And current symlink points at something valid.\n");
439 0 : logp("Deleting currenttmp.\n");
440 0 : return unlink_w(sdirs->currenttmp, __func__);
441 : default:
442 : return -1;
443 : }
444 : default:
445 0 : logp("Renaming currenttmp to current\n");
446 0 : if(append_to_resume_file(sdirs->currenttmp))
447 : return -1;
448 0 : return do_rename(sdirs->currenttmp, sdirs->current);
449 : }
450 : }
451 :
452 1 : int check_for_rubble(struct sdirs *sdirs)
453 : {
454 1 : return is_lnk_lstat(sdirs->finishing)>0
455 1 : || is_lnk_lstat(sdirs->working)>0
456 2 : || is_lnk_lstat(sdirs->currenttmp)>0;
457 : }
458 :
459 : // Return 1 if the backup is now finalising.
460 2 : int check_for_rubble_and_clean(struct async *as,
461 : struct sdirs *sdirs, const char *incexc,
462 : int *resume, struct conf **cconfs)
463 : {
464 2 : struct asfd *asfd=as->asfd;
465 :
466 2 : switch(is_lnk_lstat(sdirs->finishing))
467 : {
468 : case 1:
469 0 : if(recover_finishing(as, sdirs, cconfs))
470 : return -1;
471 0 : return 1;
472 : case 0:
473 0 : log_and_send(asfd,
474 : "Finishing directory is not a symlink.\n");
475 0 : return -1;
476 : }
477 :
478 2 : switch(is_lnk_lstat(sdirs->working))
479 : {
480 : case 1:
481 0 : return recover_working(as,
482 : sdirs, incexc, resume, cconfs);
483 : case 0:
484 0 : log_and_send(asfd,
485 : "Working directory is not a symlink.\n");
486 0 : return -1;
487 : }
488 :
489 2 : switch(is_lnk_lstat(sdirs->currenttmp))
490 : {
491 : case 1:
492 0 : return recover_currenttmp(sdirs);
493 : case 0:
494 0 : log_and_send(asfd,
495 : "Currenttmp directory is not a symlink.\n");
496 0 : return -1;
497 : }
498 :
499 : return 0;
500 : }
|