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