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