applylog.c 23 KB

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