2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
5 * See wvsyncarbiter.h for details.
9 #include "wvsyncarbiter.h"
10 #include "wvsyncobj.h"
16 bool WvSyncArbiter::arblocal(UniConf
&root
, WvSyncObj
&obj
, WvBuf
&sigbuf
)
18 // make a local handle to the file in the given tree
19 UniConf
cfg(root
[obj
.name
]);
20 cfg
["act"].setme(WvString::null
);
22 time_t oldmtime
= cfg
.xgetint("mtime", -1);
23 WvString oldmeta
= cfg
["meta"].getme();
25 // remember a few old things for revertability, should a
26 // transfer fail or be unacceptable.
28 cfg
["old mtime"].setmeint(oldmtime
);
29 cfg
["old meta"].setme(oldmeta
);
30 cfg
["old md5sig"].setme(cfg
["md5sig"].getme());
34 // if it didn't exist, obviously we're adding.
36 int sig_ret
= obj
.getsig(sigbuf
);
37 log(WvLog::Debug5
, "getsig: %s\n", sig_ret
);
38 int siglen
= sigbuf
.used();
42 log(WvLog::Debug2
, " NEW: %s\n", obj
.name
);
46 // special case: if isdir(), that's different from
47 // something that DOES have contents, but 0 bytes in it!
50 // used to unget() here, but sometimes siglen was larger
51 // than maxungettable. Make a copy instead... (sigh)
53 tmpbuf2
.put(sigbuf
.peek(0, siglen
), siglen
);
54 WvMD5Digest().flush(tmpbuf2
, tmpbuf
, true);
55 md5sig
= WvHexEncoder().strflushbuf(tmpbuf
, true);
60 cfg
["md5sig"].setme(md5sig
);
61 cfg
["meta"].setme(obj
.getmeta());
62 cfg
["act"].setme("new");
63 cfg
["mtime"].setmeint(obj
.getlastmodtime());
66 return false; // couldn't getsig(), so it's broken. skip it.
70 // if it did exist in cfg, we're (potentially) changing.
71 // check the mtime and signature.
72 if (obj
.isunchanged(oldmtime
, oldmeta
))
74 // if mtimes and meta match, nothing has changed.
75 log(WvLog::Debug5
, "UNCHANGED: %s\n", obj
.name
);
76 cfg
["act"].setme("ok");
78 //jdeboer: Lets compute a sig, maybe good things will happen.
79 //dcoombs: FIXME: why is this necessary?
85 // mtime mismatch, so we have to think harder.
86 // if the signature has changed, the contents obviously have
87 // too. if the signatures match, but the metadata has changed,
88 // then we can send just the metadata and save time.
92 int siglen
= sigbuf
.used();
93 WvString meta
= obj
.getmeta();
97 // couldn't getsig(), I guess it was deleted.
98 // should still return true!
99 log(WvLog::Debug2
, " GONE: %s\n", obj
.name
);
100 cfg
["act"].setme("del");
101 WvSyncObj::forget(cfg
);
108 // special case: if isdir(), that's different from
109 // something that DOES have contents, but 0 bytes in it!
112 // used to unget() here, but sometimes siglen was larger
113 // than maxungettable. Make a copy instead... (sigh)
115 tmpbuf2
.put(sigbuf
.peek(0, siglen
), siglen
);
116 WvMD5Digest().flush(tmpbuf2
, tmpbuf
, true);
117 md5sig
= WvHexEncoder().strflushbuf(tmpbuf
, true);
122 WvString oldmd5sig
= cfg
["md5sig"].getme();
123 if (strcmp(md5sig
, oldmd5sig
))
126 log(WvLog::Debug2
, " CHANGED: %s\n", obj
.name
);
127 cfg
["act"].setme("mod");
128 cfg
["md5sig"].setme(md5sig
);
132 // signatures don't differ. just send metadata.
133 log(WvLog::Debug2
, " META: %s\n", obj
.name
);
134 cfg
["act"].setme("meta");
136 cfg
["meta"].setme(meta
);
137 cfg
["mtime"].setmeint(obj
.getlastmodtime());
147 bool WvSyncArbiter::arbremote(UniConf
&root
, WvSyncObj
&obj
,
148 WvStringParm remact
, WvStringParm remmeta
,
149 time_t remmtime
, WvStringParm remmd5sig
)
151 // make a local handle to the file in the given tree
152 UniConf
cfg(root
[obj
.name
]);
154 // First question: Did the remote end know about this object? If not,
155 // remmtime will be -1.
158 // If the other side didn't know about it, we obviously can't
159 // revert our own, even if we thought we could. And if it's
160 // locally anything other than delete, we might as well call it
161 // new, since the other side will.
162 WvSyncObj::forget(cfg
);
164 if (cfg
["act"].getme() != "del")
165 cfg
["act"].setme("new");
168 return false; // won't request
171 // So the remote side knew about it.
173 // Next question: Did WE know about it? If not, obj.isok() will be
174 // false because we will have failed to calculate the signature. In
175 // that case, we should consider it new from remote, and request it,
176 // unless the remote side is deleting it.
181 log(WvLog::Debug1
, "NEW FROM REMOTE: %s\n", obj
.name
);
182 cfg
["md5sig"].setme(remmd5sig
);
183 cfg
["mtime"].setme(remmtime
);
184 cfg
["meta"].setme(remmeta
);
185 cfg
["act"].setme("new");
187 return true; // will request
190 return false; // do nothing
193 // OK, both sides knew about the object. Think harder.
194 WvString
act(cfg
["act"].getme("ok"));
196 if (act
== "new" || act
== "mod")
197 ret
= arbr_mod(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
198 else if (act
== "meta")
199 ret
= arbr_meta(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
200 else if (act
== "del")
201 ret
= arbr_del(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
203 ret
= arbr_ok(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
210 void WvSyncArbiter::arbr_win(UniConf
&root
, WvSyncObj
&obj
,
211 WvStringParm remact
, WvStringParm remmeta
,
212 time_t remmtime
, WvStringParm remmd5sig
)
214 // make a local handle to the file in the given tree
215 UniConf
cfg(root
[obj
.name
]);
217 if (cfg
["act"].getme() != "new" || remact
!= "new")
218 cfg
["act"].setme("mod");
220 // if we win, the remote side will request from us.
221 // but we can't revert, because the remote side has renamed its copy.
222 WvSyncObj::forget(cfg
);
224 // find out the name of the renamed version we have to download.
225 WvString newname
= obj
.conflictname(remmtime
);
226 log(WvLog::Warning
, "CONFLICT: remote '%s' will be called '%s'\n",
228 wvcon
->print("FIXME: tell protocol it has to download %s\n", newname
);
230 // we copy the information (mtime, md5sig, meta) from our own winning
231 // object, since we're in fact copying the winning object to give us
232 // something to rsync. when we request the renamed version later,
233 // we'll figure stuff out.
234 obj
.makecopy(newname
);
235 UniConf
newcfg(root
[newname
]);
236 cfg
.copy(newcfg
, false);
237 newcfg
["act"].setme("ok");
238 WvSyncObj::forget(newcfg
);
242 void WvSyncArbiter::arbr_lose(UniConf
&root
, WvSyncObj
&obj
,
243 WvStringParm remact
, WvStringParm remmeta
,
244 time_t remmtime
, WvStringParm remmd5sig
)
246 // make a local handle to the file in the given tree
247 UniConf
cfg(root
[obj
.name
]);
249 if (cfg
["act"].getme() != "new" || remact
!= "new")
250 cfg
["act"].setme("mod");
252 // if we lose, rename the object on this end, and mark it new.
253 WvString newname
= obj
.conflictname(obj
.getlastmodtime());
254 log(WvLog::Warning
, "CONFLICT: local '%s' will be renamed to '%s'\n",
257 obj
.makecopy(newname
);
258 UniConf
newcfg(root
[newname
]);
259 cfg
.copy(newcfg
, false);
260 newcfg
["act"].setme("new"); // redundant, since arblocal will
261 // decide the same thing, but it helps
262 // me think straight.
263 WvSyncObj::forget(newcfg
);
265 // we still want to receive the winning object!
266 WvSyncObj::forget(cfg
);
267 cfg
["mtime"].setmeint(remmtime
);
268 cfg
["md5sig"].setme(remmd5sig
);
269 cfg
["meta"].setme(remmeta
);
273 bool WvSyncArbiter::arbr_conflict(UniConf
&root
, WvSyncObj
&obj
,
274 WvStringParm remact
, WvStringParm remmeta
,
275 time_t remmtime
, WvStringParm remmd5sig
)
277 // make a local handle to the file in the given tree
278 UniConf
cfg(root
[obj
.name
]);
280 // the most recent change wins. If they were at the same time, pick
281 // one randomly using strcmp. Ha ha ha ha ha FIXME.
283 if (obj
.getlastmodtime() > remmtime
)
285 arbr_win(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
286 return false; // we won, don't request
288 else if (obj
.getlastmodtime() < remmtime
)
290 arbr_lose(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
291 return true; // we lost, must probably request download
293 else if (strcmp(cfg
["md5sig"].getme(), remmd5sig
) < 0)
295 arbr_win(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
296 return false; // we won, don't request
300 arbr_lose(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
301 return true; // we lost, must probably request donwload
306 bool WvSyncArbiter::arbr_ok(UniConf
&root
, WvSyncObj
&obj
,
307 WvStringParm remact
, WvStringParm remmeta
,
308 time_t remmtime
, WvStringParm remmd5sig
)
310 // make a local handle to the file in the given tree
311 UniConf
cfg(root
[obj
.name
]);
313 // no change on our end, so:
314 // 1) no change on remote end, we're fine
315 // 2) remote end new/mod, we surrender
316 // 3) remote end meta, we accept the new meta info
317 // 4) remote end del, delete here too
319 if (remact
== "new" || remact
== "mod")
322 WvSyncObj::forget(cfg
);
324 cfg
["mtime"].setmeint(remmtime
);
325 cfg
["md5sig"].setme(remmd5sig
);
326 cfg
["meta"].setme(remmeta
);
327 cfg
["act"].setme(remact
);
329 // if the object has changed in nature so that it no longer has
330 // contents, consider that a conflict. The one with no contents
331 // wins. This is a little random, but it's probably only used for
332 // synchronizing files, where something with no contents is a
333 // directory, and when directories turn into files bad things can
335 if (!obj
.isdir() && obj
.isdir(remmeta
))
337 arbr_lose(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
338 return true; // we lost, must request download
340 else if (obj
.isdir() && !obj
.isdir(remmeta
))
342 arbr_win(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
343 return false; // we won, don't request
347 return true; // will request
352 cfg
["act"].setme(remact
);
353 return false; // nothing to request
356 if (remact
== "meta")
358 cfg
["meta"].setme(remmeta
);
359 cfg
["mtime"].setmeint(remmtime
);
360 obj
.applymeta(remmeta
, remmtime
);
361 return false; // don't need to request the actual data
364 return false; // don't need to request anything
368 bool WvSyncArbiter::arbr_mod(UniConf
&root
, WvSyncObj
&obj
,
369 WvStringParm remact
, WvStringParm remmeta
,
370 time_t remmtime
, WvStringParm remmd5sig
)
372 // make a local handle to the file in the given tree
373 UniConf
cfg(root
[obj
.name
]);
375 // changed on our end, so
376 // 1) no change on remote end, it'll surrender
377 // 2) remote end new/mod, check for conflict...
378 // 3) remote end meta, conflict
379 // 4) remote end del, it'll forget it
381 // can't revert if it's new
382 if (cfg
["act"].getme() == "new")
383 WvSyncObj::forget(cfg
);
385 if (remact
== "meta")
387 // if it was changed locally, and only the metadata changed on the
388 // remote end, I guarantee you have a conflict.
389 return arbr_conflict(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
392 if (remact
== "new" || remact
== "mod")
394 // if the object has changed in nature so that it no longer has
395 // contents, consider that a conflict. The one with no contents
396 // wins. This is a little random, but it's probably only used for
397 // synchronizing files, where something with no contents is a
398 // directory, and when directories turn into files bad things can
400 if (!obj
.isdir() && obj
.isdir(remmeta
))
402 arbr_lose(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
403 return true; // we lost, must request download
405 else if (obj
.isdir() && !obj
.isdir(remmeta
))
407 arbr_win(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
408 return false; // we won, don't request
411 // no conflict, at least not due to type change
413 // if the md5sigs and metadata match, the same change was made on
414 // both ends! just update the mtime in that case.
415 if (cfg
["md5sig"].getme() == remmd5sig
416 && obj
.getmeta() == remmeta
)
418 // most recent mtime and meta information wins
419 if (obj
.getlastmodtime() < remmtime
)
421 cfg
["mtime"].setmeint(remmtime
);
422 obj
.applymeta(remmeta
, remmtime
); // remmeta was the same
429 // if there was an md5sig or metadata conflict...
430 return arbr_conflict(root
, obj
, remact
, remmeta
,
431 remmtime
, remmd5sig
);
437 // no point being revertible if other side isn't
438 WvSyncObj::forget(cfg
);
441 return false; // other side is requesting, not us
445 bool WvSyncArbiter::arbr_meta(UniConf
&root
, WvSyncObj
&obj
,
446 WvStringParm remact
, WvStringParm remmeta
,
447 time_t remmtime
, WvStringParm remmd5sig
)
449 // make a local handle to the file in the given tree
450 UniConf
cfg(root
[obj
.name
]);
452 // metadata changed on our end, so
453 // 1) no change on remote end, it'll surrender
454 // 2) remote end new/mod, conflict
455 // 3) remote end meta, check for conflict...
456 // 4) remote end del, it'll forget it
458 if (remact
== "new" || remact
== "mod")
460 // if it was changed remotely, and only the metadata changed on
461 // this end, I guarantee you have a conflict.
462 return arbr_conflict(root
, obj
, remact
, remmeta
, remmtime
, remmd5sig
);
465 if (remact
== "meta")
467 // if the metadata matches, the same change was made on both ends!
468 // just update the mtime in that case.
469 if (obj
.getmeta() == remmeta
)
471 // most recent mtime and meta information wins
472 if (obj
.getlastmodtime() < remmtime
)
474 cfg
["mtime"].setmeint(remmtime
);
475 obj
.applymeta(remmeta
, remmtime
); // remmeta was the same
482 // if there was a metadata conflict...
483 return arbr_conflict(root
, obj
, remact
, remmeta
,
484 remmtime
, remmd5sig
);
490 // no point being revertible if other side isn't
491 WvSyncObj::forget(cfg
);
498 bool WvSyncArbiter::arbr_del(UniConf
&root
, WvSyncObj
&obj
,
499 WvStringParm remact
, WvStringParm remmeta
,
500 time_t remmtime
, WvStringParm remmd5sig
)
502 // make a local handle to the file in the given tree
503 UniConf
cfg(root
[obj
.name
]);
505 // deleted on our end, so
506 // 1) no change on remote end, it'll delete
507 // 2) remote end new/mod, forget the delete, surrender
508 // 3) remote end meta, be safe, forget the delete, surrender
509 // 4) remote end del, we're fine
511 WvSyncObj::forget(cfg
);
513 if (remact
== "new" || remact
== "mod" || remact
== "meta")
515 cfg
["mtime"].setmeint(remmtime
);
516 cfg
["md5sig"].setme(remmd5sig
);
517 cfg
["meta"].setme(remmeta
);
518 cfg
["act"].setme(remact
);
520 return true; // will request