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 :
16 0 : static int incexc_matches(const char *fullrealwork, const char *incexc)
17 : {
18 0 : int ret=0;
19 0 : int got=0;
20 0 : struct fzp *fzp=NULL;
21 0 : char buf[4096]="";
22 0 : const char *inc=NULL;
23 0 : char *old_incexc_path=NULL;
24 0 : if(!(old_incexc_path=prepend_s(fullrealwork, "incexc")))
25 : return -1;
26 0 : if(!(fzp=fzp_open(old_incexc_path, "rb")))
27 : {
28 : // Assume that no incexc file could be found because the client
29 : // was on an old version. Assume resume is OK and return 1.
30 : ret=1;
31 : goto end;
32 : }
33 : inc=incexc;
34 0 : while((got=fzp_read(fzp, buf, sizeof(buf)))>0)
35 : {
36 0 : if(strlen(inc)<(size_t)got) break;
37 0 : if(strncmp(buf, inc, got)) break;
38 0 : inc+=got;
39 : }
40 0 : if(inc && strlen(inc)) ret=0;
41 0 : else ret=1;
42 : end:
43 0 : fzp_close(&fzp);
44 0 : free_w(&old_incexc_path);
45 0 : return ret;
46 : }
47 :
48 0 : static int working_delete(struct async *as, struct sdirs *sdirs)
49 : {
50 : // Try to remove it and start again.
51 0 : logp("deleting old working directory\n");
52 0 : if(recursive_delete(sdirs->rworking))
53 : {
54 0 : log_and_send(as->asfd,
55 : "Old working directory is in the way.\n");
56 : return -1;
57 : }
58 : // Get rid of the symlink.
59 0 : unlink(sdirs->working);
60 : return 0;
61 : }
62 :
63 0 : static int working_resume(struct async *as, struct sdirs *sdirs,
64 : const char *incexc, int *resume, struct conf **cconfs)
65 : {
66 0 : if(get_string(cconfs[OPT_RESTORE_CLIENT]))
67 : {
68 : // This client is not the original client, resuming might cause
69 : // all sorts of trouble.
70 0 : log_and_send(as->asfd, "Found interrupted backup - not resuming because the connected client is not the original");
71 0 : return -1;
72 : }
73 :
74 0 : logp("Found interrupted backup.\n");
75 :
76 : // Check that the current incexc configuration is the same
77 : // as before.
78 0 : switch(incexc_matches(sdirs->rworking, incexc))
79 : {
80 : case 1:
81 : // Attempt to resume on the next backup.
82 0 : logp("Will resume on the next backup request.\n");
83 0 : *resume=1;
84 0 : return 0;
85 : case 0:
86 0 : logp("Includes/excludes changed since last backup.\n");
87 0 : logp("Will delete instead of resuming.\n");
88 0 : return working_delete(as, sdirs);
89 : case -1:
90 : default:
91 : return -1;
92 : }
93 : }
94 :
95 0 : static int get_fullrealwork(struct sdirs *sdirs)
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 0 : logp("removing dangling working symlink %s -> %s\n",
105 : 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 0 : logp("working_dir_recovery_method: %s\n",
161 : 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 0 : enum recovery_method recovery_method=get_e_recovery_method(
174 : 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(sdirs)) 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);
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 0 : snprintf(msg, sizeof(msg),
222 : "Unknown working_dir_recovery_method: %d\n",
223 : (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 0 : 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 6 : int check_for_rubble(struct async *as,
278 : struct sdirs *sdirs, const char *incexc,
279 : int *resume, struct conf **cconfs)
280 : {
281 6 : struct asfd *asfd=as->asfd;
282 :
283 6 : 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 0 : log_and_send(asfd,
291 : "Finishing directory is not a symlink.\n");
292 0 : return -1;
293 : }
294 :
295 6 : switch(is_lnk(sdirs->working))
296 : {
297 : case 1:
298 0 : return recover_working(as,
299 : sdirs, incexc, resume, cconfs);
300 : case 0:
301 0 : log_and_send(asfd,
302 : "Working directory is not a symlink.\n");
303 0 : return -1;
304 : }
305 :
306 6 : switch(is_lnk(sdirs->currenttmp))
307 : {
308 : case 1:
309 0 : return recover_currenttmp(sdirs);
310 : case 0:
311 0 : log_and_send(asfd,
312 : "Currenttmp directory is not a symlink.\n");
313 0 : return -1;
314 : }
315 :
316 : return 0;
317 : }
|