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