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