1 /* 2 * push changes from client to server. 3 */ 4 #include "all.h" 5 6 int douid; 7 Db *db; 8 char **x; 9 int nx; 10 int justshow; 11 int verbose; 12 int conflicts; 13 char newpath[10000]; 14 char oldpath[10000]; 15 char *clientroot; 16 char *serverroot; 17 int copyfile(char*, char*, Dir*, int); 18 int metafile(char*, Dir*); 19 char **match; 20 int nmatch; 21 22 int 23 ismatch(char *s) 24 { 25 int i, len; 26 27 if(nmatch == 0) 28 return 1; 29 for(i=0; i<nmatch; i++){ 30 if(strcmp(s, match[i]) == 0) 31 return 1; 32 len = strlen(match[i]); 33 if(strncmp(s, match[i], len) == 0 && s[len]=='/') 34 return 1; 35 } 36 return 0; 37 } 38 39 void 40 xlog(char c, char *path, Dir *d) 41 { 42 if(!verbose) 43 return; 44 print("%c %s %luo %s %s %lud\n", c, path, d->mode, d->uid, d->gid, d->mtime); 45 } 46 47 void 48 walk(char *new, char *old, Dir *pd, void*) 49 { 50 int i, len; 51 Dir od, d; 52 static Dir *xd; 53 54 new = unroot(new, "/"); 55 old = unroot(old, serverroot); 56 57 if(!ismatch(new)) 58 return; 59 if(xd != nil){ 60 free(xd); 61 xd = nil; 62 } 63 64 for(i=0; i<nx; i++){ 65 if(strcmp(new, x[i]) == 0) 66 return; 67 len = strlen(x[i]); 68 if(strncmp(new, x[i], len)==0 && new[len]=='/') 69 return; 70 } 71 72 d = *pd; 73 d.name = old; 74 memset(&od, 0, sizeof od); 75 snprint(newpath, sizeof newpath, "%s/%s", clientroot, new); 76 snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, old); 77 78 xd = dirstat(oldpath); 79 if(markdb(db, new, &od) < 0){ 80 if(xd != nil){ 81 print("x %s create/create conflict\n", new); 82 conflicts = 1; 83 return; 84 } 85 xlog('a', new, &d); 86 d.muid = "mark"; /* mark bit */ 87 if(!justshow){ 88 if(copyfile(newpath, oldpath, &d, 1) == 0) 89 insertdb(db, new, &d); 90 } 91 }else{ 92 if((d.mode&DMDIR)==0 && od.mtime!=d.mtime){ 93 if(xd==nil){ 94 print("%s update/remove conflict\n", new); 95 conflicts = 1; 96 return; 97 } 98 if(xd->mtime != od.mtime){ 99 print("%s update/update conflict\n", new); 100 conflicts = 1; 101 return; 102 } 103 od.mtime = d.mtime; 104 xlog('c', new, &od); 105 if(!justshow){ 106 if(copyfile(new, old, &od, 0) == 0) 107 insertdb(db, new, &od); 108 } 109 } 110 if((douid&&strcmp(od.uid,d.uid)!=0) 111 || strcmp(od.gid,d.gid)!=0 112 || od.mode!=d.mode){ 113 if(xd==nil){ 114 print("%s metaupdate/remove conflict\n", new); 115 conflicts = 1; 116 return; 117 } 118 if((douid&&strcmp(od.uid,xd->uid)!=0) 119 || strcmp(od.uid,xd->gid)!=0 120 || od.mode!=xd->mode){ 121 print("%s metaupdate/metaupdate conflict\n", new); 122 conflicts = 1; 123 return; 124 } 125 if(douid) 126 od.uid = d.uid; 127 od.gid = d.gid; 128 od.mode = d.mode; 129 xlog('m', new, &od); 130 if(!justshow){ 131 if(metafile(oldpath, &od) == 0) 132 insertdb(db, new, &od); 133 } 134 } 135 } 136 } 137 138 void 139 usage(void) 140 { 141 fprint(2, "usage: replica/applychanges [-p proto] [-r root] [-t now n] [-u uid] [-x path]... clientdb [path ...]\n"); 142 exits("usage"); 143 } 144 145 void 146 main(int argc, char **argv) 147 { 148 char *proto; 149 Avlwalk *w; 150 Dir *xd, d; 151 Entry *e; 152 153 quotefmtinstall(); 154 proto = "/sys/lib/sysconfig/proto/allproto"; 155 ARGBEGIN{ 156 case 'n': 157 justshow = 1; 158 verbose = 1; 159 break; 160 case 'p': 161 proto = EARGF(usage()); 162 break; 163 case 'u': 164 douid = 1; 165 break; 166 case 'v': 167 verbose = 1; 168 break; 169 case 'x': 170 if(nx%16 == 0) 171 x = erealloc(x, (nx+16)*sizeof(x[0])); 172 x[nx++] = EARGF(usage()); 173 break; 174 default: 175 usage(); 176 }ARGEND 177 178 if(argc < 3) 179 usage(); 180 181 db = opendb(argv[0]); 182 clientroot = argv[1]; 183 serverroot = argv[2]; 184 match = argv+3; 185 nmatch = argc-3; 186 187 188 if(revrdproto(proto, clientroot, serverroot, walk, nil, nil) < 0) 189 sysfatal("rdproto: %r"); 190 191 w = avlwalk(db->avl); 192 while(e = (Entry*)avlnext(w)){ 193 if(!ismatch(e->name)) 194 continue; 195 if(!e->d.mark){ /* not visited during walk */ 196 snprint(newpath, sizeof newpath, "%s/%s", clientroot, e->name); 197 snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, e->d.name); 198 xd = dirstat(oldpath); 199 if(xd == nil){ 200 removedb(db, e->name); 201 continue; 202 } 203 if(xd->mtime != e->d.mtime){ 204 print("x %q remove/update conflict\n", e->name); 205 free(xd); 206 continue; 207 } 208 memset(&d, 0, sizeof d); 209 d.name = e->d.name; 210 d.uid = e->d.uid; 211 d.gid = e->d.gid; 212 d.mtime = e->d.mtime; 213 d.mode = e->d.mode; 214 xlog('d', e->name, &d); 215 if(!justshow){ 216 if(remove(oldpath) == 0) 217 removedb(db, e->name); 218 } 219 } 220 } 221 222 if(conflicts) 223 exits("conflicts"); 224 exits(nil); 225 } 226 227 enum { DEFB = 8192 }; 228 229 static int 230 copy1(int fdf, int fdt, char *from, char *to) 231 { 232 char buf[DEFB]; 233 long n, n1, rcount; 234 int rv; 235 char err[ERRMAX]; 236 237 /* clear any residual error */ 238 err[0] = '\0'; 239 errstr(err, ERRMAX); 240 rv = 0; 241 for(rcount=0;; rcount++) { 242 n = read(fdf, buf, DEFB); 243 if(n <= 0) 244 break; 245 n1 = write(fdt, buf, n); 246 if(n1 != n) { 247 fprint(2, "error writing %q: %r\n", to); 248 rv = -1; 249 break; 250 } 251 } 252 if(n < 0) { 253 fprint(2, "error reading %q: %r\n", from); 254 rv = -1; 255 } 256 return rv; 257 } 258 259 int 260 copyfile(char *from, char *to, Dir *d, int dowstat) 261 { 262 Dir nd; 263 int rfd, wfd, didcreate; 264 265 if((rfd = open(from, OREAD)) < 0) 266 return -1; 267 268 didcreate = 0; 269 if((wfd = open(to, OTRUNC|OWRITE)) < 0){ 270 if((wfd = create(to, OWRITE, 0)) < 0){ 271 close(rfd); 272 return -1; 273 } 274 didcreate = 1; 275 } 276 if(copy1(rfd, wfd, from, to) < 0){ 277 close(rfd); 278 close(wfd); 279 return -1; 280 } 281 if(didcreate || dowstat){ 282 nulldir(&nd); 283 nd.mode = d->mode; 284 if(dirfwstat(wfd, &nd) < 0) 285 fprint(2, "warning: cannot set mode on %q\n", to); 286 nulldir(&nd); 287 nd.gid = d->gid; 288 if(dirfwstat(wfd, &nd) < 0) 289 fprint(2, "warning: cannot set gid on %q\n", to); 290 if(douid){ 291 nulldir(&nd); 292 nd.uid = d->uid; 293 if(dirfwstat(wfd, &nd) < 0) 294 fprint(2, "warning: cannot set uid on %q\n", to); 295 } 296 } 297 nulldir(&nd); 298 nd.mtime = d->mtime; 299 if(dirfwstat(wfd, &nd) < 0) 300 fprint(2, "warning: cannot set mtime on %q\n", to); 301 close(wfd); 302 return 0; 303 } 304 305 int 306 metafile(char *path, Dir *d) 307 { 308 Dir nd; 309 310 nulldir(&nd); 311 nd.gid = d->gid; 312 nd.mode = d->mode; 313 if(douid) 314 nd.uid = d->uid; 315 if(dirwstat(path, &nd) < 0){ 316 fprint(2, "dirwstat %q: %r\n", path); 317 return -1; 318 } 319 return 0; 320 } 321