applylog.c 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. #include "all.h"
  2. #define Nwork 16
  3. int localdirstat(char*, Dir*);
  4. int ismatch(char*);
  5. void conflict(char*, char*, ...);
  6. void error(char*, ...);
  7. int isdir(char*);
  8. void worker(int fdf, int fdt, char *from, char *to);
  9. vlong nextoff(void);
  10. void failure(void *, char *note);
  11. QLock lk;
  12. vlong off;
  13. int errors;
  14. int nconf;
  15. int donothing;
  16. int verbose;
  17. char **match;
  18. int nmatch;
  19. int tempspool = 1;
  20. int safeinstall = 1;
  21. char *lroot;
  22. char *rroot;
  23. Db *clientdb;
  24. int skip;
  25. int douid;
  26. char *mkname(char*, int, char*, char*);
  27. char localbuf[10240];
  28. char remotebuf[10240];
  29. int copyfile(char*, char*, char*, Dir*, int, int*);
  30. ulong maxnow;
  31. int maxn;
  32. char *timefile;
  33. int timefd;
  34. int samecontents(char*, char*);
  35. Db *copyerr;
  36. typedef struct Res Res;
  37. struct Res
  38. {
  39. char c;
  40. char *name;
  41. };
  42. Res *res;
  43. int nres;
  44. void
  45. addresolve(int c, char *name)
  46. {
  47. if(name[0] == '/')
  48. name++;
  49. res = erealloc(res, (nres+1)*sizeof res[0]);
  50. res[nres].c = c;
  51. res[nres].name = name;
  52. nres++;
  53. }
  54. int
  55. resolve(char *name)
  56. {
  57. int i, len;
  58. for(i=0; i<nres; i++){
  59. len = strlen(res[i].name);
  60. if(len == 0)
  61. return res[i].c;
  62. if(strncmp(name, res[i].name, len) == 0 && (name[len]=='/' || name[len] == 0))
  63. return res[i].c;
  64. }
  65. return '?';
  66. }
  67. void
  68. readtimefile(void)
  69. {
  70. int n;
  71. char buf[24];
  72. if((timefd = open(timefile, ORDWR)) < 0
  73. && (timefd = create(timefile, ORDWR|OEXCL, 0666)) < 0)
  74. return;
  75. n = readn(timefd, buf, sizeof buf);
  76. if(n < sizeof buf)
  77. return;
  78. maxnow = atoi(buf);
  79. maxn = atoi(buf+12);
  80. }
  81. void
  82. writetimefile(void)
  83. {
  84. char buf[24+1];
  85. snprint(buf, sizeof buf, "%11lud %11d ", maxnow, maxn);
  86. pwrite(timefd, buf, 24, 0);
  87. }
  88. static void membogus(char**);
  89. void
  90. addce(char *local)
  91. {
  92. char e[ERRMAX];
  93. Dir d;
  94. memset(&d, 0, sizeof d);
  95. rerrstr(e, sizeof e);
  96. d.name = atom(e);
  97. d.uid = "";
  98. d.gid = "";
  99. insertdb(copyerr, atom(local), &d);
  100. }
  101. void
  102. delce(char *local)
  103. {
  104. removedb(copyerr, local);
  105. }
  106. void
  107. chat(char *f, ...)
  108. {
  109. Fmt fmt;
  110. char buf[256];
  111. va_list arg;
  112. if(!verbose)
  113. return;
  114. fmtfdinit(&fmt, 1, buf, sizeof buf);
  115. va_start(arg, f);
  116. fmtvprint(&fmt, f, arg);
  117. va_end(arg);
  118. fmtfdflush(&fmt);
  119. }
  120. void
  121. usage(void)
  122. {
  123. fprint(2, "usage: replica/applylog [-cnSstuv] [-T timefile] clientdb clientroot serverroot [path ...]\n");
  124. exits("usage");
  125. }
  126. int
  127. notexists(char *path)
  128. {
  129. char buf[ERRMAX];
  130. if(access(path, AEXIST) >= 0)
  131. return 0;
  132. rerrstr(buf, sizeof buf);
  133. if(strstr(buf, "entry not found") || strstr(buf, "not exist"))
  134. return 1;
  135. /* some other error, like network hangup */
  136. return 0;
  137. }
  138. void
  139. main(int argc, char **argv)
  140. {
  141. char *f[10], *local, *name, *remote, *s, *t, verb;
  142. int fd, havedb, havelocal, i, k, n, nf, resolve1, skip;
  143. int checkedmatch1, checkedmatch2,
  144. checkedmatch3, checkedmatch4;
  145. ulong now;
  146. Biobuf bin;
  147. Dir dbd, ld, nd, rd;
  148. Avlwalk *w;
  149. Entry *e;
  150. membogus(argv);
  151. quotefmtinstall();
  152. ARGBEGIN{
  153. case 's':
  154. case 'c':
  155. i = ARGC();
  156. addresolve(i, EARGF(usage()));
  157. break;
  158. case 'n':
  159. donothing = 1;
  160. verbose = 1;
  161. break;
  162. case 'S':
  163. safeinstall = 0;
  164. break;
  165. case 'T':
  166. timefile = EARGF(usage());
  167. break;
  168. case 't':
  169. tempspool = 0;
  170. break;
  171. case 'u':
  172. douid = 1;
  173. break;
  174. case 'v':
  175. verbose++;
  176. break;
  177. default:
  178. usage();
  179. }ARGEND
  180. if(argc < 3)
  181. usage();
  182. if(timefile)
  183. readtimefile();
  184. lroot = argv[1];
  185. if(!isdir(lroot))
  186. sysfatal("bad local root directory");
  187. rroot = argv[2];
  188. if(!isdir(rroot))
  189. sysfatal("bad remote root directory");
  190. match = argv+3;
  191. nmatch = argc-3;
  192. for(i=0; i<nmatch; i++)
  193. if(match[i][0] == '/')
  194. match[i]++;
  195. if((clientdb = opendb(argv[0])) == nil)
  196. sysfatal("opendb %q: %r", argv[2]);
  197. copyerr = opendb(nil);
  198. skip = 0;
  199. Binit(&bin, 0, OREAD);
  200. for(; s=Brdstr(&bin, '\n', 1); free(s)){
  201. t = estrdup(s);
  202. nf = tokenize(s, f, nelem(f));
  203. if(nf != 10 || strlen(f[2]) != 1){
  204. skip = 1;
  205. fprint(2, "warning: skipping bad log entry <%s>\n", t);
  206. free(t);
  207. continue;
  208. }
  209. free(t);
  210. now = strtoul(f[0], 0, 0);
  211. n = atoi(f[1]);
  212. verb = f[2][0];
  213. name = f[3];
  214. if(now < maxnow || (now==maxnow && n <= maxn))
  215. continue;
  216. local = mkname(localbuf, sizeof localbuf, lroot, name);
  217. if(strcmp(f[4], "-") == 0)
  218. f[4] = f[3];
  219. remote = mkname(remotebuf, sizeof remotebuf, rroot, f[4]);
  220. rd.name = f[4];
  221. rd.mode = strtoul(f[5], 0, 8);
  222. rd.uid = f[6];
  223. rd.gid = f[7];
  224. rd.mtime = strtoul(f[8], 0, 10);
  225. rd.length = strtoll(f[9], 0, 10);
  226. havedb = finddb(clientdb, name, &dbd)>=0;
  227. havelocal = localdirstat(local, &ld)>=0;
  228. resolve1 = resolve(name);
  229. /*
  230. * if(!ismatch(name)){
  231. * skip = 1;
  232. * continue;
  233. * }
  234. *
  235. * This check used to be right here, but we want
  236. * the time to be able to move forward past entries
  237. * that don't match and have already been applied.
  238. * So now every path below must checked !ismatch(name)
  239. * before making any changes to the local file
  240. * system. The fake variable checkedmatch
  241. * tracks whether !ismatch(name) has been checked.
  242. * If the compiler doesn't produce any used/set
  243. * warnings, then all the paths should be okay.
  244. * Even so, we have the asserts to fall back on.
  245. */
  246. switch(verb){
  247. case 'd': /* delete file */
  248. delce(local);
  249. if(!havelocal) /* doesn't exist; who cares? */
  250. break;
  251. if(access(remote, AEXIST) >= 0) /* got recreated! */
  252. break;
  253. if(!ismatch(name)){
  254. if(!skip)
  255. fprint(2, "stopped updating log apply time because of %s\n", name);
  256. skip = 1;
  257. continue;
  258. }
  259. SET(checkedmatch1);
  260. if(!havedb){
  261. if(resolve1 == 's')
  262. goto DoRemove;
  263. else if(resolve1 == 'c')
  264. goto DoRemoveDb;
  265. conflict(name, "locally created; will not remove");
  266. skip = 1;
  267. continue;
  268. }
  269. assert(havelocal && havedb);
  270. if(dbd.mtime > rd.mtime) /* we have a newer file than what was deleted */
  271. break;
  272. if(samecontents(local, remote) > 0){ /* going to get recreated */
  273. chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
  274. break;
  275. }
  276. if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){ /* locally modified since we downloaded it */
  277. if(resolve1 == 's')
  278. goto DoRemove;
  279. else if(resolve1 == 'c')
  280. break;
  281. conflict(name, "locally modified; will not remove");
  282. skip = 1;
  283. continue;
  284. }
  285. DoRemove:
  286. USED(checkedmatch1);
  287. assert(ismatch(name));
  288. chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
  289. if(donothing)
  290. break;
  291. if(remove(local) < 0){
  292. error("removing %q: %r", name);
  293. skip = 1;
  294. continue;
  295. }
  296. DoRemoveDb:
  297. USED(checkedmatch1);
  298. assert(ismatch(name));
  299. removedb(clientdb, name);
  300. break;
  301. case 'a': /* add file */
  302. if(!havedb){
  303. if(!ismatch(name)){
  304. if(!skip)
  305. fprint(2, "stopped updating log apply time because of %s\n", name);
  306. skip = 1;
  307. continue;
  308. }
  309. SET(checkedmatch2);
  310. if(!havelocal)
  311. goto DoCreate;
  312. if((ld.mode&DMDIR) && (rd.mode&DMDIR))
  313. break;
  314. if(samecontents(local, remote) > 0){
  315. chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
  316. goto DoCreateDb;
  317. }
  318. if(resolve1 == 's')
  319. goto DoCreate;
  320. else if(resolve1 == 'c')
  321. goto DoCreateDb;
  322. conflict(name, "locally created; will not overwrite");
  323. skip = 1;
  324. continue;
  325. }
  326. assert(havedb);
  327. if(dbd.mtime >= rd.mtime) /* already created this file; ignore */
  328. break;
  329. if(havelocal){
  330. if((ld.mode&DMDIR) && (rd.mode&DMDIR))
  331. break;
  332. if(!ismatch(name)){
  333. if(!skip)
  334. fprint(2, "stopped updating log apply time because of %s\n", name);
  335. skip = 1;
  336. continue;
  337. }
  338. SET(checkedmatch2);
  339. if(samecontents(local, remote) > 0){
  340. chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
  341. goto DoCreateDb;
  342. }
  343. if(dbd.mtime==ld.mtime && dbd.length==ld.length)
  344. goto DoCreate;
  345. if(resolve1=='s')
  346. goto DoCreate;
  347. else if(resolve1 == 'c')
  348. break;
  349. conflict(name, "locally modified; will not overwrite");
  350. skip = 1;
  351. continue;
  352. }
  353. if(!ismatch(name)){
  354. if(!skip)
  355. fprint(2, "stopped updating log apply time because of %s\n", name);
  356. skip = 1;
  357. continue;
  358. }
  359. SET(checkedmatch2);
  360. DoCreate:
  361. USED(checkedmatch2);
  362. assert(ismatch(name));
  363. if(notexists(remote)){
  364. addce(local);
  365. /* no skip=1 */
  366. break;;
  367. }
  368. chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
  369. if(donothing)
  370. break;
  371. if(rd.mode&DMDIR){
  372. if((fd = create(local, OREAD, DMDIR)) < 0){
  373. error("mkdir %q: %r", name);
  374. skip = 1;
  375. continue;
  376. }
  377. nulldir(&nd);
  378. nd.mode = rd.mode;
  379. if(dirfwstat(fd, &nd) < 0)
  380. fprint(2, "warning: cannot set mode on %q\n", local);
  381. nulldir(&nd);
  382. nd.gid = rd.gid;
  383. if(dirfwstat(fd, &nd) < 0)
  384. fprint(2, "warning: cannot set gid on %q\n", local);
  385. if(douid){
  386. nulldir(&nd);
  387. nd.uid = rd.uid;
  388. if(dirfwstat(fd, &nd) < 0)
  389. fprint(2, "warning: cannot set uid on %q\n", local);
  390. }
  391. close(fd);
  392. rd.mtime = now;
  393. }else{
  394. if(copyfile(local, remote, name, &rd, 1, &k) < 0){
  395. if(k)
  396. addce(local);
  397. skip = 1;
  398. continue;
  399. }
  400. }
  401. DoCreateDb:
  402. USED(checkedmatch2);
  403. assert(ismatch(name));
  404. insertdb(clientdb, name, &rd);
  405. break;
  406. case 'c': /* change contents */
  407. if(!havedb){
  408. if(notexists(remote)){
  409. addce(local);
  410. /* no skip=1 */
  411. break;
  412. }
  413. if(!ismatch(name)){
  414. if(!skip)
  415. fprint(2, "stopped updating log apply time because of %s\n", name);
  416. skip = 1;
  417. continue;
  418. }
  419. SET(checkedmatch3);
  420. if(resolve1 == 's')
  421. goto DoCopy;
  422. else if(resolve1=='c')
  423. goto DoCopyDb;
  424. if(samecontents(local, remote) > 0){
  425. chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
  426. goto DoCopyDb;
  427. }
  428. if(havelocal)
  429. conflict(name, "locally created; will not update");
  430. else
  431. conflict(name, "not replicated; will not update");
  432. skip = 1;
  433. continue;
  434. }
  435. if(dbd.mtime >= rd.mtime) /* already have/had this version; ignore */
  436. break;
  437. if(!ismatch(name)){
  438. if(!skip)
  439. fprint(2, "stopped updating log apply time because of %s\n", name);
  440. skip = 1;
  441. continue;
  442. }
  443. SET(checkedmatch3);
  444. if(!havelocal){
  445. if(notexists(remote)){
  446. addce(local);
  447. /* no skip=1 */
  448. break;
  449. }
  450. if(resolve1 == 's')
  451. goto DoCopy;
  452. else if(resolve1 == 'c')
  453. break;
  454. conflict(name, "locally removed; will not update");
  455. skip = 1;
  456. continue;
  457. }
  458. assert(havedb && havelocal);
  459. if(dbd.mtime != ld.mtime || dbd.length != ld.length){
  460. if(notexists(remote)){
  461. addce(local);
  462. /* no skip=1 */
  463. break;
  464. }
  465. if(samecontents(local, remote) > 0){
  466. chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
  467. goto DoCopyDb;
  468. }
  469. if(resolve1 == 's')
  470. goto DoCopy;
  471. else if(resolve1 == 'c')
  472. break;
  473. conflict(name, "locally modified; will not update [%llud %lud -> %llud %lud]", dbd.length, dbd.mtime, ld.length, ld.mtime);
  474. skip = 1;
  475. continue;
  476. }
  477. DoCopy:
  478. USED(checkedmatch3);
  479. assert(ismatch(name));
  480. if(notexists(remote)){
  481. addce(local);
  482. /* no skip=1 */
  483. break;
  484. }
  485. chat("c %q\n", name);
  486. if(donothing)
  487. break;
  488. if(copyfile(local, remote, name, &rd, 0, &k) < 0){
  489. if(k)
  490. addce(local);
  491. skip = 1;
  492. continue;
  493. }
  494. DoCopyDb:
  495. USED(checkedmatch3);
  496. assert(ismatch(name));
  497. if(!havedb){
  498. if(havelocal)
  499. dbd = ld;
  500. else
  501. dbd = rd;
  502. }
  503. dbd.mtime = rd.mtime;
  504. dbd.length = rd.length;
  505. insertdb(clientdb, name, &dbd);
  506. break;
  507. case 'm': /* change metadata */
  508. if(!havedb){
  509. if(notexists(remote)){
  510. addce(local);
  511. /* no skip=1 */
  512. break;
  513. }
  514. if(!ismatch(name)){
  515. if(!skip)
  516. fprint(2, "stopped updating log apply time because of %s\n", name);
  517. skip = 1;
  518. continue;
  519. }
  520. SET(checkedmatch4);
  521. if(resolve1 == 's'){
  522. USED(checkedmatch4);
  523. SET(checkedmatch2);
  524. goto DoCreate;
  525. }
  526. else if(resolve1 == 'c')
  527. goto DoMetaDb;
  528. if(havelocal)
  529. conflict(name, "locally created; will not update metadata");
  530. else
  531. conflict(name, "not replicated; will not update metadata");
  532. skip = 1;
  533. continue;
  534. }
  535. if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime) /* have newer version; ignore */
  536. break;
  537. if((dbd.mode&DMDIR) && dbd.mtime > now)
  538. break;
  539. if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode)
  540. break;
  541. if(!havelocal){
  542. if(notexists(remote)){
  543. addce(local);
  544. /* no skip=1 */
  545. break;
  546. }
  547. if(!ismatch(name)){
  548. if(!skip)
  549. fprint(2, "stopped updating log apply time because of %s\n", name);
  550. skip = 1;
  551. continue;
  552. }
  553. SET(checkedmatch4);
  554. if(resolve1 == 's'){
  555. USED(checkedmatch4);
  556. SET(checkedmatch2);
  557. goto DoCreate;
  558. }
  559. else if(resolve1 == 'c')
  560. break;
  561. conflict(name, "locally removed; will not update metadata");
  562. skip = 1;
  563. continue;
  564. }
  565. if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){ /* this check might be overkill */
  566. if(notexists(remote)){
  567. addce(local);
  568. /* no skip=1 */
  569. break;
  570. }
  571. if(!ismatch(name)){
  572. if(!skip)
  573. fprint(2, "stopped updating log apply time because of %s\n", name);
  574. skip = 1;
  575. continue;
  576. }
  577. SET(checkedmatch4);
  578. if(resolve1 == 's' || samecontents(local, remote) > 0)
  579. goto DoMeta;
  580. else if(resolve1 == 'c')
  581. break;
  582. conflict(name, "contents locally modified (%s); will not update metadata to %s %s %luo",
  583. dbd.mtime != ld.mtime ? "mtime" :
  584. dbd.length != ld.length ? "length" :
  585. "unknown",
  586. rd.uid, rd.gid, rd.mode);
  587. skip = 1;
  588. continue;
  589. }
  590. if((douid && strcmp(ld.uid, dbd.uid)!=0) || strcmp(ld.gid, dbd.gid)!=0 || ld.mode!=dbd.mode){
  591. if(notexists(remote)){
  592. addce(local);
  593. /* no skip=1 */
  594. break;
  595. }
  596. if(!ismatch(name)){
  597. if(!skip)
  598. fprint(2, "stopped updating log apply time because of %s\n", name);
  599. skip = 1;
  600. continue;
  601. }
  602. SET(checkedmatch4);
  603. if(resolve1 == 's')
  604. goto DoMeta;
  605. else if(resolve1 == 'c')
  606. break;
  607. conflict(name, "metadata locally changed; will not update metadata to %s %s %luo", rd.uid, rd.gid, rd.mode);
  608. skip = 1;
  609. continue;
  610. }
  611. if(!ismatch(name)){
  612. if(!skip)
  613. fprint(2, "stopped updating log apply time because of %s\n", name);
  614. skip = 1;
  615. continue;
  616. }
  617. SET(checkedmatch4);
  618. DoMeta:
  619. USED(checkedmatch4);
  620. assert(ismatch(name));
  621. if(notexists(remote)){
  622. addce(local);
  623. /* no skip=1 */
  624. break;
  625. }
  626. chat("m %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
  627. if(donothing)
  628. break;
  629. nulldir(&nd);
  630. nd.gid = rd.gid;
  631. nd.mode = rd.mode;
  632. if(douid)
  633. nd.uid = rd.uid;
  634. if(dirwstat(local, &nd) < 0){
  635. error("dirwstat %q: %r", name);
  636. skip = 1;
  637. continue;
  638. }
  639. DoMetaDb:
  640. USED(checkedmatch4);
  641. assert(ismatch(name));
  642. if(!havedb){
  643. if(havelocal)
  644. dbd = ld;
  645. else
  646. dbd = rd;
  647. }
  648. if(dbd.mode&DMDIR)
  649. dbd.mtime = now;
  650. dbd.gid = rd.gid;
  651. dbd.mode = rd.mode;
  652. if(douid)
  653. dbd.uid = rd.uid;
  654. insertdb(clientdb, name, &dbd);
  655. break;
  656. }
  657. if(!skip && !donothing){
  658. maxnow = now;
  659. maxn = n;
  660. }
  661. }
  662. w = avlwalk(copyerr->avl);
  663. while(e = (Entry*)avlnext(w))
  664. error("copying %q: %s\n", e->name, e->d.name);
  665. if(timefile)
  666. writetimefile();
  667. if(nconf)
  668. exits("conflicts");
  669. if(errors)
  670. exits("errors");
  671. exits(nil);
  672. }
  673. char*
  674. mkname(char *buf, int nbuf, char *a, char *b)
  675. {
  676. if(strlen(a)+strlen(b)+2 > nbuf)
  677. sysfatal("name too long");
  678. strcpy(buf, a);
  679. if(a[strlen(a)-1] != '/')
  680. strcat(buf, "/");
  681. strcat(buf, b);
  682. return buf;
  683. }
  684. int
  685. isdir(char *s)
  686. {
  687. ulong m;
  688. Dir *d;
  689. if((d = dirstat(s)) == nil)
  690. return 0;
  691. m = d->mode;
  692. free(d);
  693. return (m&DMDIR) != 0;
  694. }
  695. void
  696. conflict(char *name, char *f, ...)
  697. {
  698. char *s;
  699. va_list arg;
  700. va_start(arg, f);
  701. s = vsmprint(f, arg);
  702. va_end(arg);
  703. fprint(2, "! %s: %s\n", name, s);
  704. free(s);
  705. nconf++;
  706. }
  707. void
  708. error(char *f, ...)
  709. {
  710. char *s;
  711. va_list arg;
  712. va_start(arg, f);
  713. s = vsmprint(f, arg);
  714. va_end(arg);
  715. fprint(2, "error: %s\n", s);
  716. free(s);
  717. errors = 1;
  718. }
  719. int
  720. ismatch(char *s)
  721. {
  722. int i, len;
  723. if(nmatch == 0)
  724. return 1;
  725. for(i=0; i<nmatch; i++){
  726. len = strlen(match[i]);
  727. if(len == 0)
  728. return 1;
  729. if(strncmp(s, match[i], len) == 0 && (s[len]=='/' || s[len] == 0))
  730. return 1;
  731. }
  732. return 0;
  733. }
  734. int
  735. localdirstat(char *name, Dir *d)
  736. {
  737. static Dir *d2;
  738. free(d2);
  739. if((d2 = dirstat(name)) == nil)
  740. return -1;
  741. *d = *d2;
  742. return 0;
  743. }
  744. enum { DEFB = 8192 };
  745. static int
  746. cmp1(int fd1, int fd2)
  747. {
  748. char buf1[DEFB];
  749. char buf2[DEFB];
  750. int n1, n2;
  751. for(;;){
  752. n1 = readn(fd1, buf1, DEFB);
  753. n2 = readn(fd2, buf2, DEFB);
  754. if(n1 < 0 || n2 < 0)
  755. return -1;
  756. if(n1 != n2)
  757. return 0;
  758. if(n1 == 0)
  759. return 1;
  760. if(memcmp(buf1, buf2, n1) != 0)
  761. return 0;
  762. }
  763. }
  764. static int
  765. copy1(int fdf, int fdt, char *from, char *to)
  766. {
  767. int i, n, rv, pid[Nwork];
  768. Waitmsg *w;
  769. n = 0;
  770. off = 0;
  771. for(i=0; i<Nwork; i++){
  772. switch(pid[n] = rfork(RFPROC|RFMEM)){
  773. case 0:
  774. notify(failure);
  775. worker(fdf, fdt, from, to);
  776. case -1:
  777. break;
  778. default:
  779. n++;
  780. break;
  781. }
  782. }
  783. if(n == 0){
  784. fprint(2, "cp: rfork: %r\n");
  785. return -1;
  786. }
  787. rv = 0;
  788. while((w = wait()) != nil){
  789. if(w->msg[0]){
  790. rv = -1;
  791. for(i=0; i<n; i++)
  792. if(pid[i] > 0)
  793. postnote(PNPROC, pid[i], "failure");
  794. }
  795. free(w);
  796. }
  797. return rv;
  798. }
  799. void
  800. worker(int fdf, int fdt, char *from, char *to)
  801. {
  802. char buf[DEFB], *bp;
  803. long len, n;
  804. vlong o;
  805. len = sizeof(buf);
  806. bp = buf;
  807. o = nextoff();
  808. while(n = pread(fdf, bp, len, o)){
  809. if(n < 0){
  810. fprint(2, "reading %s: %r\n", from);
  811. _exits("bad");
  812. }
  813. if(pwrite(fdt, buf, n, o) != n){
  814. fprint(2, "writing %s: %r\n", to);
  815. _exits("bad");
  816. }
  817. bp += n;
  818. o += n;
  819. len -= n;
  820. if(len == 0){
  821. len = sizeof buf;
  822. bp = buf;
  823. o = nextoff();
  824. }
  825. }
  826. _exits(nil);
  827. }
  828. vlong
  829. nextoff(void)
  830. {
  831. vlong o;
  832. qlock(&lk);
  833. o = off;
  834. off += DEFB;
  835. qunlock(&lk);
  836. return o;
  837. }
  838. void
  839. failure(void*, char *note)
  840. {
  841. if(strcmp(note, "failure") == 0)
  842. _exits(nil);
  843. noted(NDFLT);
  844. }
  845. static int
  846. opentemp(char *template)
  847. {
  848. int fd, i;
  849. char *p;
  850. p = estrdup(template);
  851. fd = -1;
  852. for(i=0; i<10; i++){
  853. mktemp(p);
  854. if((fd=create(p, ORDWR|OEXCL|ORCLOSE, 0000)) >= 0)
  855. break;
  856. strcpy(p, template);
  857. }
  858. if(fd < 0)
  859. return -1;
  860. strcpy(template, p);
  861. free(p);
  862. return fd;
  863. }
  864. int
  865. copyfile(char *local, char *remote, char *name, Dir *d, int dowstat, int *printerror)
  866. {
  867. Dir *d0, *d1, *dl;
  868. Dir nd;
  869. int rfd, tfd, wfd, didcreate;
  870. char tmp[32], *p, *safe;
  871. char err[ERRMAX];
  872. Again:
  873. *printerror = 0;
  874. if((rfd = open(remote, OREAD)) < 0)
  875. return -1;
  876. d0 = dirfstat(rfd);
  877. if(d0 == nil){
  878. close(rfd);
  879. return -1;
  880. }
  881. *printerror = 1;
  882. if(!tempspool){
  883. tfd = rfd;
  884. goto DoCopy;
  885. }
  886. strcpy(tmp, "/tmp/replicaXXXXXXXX");
  887. tfd = opentemp(tmp);
  888. if(tfd < 0){
  889. close(rfd);
  890. free(d0);
  891. return -1;
  892. }
  893. if(copy1(rfd, tfd, remote, tmp) < 0 || (d1 = dirfstat(rfd)) == nil){
  894. close(rfd);
  895. close(tfd);
  896. free(d0);
  897. return -1;
  898. }
  899. close(rfd);
  900. if(d0->qid.path != d1->qid.path
  901. || d0->qid.vers != d1->qid.vers
  902. || d0->mtime != d1->mtime
  903. || d0->length != d1->length){
  904. /* file changed underfoot; go around again */
  905. close(tfd);
  906. free(d0);
  907. free(d1);
  908. goto Again;
  909. }
  910. free(d1);
  911. if(seek(tfd, 0, 0) != 0){
  912. close(tfd);
  913. free(d0);
  914. return -1;
  915. }
  916. DoCopy:
  917. /*
  918. * clumsy but important hack to do safeinstall-like installs.
  919. */
  920. p = strchr(name, '/');
  921. if(safeinstall && p && strncmp(p, "/bin/", 5) == 0 && access(local, AEXIST) >= 0){
  922. /*
  923. * remove bin/_targ
  924. */
  925. safe = emalloc(strlen(local)+2);
  926. strcpy(safe, local);
  927. p = strrchr(safe, '/')+1;
  928. memmove(p+1, p, strlen(p)+1);
  929. p[0] = '_';
  930. remove(safe); /* ignore failure */
  931. /*
  932. * rename bin/targ to bin/_targ
  933. */
  934. nulldir(&nd);
  935. nd.name = p;
  936. if(dirwstat(local, &nd) < 0)
  937. fprint(2, "warning: rename %s to %s: %r\n", local, p);
  938. }
  939. didcreate = 0;
  940. if((dl = dirstat(local)) == nil){
  941. if((wfd = create(local, OWRITE, 0)) >= 0){
  942. didcreate = 1;
  943. goto okay;
  944. }
  945. goto err;
  946. }else{
  947. if((wfd = open(local, OTRUNC|OWRITE)) >= 0)
  948. goto okay;
  949. rerrstr(err, sizeof err);
  950. if(strstr(err, "permission") == nil)
  951. goto err;
  952. nulldir(&nd);
  953. /*
  954. * Assume the person running pull is in the appropriate
  955. * groups. We could set 0666 instead, but I'm worried
  956. * about leaving the file world-readable or world-writable
  957. * when it shouldn't be.
  958. */
  959. nd.mode = dl->mode | 0660;
  960. if(nd.mode == dl->mode)
  961. goto err;
  962. if(dirwstat(local, &nd) < 0)
  963. goto err;
  964. if((wfd = open(local, OTRUNC|OWRITE)) >= 0){
  965. nd.mode = dl->mode;
  966. if(dirfwstat(wfd, &nd) < 0)
  967. fprint(2, "warning: set mode on %s to 0660 to open; cannot set back to %luo: %r\n", local, nd.mode);
  968. goto okay;
  969. }
  970. nd.mode = dl->mode;
  971. if(dirwstat(local, &nd) < 0)
  972. fprint(2, "warning: set mode on %s to %luo to open; open failed; cannot set mode back to %luo: %r\n", local, nd.mode|0660, nd.mode);
  973. goto err;
  974. }
  975. err:
  976. close(tfd);
  977. free(d0);
  978. free(dl);
  979. return -1;
  980. okay:
  981. free(dl);
  982. if(copy1(tfd, wfd, tmp, local) < 0){
  983. close(tfd);
  984. close(wfd);
  985. free(d0);
  986. return -1;
  987. }
  988. close(tfd);
  989. if(didcreate || dowstat){
  990. nulldir(&nd);
  991. nd.mode = d->mode;
  992. if(dirfwstat(wfd, &nd) < 0)
  993. fprint(2, "warning: cannot set mode on %s\n", local);
  994. nulldir(&nd);
  995. nd.gid = d->gid;
  996. if(dirfwstat(wfd, &nd) < 0)
  997. fprint(2, "warning: cannot set gid on %s\n", local);
  998. if(douid){
  999. nulldir(&nd);
  1000. nd.uid = d->uid;
  1001. if(dirfwstat(wfd, &nd) < 0)
  1002. fprint(2, "warning: cannot set uid on %s\n", local);
  1003. }
  1004. }
  1005. d->mtime = d0->mtime;
  1006. d->length = d0->length;
  1007. nulldir(&nd);
  1008. nd.mtime = d->mtime;
  1009. if(dirfwstat(wfd, &nd) < 0)
  1010. fprint(2, "warning: cannot set mtime on %s\n", local);
  1011. free(d0);
  1012. close(wfd);
  1013. return 0;
  1014. }
  1015. int
  1016. samecontents(char *local, char *remote)
  1017. {
  1018. Dir *d0, *d1;
  1019. int rfd, tfd, lfd, ret;
  1020. char tmp[32];
  1021. /* quick check: sizes must match */
  1022. d1 = nil;
  1023. if((d0 = dirstat(local)) == nil || (d1 = dirstat(remote)) == nil){
  1024. free(d0);
  1025. free(d1);
  1026. return -1;
  1027. }
  1028. if(d0->length != d1->length){
  1029. free(d0);
  1030. free(d1);
  1031. return 0;
  1032. }
  1033. Again:
  1034. if((rfd = open(remote, OREAD)) < 0)
  1035. return -1;
  1036. d0 = dirfstat(rfd);
  1037. if(d0 == nil){
  1038. close(rfd);
  1039. return -1;
  1040. }
  1041. strcpy(tmp, "/tmp/replicaXXXXXXXX");
  1042. tfd = opentemp(tmp);
  1043. if(tfd < 0){
  1044. close(rfd);
  1045. free(d0);
  1046. return -1;
  1047. }
  1048. if(copy1(rfd, tfd, remote, tmp) < 0 || (d1 = dirfstat(rfd)) == nil){
  1049. close(rfd);
  1050. close(tfd);
  1051. free(d0);
  1052. return -1;
  1053. }
  1054. close(rfd);
  1055. if(d0->qid.path != d1->qid.path
  1056. || d0->qid.vers != d1->qid.vers
  1057. || d0->mtime != d1->mtime
  1058. || d0->length != d1->length){
  1059. /* file changed underfoot; go around again */
  1060. close(tfd);
  1061. free(d0);
  1062. free(d1);
  1063. goto Again;
  1064. }
  1065. free(d1);
  1066. free(d0);
  1067. if(seek(tfd, 0, 0) != 0){
  1068. close(tfd);
  1069. return -1;
  1070. }
  1071. /*
  1072. * now compare
  1073. */
  1074. if((lfd = open(local, OREAD)) < 0){
  1075. close(tfd);
  1076. return -1;
  1077. }
  1078. ret = cmp1(lfd, tfd);
  1079. close(lfd);
  1080. close(tfd);
  1081. return ret;
  1082. }
  1083. /*
  1084. * Applylog might try to overwrite itself.
  1085. * To avoid problems with this, we copy ourselves
  1086. * into /tmp and then re-exec.
  1087. */
  1088. char *rmargv0;
  1089. static void
  1090. rmself(void)
  1091. {
  1092. remove(rmargv0);
  1093. }
  1094. static int
  1095. genopentemp(char *template, int mode, int perm)
  1096. {
  1097. int fd, i;
  1098. char *p;
  1099. p = estrdup(template);
  1100. fd = -1;
  1101. for(i=0; i<10; i++){
  1102. mktemp(p);
  1103. if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0)
  1104. break;
  1105. strcpy(p, template);
  1106. }
  1107. if(fd < 0)
  1108. sysfatal("could not create temporary file");
  1109. strcpy(template, p);
  1110. free(p);
  1111. return fd;
  1112. }
  1113. static void
  1114. membogus(char **argv)
  1115. {
  1116. int n, fd, wfd;
  1117. char template[50], buf[1024];
  1118. if(strncmp(argv[0], "/tmp/_applylog_", 1+3+1+1+8+1)==0) {
  1119. rmargv0 = argv[0];
  1120. atexit(rmself);
  1121. return;
  1122. }
  1123. if((fd = open(argv[0], OREAD)) < 0)
  1124. return;
  1125. strcpy(template, "/tmp/_applylog_XXXXXX");
  1126. if((wfd = genopentemp(template, OWRITE, 0700)) < 0)
  1127. return;
  1128. while((n = read(fd, buf, sizeof buf)) > 0)
  1129. if(write(wfd, buf, n) != n)
  1130. goto Error;
  1131. if(n != 0)
  1132. goto Error;
  1133. close(fd);
  1134. close(wfd);
  1135. argv[0] = template;
  1136. exec(template, argv);
  1137. fprint(2, "exec error %r\n");
  1138. Error:
  1139. close(fd);
  1140. close(wfd);
  1141. remove(template);
  1142. return;
  1143. }