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