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