📄 applylog.c
字号:
#include "all.h"#define Nwork 16int localdirstat(char*, Dir*);int ismatch(char*);void conflict(char*, char*, ...);void error(char*, ...);int isdir(char*);void worker(int fdf, int fdt, char *from, char *to);vlong nextoff(void);void failure(void *, char *note);QLock lk;vlong off;int errors;int nconf;int donothing;char **conf;int verbose;char **match;int nmatch;int tempspool = 1;int safeinstall = 1;char *lroot;char *rroot;Db *clientdb;int skip;int douid;char *mkname(char*, int, char*, char*);char localbuf[10240];char remotebuf[10240];int copyfile(char*, char*, char*, Dir*, int, int*);ulong maxnow;int maxn;char *timefile;int timefd;Db *copyerr;typedef struct Res Res;struct Res{ char c; char *name;};Res *res;int nres;void addresolve(int c, char *name){ if(name[0] == '/') name++; res = erealloc(res, (nres+1)*sizeof res[0]); res[nres].c = c; res[nres].name = name; nres++;}intresolve(char *name){ int i, len; for(i=0; i<nres; i++){ len = strlen(res[i].name); if(len == 0) return res[i].c; if(strncmp(name, res[i].name, len) == 0 && (name[len]=='/' || name[len] == 0)) return res[i].c; } return '?';}voidreadtimefile(void){ int n; char buf[24]; if((timefd = open(timefile, ORDWR)) < 0 && (timefd = create(timefile, ORDWR|OEXCL, 0666)) < 0) return; n = readn(timefd, buf, sizeof buf); if(n < sizeof buf) return; maxnow = atoi(buf); maxn = atoi(buf+12);}voidwritetimefile(void){ char buf[24+1]; snprint(buf, sizeof buf, "%11lud %11d ", maxnow, maxn); pwrite(timefd, buf, 24, 0);}static void membogus(char**);voidaddce(char *local){ char e[ERRMAX]; Dir d; memset(&d, 0, sizeof d); rerrstr(e, sizeof e); d.name = atom(e); d.uid = ""; d.gid = ""; insertdb(copyerr, atom(local), &d);}voiddelce(char *local){ removedb(copyerr, local);}voidchat(char *f, ...){ Fmt fmt; char buf[256]; va_list arg; if(!verbose) return; fmtfdinit(&fmt, 1, buf, sizeof buf); va_start(arg, f); fmtvprint(&fmt, f, arg); va_end(arg); fmtfdflush(&fmt);}voidusage(void){ fprint(2, "usage: replica/applylog [-cnSstuv] [-T timefile] clientdb clientroot serverroot [path ...]\n"); exits("usage");}intnotexists(char *path){ char buf[ERRMAX]; if(access(path, AEXIST) >= 0) return 0; rerrstr(buf, sizeof buf); if(strstr(buf, "entry not found") || strstr(buf, "not exist")) return 1; /* some other error, like network hangup */ return 0;}voidmain(int argc, char **argv){ char *f[10], *local, *name, *remote, *s, *t, verb; int fd, havedb, havelocal, i, k, n, nf, resolve1, skip; int checkedmatch1, checkedmatch2, checkedmatch3, checkedmatch4; ulong now; Biobuf bin; Dir dbd, ld, nd, rd; Avlwalk *w; Entry *e; membogus(argv); quotefmtinstall(); ARGBEGIN{ case 's': case 'c': i = ARGC(); addresolve(i, EARGF(usage())); break; case 'n': donothing = 1; verbose = 1; break; case 'S': safeinstall = 0; break; case 'T': timefile = EARGF(usage()); break; case 't': tempspool = 0; break; case 'u': douid = 1; break; case 'v': verbose++; break; default: usage(); }ARGEND if(argc < 3) usage(); if(timefile) readtimefile(); lroot = argv[1]; if(!isdir(lroot)) sysfatal("bad local root directory"); rroot = argv[2]; if(!isdir(rroot)) sysfatal("bad remote root directory"); match = argv+3; nmatch = argc-3; for(i=0; i<nmatch; i++) if(match[i][0] == '/') match[i]++; if((clientdb = opendb(argv[0])) == nil) sysfatal("opendb %q: %r", argv[2]); copyerr = opendb(nil); skip = 0; Binit(&bin, 0, OREAD); for(; s=Brdstr(&bin, '\n', 1); free(s)){ t = estrdup(s); nf = tokenize(s, f, nelem(f)); if(nf != 10 || strlen(f[2]) != 1){ skip = 1; fprint(2, "warning: skipping bad log entry <%s>\n", t); free(t); continue; } free(t); now = strtoul(f[0], 0, 0); n = atoi(f[1]); verb = f[2][0]; name = f[3]; if(now < maxnow || (now==maxnow && n <= maxn)) continue; local = mkname(localbuf, sizeof localbuf, lroot, name); if(strcmp(f[4], "-") == 0) f[4] = f[3]; remote = mkname(remotebuf, sizeof remotebuf, rroot, f[4]); rd.name = f[4]; rd.mode = strtoul(f[5], 0, 8); rd.uid = f[6]; rd.gid = f[7]; rd.mtime = strtoul(f[8], 0, 10); rd.length = strtoll(f[9], 0, 10); havedb = finddb(clientdb, name, &dbd)>=0; havelocal = localdirstat(local, &ld)>=0; resolve1 = resolve(name); /* * if(!ismatch(name)){ * skip = 1; * continue; * } * * This check used to be right here, but we want * the time to be able to move forward past entries * that don't match and have already been applied. * So now every path below must checked !ismatch(name) * before making any changes to the local file * system. The fake variable checkedmatch * tracks whether !ismatch(name) has been checked. * If the compiler doesn't produce any used/set * warnings, then all the paths should be okay. * Even so, we have the asserts to fall back on. */ switch(verb){ case 'd': /* delete file */ delce(local); if(!havelocal) /* doesn't exist; who cares? */ break; if(!ismatch(name)){ if(!skip) fprint(2, "stopped updating log apply time because of %s\n", name); skip = 1; continue; } SET(checkedmatch1); if(!havedb){ if(resolve1 == 's') goto DoRemove; else if(resolve1 == 'c') goto DoRemoveDb; conflict(name, "locally created; will not remove"); skip = 1; continue; } assert(havelocal && havedb); if(dbd.mtime > rd.mtime) /* we have a newer file than what was deleted */ break; if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){ /* locally modified since we downloaded it */ if(resolve1 == 's') goto DoRemove; else if(resolve1 == 'c') break; conflict(name, "locally modified; will not remove"); skip = 1; continue; } DoRemove: USED(checkedmatch1); assert(ismatch(name)); chat("d %q\n", name); if(donothing) break; if(remove(local) < 0){ error("removing %q", name); skip = 1; continue; } DoRemoveDb: USED(checkedmatch1); assert(ismatch(name)); removedb(clientdb, name); break; case 'a': /* add file */ if(!havedb){ if(!ismatch(name)){ if(!skip) fprint(2, "stopped updating log apply time because of %s\n", name); skip = 1; continue; } SET(checkedmatch2); if(!havelocal) goto DoCreate; if((ld.mode&DMDIR) && (rd.mode&DMDIR)) break; if(resolve1 == 's') goto DoCreate; else if(resolve1 == 'c') goto DoCreateDb; conflict(name, "locally created; will not overwrite"); skip = 1; continue; } assert(havedb); if(dbd.mtime >= rd.mtime) /* already created this file; ignore */ break; if(havelocal){ if((ld.mode&DMDIR) && (rd.mode&DMDIR)) break; if(!ismatch(name)){ if(!skip) fprint(2, "stopped updating log apply time because of %s\n", name); skip = 1; continue; } SET(checkedmatch2); if(dbd.mtime==ld.mtime && dbd.length==ld.length) goto DoCreate; if(resolve1=='s') goto DoCreate; else if(resolve1 == 'c') break; conflict(name, "locally modified; will not overwrite"); skip = 1; continue; } if(!ismatch(name)){ if(!skip) fprint(2, "stopped updating log apply time because of %s\n", name); skip = 1; continue; } SET(checkedmatch2); DoCreate: USED(checkedmatch2); assert(ismatch(name)); if(notexists(remote)){ addce(local); /* no skip=1 */ break;; } chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime); if(donothing) break; if(rd.mode&DMDIR){ if((fd = create(local, OREAD, DMDIR)) < 0){ error("mkdir %q: %r", name); skip = 1; continue; } nulldir(&nd); nd.mode = rd.mode; if(dirfwstat(fd, &nd) < 0) fprint(2, "warning: cannot set mode on %q\n", local); nulldir(&nd); nd.gid = rd.gid; if(dirfwstat(fd, &nd) < 0) fprint(2, "warning: cannot set gid on %q\n", local); if(douid){ nulldir(&nd); nd.uid = rd.uid; if(dirfwstat(fd, &nd) < 0) fprint(2, "warning: cannot set uid on %q\n", local); } close(fd); rd.mtime = now; }else{ if(copyfile(local, remote, name, &rd, 1, &k) < 0){ if(k) addce(local); skip = 1; continue; } } DoCreateDb: USED(checkedmatch2); assert(ismatch(name)); insertdb(clientdb, name, &rd); break; case 'c': /* change contents */ if(!havedb){ if(notexists(remote)){ addce(local); /* no skip=1 */ break; } if(!ismatch(name)){ if(!skip) fprint(2, "stopped updating log apply time because of %s\n", name); skip = 1; continue; } SET(checkedmatch3); if(resolve1 == 's') goto DoCopy; else if(resolve1=='c') goto DoCopyDb; if(havelocal) conflict(name, "locally created; will not update"); else conflict(name, "not replicated; will not update"); skip = 1; continue; } if(dbd.mtime >= rd.mtime) /* already have/had this version; ignore */ break; if(!ismatch(name)){ if(!skip) fprint(2, "stopped updating log apply time because of %s\n", name); skip = 1; continue; } SET(checkedmatch3); if(!havelocal){ if(notexists(remote)){ addce(local); /* no skip=1 */ break; } if(resolve1 == 's') goto DoCopy; else if(resolve1 == 'c') break; conflict(name, "locally removed; will not update"); skip = 1; continue; } assert(havedb && havelocal); if(dbd.mtime != ld.mtime || dbd.length != ld.length){ if(notexists(remote)){ addce(local); /* no skip=1 */ break; } if(resolve1 == 's') goto DoCopy; else if(resolve1 == 'c') break; conflict(name, "locally modified; will not update"); skip = 1; continue; } DoCopy: USED(checkedmatch3); assert(ismatch(name)); if(notexists(remote)){ addce(local); /* no skip=1 */ break; } chat("c %q\n", name); if(donothing) break; if(copyfile(local, remote, name, &rd, 0, &k) < 0){ if(k) addce(local); skip = 1; continue; } DoCopyDb: USED(checkedmatch3); assert(ismatch(name)); if(!havedb){ if(havelocal) dbd = ld; else dbd = rd; } dbd.mtime = rd.mtime; dbd.length = rd.length; insertdb(clientdb, name, &dbd); break; case 'm': /* change metadata */ if(!havedb){ if(notexists(remote)){ addce(local); /* no skip=1 */ break; } if(!ismatch(name)){ if(!skip) fprint(2, "stopped updating log apply time because of %s\n", name); skip = 1; continue; } SET(checkedmatch4); if(resolve1 == 's'){ USED(checkedmatch4); SET(checkedmatch2); goto DoCreate; } else if(resolve1 == 'c') goto DoMetaDb; if(havelocal) conflict(name, "locally created; will not update metadata"); else conflict(name, "not replicated; will not update metadata"); skip = 1; continue; } if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime) /* have newer version; ignore */ break; if((dbd.mode&DMDIR) && dbd.mtime > now) break; if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode) break; if(!havelocal){ if(notexists(remote)){ addce(local); /* no skip=1 */ break; } if(!ismatch(name)){ if(!skip)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -