331 lines
6 KiB
C
331 lines
6 KiB
C
/*
|
|
* push changes from client to server.
|
|
*/
|
|
#include "all.h"
|
|
|
|
int douid;
|
|
Db *db;
|
|
char **x;
|
|
int nx;
|
|
int justshow;
|
|
int verbose;
|
|
int conflicts;
|
|
char newpath[10000];
|
|
char oldpath[10000];
|
|
char *clientroot;
|
|
char *serverroot;
|
|
int copyfile(char*, char*, Dir*, int);
|
|
int metafile(char*, Dir*);
|
|
char **match;
|
|
int nmatch;
|
|
|
|
int
|
|
ismatch(char *s)
|
|
{
|
|
int i, len;
|
|
|
|
if(nmatch == 0)
|
|
return 1;
|
|
for(i=0; i<nmatch; i++){
|
|
if(strcmp(s, match[i]) == 0)
|
|
return 1;
|
|
len = strlen(match[i]);
|
|
if(strncmp(s, match[i], len) == 0 && s[len]=='/')
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
xlog(char c, char *path, Dir *d)
|
|
{
|
|
if(!verbose)
|
|
return;
|
|
print("%c %s %luo %s %s %lud\n", c, path, d->mode, d->uid, d->gid, d->mtime);
|
|
}
|
|
|
|
void
|
|
walk(char *new, char *old, Dir *pd, void*)
|
|
{
|
|
int i, len;
|
|
Dir od, d;
|
|
static Dir *xd;
|
|
|
|
new = unroot(new, "/");
|
|
old = unroot(old, serverroot);
|
|
|
|
if(!ismatch(new))
|
|
return;
|
|
if(xd != nil){
|
|
free(xd);
|
|
xd = nil;
|
|
}
|
|
|
|
for(i=0; i<nx; i++){
|
|
if(strcmp(new, x[i]) == 0)
|
|
return;
|
|
len = strlen(x[i]);
|
|
if(strncmp(new, x[i], len)==0 && new[len]=='/')
|
|
return;
|
|
}
|
|
|
|
d = *pd;
|
|
d.name = old;
|
|
memset(&od, 0, sizeof od);
|
|
snprint(newpath, sizeof newpath, "%s/%s", clientroot, new);
|
|
snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, old);
|
|
|
|
xd = dirstat(oldpath);
|
|
if(markdb(db, new, &od) < 0){
|
|
if(xd != nil){
|
|
print("x %s create/create conflict\n", new);
|
|
conflicts = 1;
|
|
return;
|
|
}
|
|
xlog('a', new, &d);
|
|
d.muid = "mark"; /* mark bit */
|
|
if(!justshow){
|
|
if(copyfile(newpath, oldpath, &d, 1) == 0)
|
|
insertdb(db, new, &d);
|
|
}
|
|
}else{
|
|
if((d.mode&DMDIR)==0 && od.mtime!=d.mtime){
|
|
if(xd==nil){
|
|
print("%s update/remove conflict\n", new);
|
|
conflicts = 1;
|
|
return;
|
|
}
|
|
if(xd->mtime != od.mtime){
|
|
print("%s update/update conflict\n", new);
|
|
conflicts = 1;
|
|
return;
|
|
}
|
|
od.mtime = d.mtime;
|
|
od.muid = "mark";
|
|
xlog('c', new, &od);
|
|
if(!justshow){
|
|
if(copyfile(newpath, oldpath, &od, 0) == 0)
|
|
insertdb(db, new, &od);
|
|
}
|
|
}
|
|
if((douid&&strcmp(od.uid,d.uid)!=0)
|
|
|| strcmp(od.gid,d.gid)!=0
|
|
|| od.mode!=d.mode){
|
|
if(xd==nil){
|
|
print("%s metaupdate/remove conflict\n", new);
|
|
conflicts = 1;
|
|
return;
|
|
}
|
|
if((douid&&strcmp(od.uid,xd->uid)!=0)
|
|
|| strcmp(od.uid,xd->gid)!=0
|
|
|| od.mode!=xd->mode){
|
|
print("%s metaupdate/metaupdate conflict\n", new);
|
|
conflicts = 1;
|
|
return;
|
|
}
|
|
if(douid)
|
|
od.uid = d.uid;
|
|
od.gid = d.gid;
|
|
od.mode = d.mode;
|
|
od.muid = "mark";
|
|
xlog('m', new, &od);
|
|
if(!justshow){
|
|
if(metafile(oldpath, &od) == 0)
|
|
insertdb(db, new, &od);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: replica/applychanges [-nuv] [-p proto] [-x path]... clientdb clientroot serverroot [path ...]\n");
|
|
exits("usage");
|
|
}
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
char *proto;
|
|
Dir *xd, d;
|
|
Entry *e;
|
|
|
|
quotefmtinstall();
|
|
proto = "/sys/lib/sysconfig/proto/allproto";
|
|
ARGBEGIN{
|
|
case 'n':
|
|
justshow = 1;
|
|
verbose = 1;
|
|
break;
|
|
case 'p':
|
|
proto = EARGF(usage());
|
|
break;
|
|
case 'u':
|
|
douid = 1;
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'x':
|
|
if(nx%16 == 0)
|
|
x = erealloc(x, (nx+16)*sizeof(x[0]));
|
|
x[nx++] = EARGF(usage());
|
|
break;
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
if(argc < 3)
|
|
usage();
|
|
|
|
db = opendb(argv[0]);
|
|
clientroot = argv[1];
|
|
serverroot = argv[2];
|
|
match = argv+3;
|
|
nmatch = argc-3;
|
|
|
|
|
|
if(revrdproto(proto, clientroot, serverroot, walk, nil, nil) < 0)
|
|
sysfatal("rdproto: %r");
|
|
|
|
for(e = (Entry*)avlmax(db->avl); e != nil; e = (Entry*)avlprev(e)){
|
|
if(!ismatch(e->name))
|
|
continue;
|
|
if(!e->d.mark){ /* not visited during walk */
|
|
snprint(newpath, sizeof newpath, "%s/%s", clientroot, e->name);
|
|
snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, e->d.name);
|
|
xd = dirstat(oldpath);
|
|
if(xd == nil){
|
|
removedb(db, e->name);
|
|
continue;
|
|
}
|
|
if(xd->mtime != e->d.mtime && (e->d.mode&xd->mode&DMDIR)==0){
|
|
print("x %q remove/update conflict\n", e->name);
|
|
free(xd);
|
|
continue;
|
|
}
|
|
memset(&d, 0, sizeof d);
|
|
d.name = e->d.name;
|
|
d.uid = e->d.uid;
|
|
d.gid = e->d.gid;
|
|
d.mtime = e->d.mtime;
|
|
d.mode = e->d.mode;
|
|
xlog('d', e->name, &d);
|
|
if(!justshow){
|
|
if(remove(oldpath) == 0)
|
|
removedb(db, e->name);
|
|
}
|
|
free(xd);
|
|
}
|
|
}
|
|
|
|
if(conflicts)
|
|
exits("conflicts");
|
|
exits(nil);
|
|
}
|
|
|
|
enum { DEFB = 8192 };
|
|
|
|
static int
|
|
copy1(int fdf, int fdt, char *from, char *to)
|
|
{
|
|
char buf[DEFB];
|
|
long n, n1, rcount;
|
|
int rv;
|
|
char err[ERRMAX];
|
|
|
|
/* clear any residual error */
|
|
err[0] = '\0';
|
|
errstr(err, ERRMAX);
|
|
rv = 0;
|
|
for(rcount=0;; rcount++) {
|
|
n = read(fdf, buf, DEFB);
|
|
if(n <= 0)
|
|
break;
|
|
n1 = write(fdt, buf, n);
|
|
if(n1 != n) {
|
|
fprint(2, "error writing %q: %r\n", to);
|
|
rv = -1;
|
|
break;
|
|
}
|
|
}
|
|
if(n < 0) {
|
|
fprint(2, "error reading %q: %r\n", from);
|
|
rv = -1;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
copyfile(char *from, char *to, Dir *d, int dowstat)
|
|
{
|
|
Dir nd;
|
|
int rfd, wfd, didcreate;
|
|
|
|
if((rfd = open(from, OREAD)) < 0)
|
|
return -1;
|
|
|
|
didcreate = 0;
|
|
if(d->mode&DMDIR){
|
|
if((wfd = create(to, OREAD, DMDIR)) < 0){
|
|
fprint(2, "mkdir %q: %r\n", to);
|
|
close(rfd);
|
|
return -1;
|
|
}
|
|
}else{
|
|
if((wfd = open(to, OTRUNC|OWRITE)) < 0){
|
|
if((wfd = create(to, OWRITE, 0)) < 0){
|
|
close(rfd);
|
|
return -1;
|
|
}
|
|
didcreate = 1;
|
|
}
|
|
if(copy1(rfd, wfd, from, to) < 0){
|
|
close(rfd);
|
|
close(wfd);
|
|
return -1;
|
|
}
|
|
}
|
|
close(rfd);
|
|
if(didcreate || dowstat){
|
|
nulldir(&nd);
|
|
nd.mode = d->mode;
|
|
if(dirfwstat(wfd, &nd) < 0)
|
|
fprint(2, "warning: cannot set mode on %q\n", to);
|
|
nulldir(&nd);
|
|
nd.gid = d->gid;
|
|
if(dirfwstat(wfd, &nd) < 0)
|
|
fprint(2, "warning: cannot set gid on %q\n", to);
|
|
if(douid){
|
|
nulldir(&nd);
|
|
nd.uid = d->uid;
|
|
if(dirfwstat(wfd, &nd) < 0)
|
|
fprint(2, "warning: cannot set uid on %q\n", to);
|
|
}
|
|
}
|
|
nulldir(&nd);
|
|
nd.mtime = d->mtime;
|
|
if(dirfwstat(wfd, &nd) < 0)
|
|
fprint(2, "warning: cannot set mtime on %q\n", to);
|
|
close(wfd);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
metafile(char *path, Dir *d)
|
|
{
|
|
Dir nd;
|
|
|
|
nulldir(&nd);
|
|
nd.gid = d->gid;
|
|
nd.mode = d->mode;
|
|
if(douid)
|
|
nd.uid = d->uid;
|
|
if(dirwstat(path, &nd) < 0){
|
|
fprint(2, "dirwstat %q: %r\n", path);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|