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