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 od.muid = "mark"; 105 xlog('c', new, &od); 106 if(!justshow){ 107 if(copyfile(newpath, oldpath, &od, 0) == 0) 108 insertdb(db, new, &od); 109 } 110 } 111 if((douid&&strcmp(od.uid,d.uid)!=0) 112 || strcmp(od.gid,d.gid)!=0 113 || od.mode!=d.mode){ 114 if(xd==nil){ 115 print("%s metaupdate/remove conflict\n", new); 116 conflicts = 1; 117 return; 118 } 119 if((douid&&strcmp(od.uid,xd->uid)!=0) 120 || strcmp(od.uid,xd->gid)!=0 121 || od.mode!=xd->mode){ 122 print("%s metaupdate/metaupdate conflict\n", new); 123 conflicts = 1; 124 return; 125 } 126 if(douid) 127 od.uid = d.uid; 128 od.gid = d.gid; 129 od.mode = d.mode; 130 od.muid = "mark"; 131 xlog('m', new, &od); 132 if(!justshow){ 133 if(metafile(oldpath, &od) == 0) 134 insertdb(db, new, &od); 135 } 136 } 137 } 138 } 139 140 void 141 usage(void) 142 { 143 fprint(2, "usage: replica/applychanges [-nuv] [-p proto] [-x path]... clientdb clientroot serverroot [path ...]\n"); 144 exits("usage"); 145 } 146 147 void 148 main(int argc, char **argv) 149 { 150 char *proto; 151 Avlwalk *w; 152 Dir *xd, d; 153 Entry *e; 154 155 quotefmtinstall(); 156 proto = "/sys/lib/sysconfig/proto/allproto"; 157 ARGBEGIN{ 158 case 'n': 159 justshow = 1; 160 verbose = 1; 161 break; 162 case 'p': 163 proto = EARGF(usage()); 164 break; 165 case 'u': 166 douid = 1; 167 break; 168 case 'v': 169 verbose = 1; 170 break; 171 case 'x': 172 if(nx%16 == 0) 173 x = erealloc(x, (nx+16)*sizeof(x[0])); 174 x[nx++] = EARGF(usage()); 175 break; 176 default: 177 usage(); 178 }ARGEND 179 180 if(argc < 3) 181 usage(); 182 183 db = opendb(argv[0]); 184 clientroot = argv[1]; 185 serverroot = argv[2]; 186 match = argv+3; 187 nmatch = argc-3; 188 189 190 if(revrdproto(proto, clientroot, serverroot, walk, nil, nil) < 0) 191 sysfatal("rdproto: %r"); 192 193 w = avlwalk(db->avl); 194 while(e = (Entry*)avlprev(w)){ 195 if(!ismatch(e->name)) 196 continue; 197 if(!e->d.mark){ /* not visited during walk */ 198 snprint(newpath, sizeof newpath, "%s/%s", clientroot, e->name); 199 snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, e->d.name); 200 xd = dirstat(oldpath); 201 if(xd == nil){ 202 removedb(db, e->name); 203 continue; 204 } 205 if(xd->mtime != e->d.mtime && (e->d.mode&xd->mode&DMDIR)==0){ 206 print("x %q remove/update conflict\n", e->name); 207 free(xd); 208 continue; 209 } 210 memset(&d, 0, sizeof d); 211 d.name = e->d.name; 212 d.uid = e->d.uid; 213 d.gid = e->d.gid; 214 d.mtime = e->d.mtime; 215 d.mode = e->d.mode; 216 xlog('d', e->name, &d); 217 if(!justshow){ 218 if(remove(oldpath) == 0) 219 removedb(db, e->name); 220 } 221 free(xd); 222 } 223 } 224 225 if(conflicts) 226 exits("conflicts"); 227 exits(nil); 228 } 229 230 enum { DEFB = 8192 }; 231 232 static int 233 copy1(int fdf, int fdt, char *from, char *to) 234 { 235 char buf[DEFB]; 236 long n, n1, rcount; 237 int rv; 238 char err[ERRMAX]; 239 240 /* clear any residual error */ 241 err[0] = '\0'; 242 errstr(err, ERRMAX); 243 rv = 0; 244 for(rcount=0;; rcount++) { 245 n = read(fdf, buf, DEFB); 246 if(n <= 0) 247 break; 248 n1 = write(fdt, buf, n); 249 if(n1 != n) { 250 fprint(2, "error writing %q: %r\n", to); 251 rv = -1; 252 break; 253 } 254 } 255 if(n < 0) { 256 fprint(2, "error reading %q: %r\n", from); 257 rv = -1; 258 } 259 return rv; 260 } 261 262 int 263 copyfile(char *from, char *to, Dir *d, int dowstat) 264 { 265 Dir nd; 266 int rfd, wfd, didcreate; 267 268 if((rfd = open(from, OREAD)) < 0) 269 return -1; 270 271 didcreate = 0; 272 if(d->mode&DMDIR){ 273 if((wfd = create(to, OREAD, DMDIR)) < 0){ 274 fprint(2, "mkdir %q: %r\n", to); 275 close(rfd); 276 return -1; 277 } 278 }else{ 279 if((wfd = open(to, OTRUNC|OWRITE)) < 0){ 280 if((wfd = create(to, OWRITE, 0)) < 0){ 281 close(rfd); 282 return -1; 283 } 284 didcreate = 1; 285 } 286 if(copy1(rfd, wfd, from, to) < 0){ 287 close(rfd); 288 close(wfd); 289 return -1; 290 } 291 } 292 close(rfd); 293 if(didcreate || dowstat){ 294 nulldir(&nd); 295 nd.mode = d->mode; 296 if(dirfwstat(wfd, &nd) < 0) 297 fprint(2, "warning: cannot set mode on %q\n", to); 298 nulldir(&nd); 299 nd.gid = d->gid; 300 if(dirfwstat(wfd, &nd) < 0) 301 fprint(2, "warning: cannot set gid on %q\n", to); 302 if(douid){ 303 nulldir(&nd); 304 nd.uid = d->uid; 305 if(dirfwstat(wfd, &nd) < 0) 306 fprint(2, "warning: cannot set uid on %q\n", to); 307 } 308 } 309 nulldir(&nd); 310 nd.mtime = d->mtime; 311 if(dirfwstat(wfd, &nd) < 0) 312 fprint(2, "warning: cannot set mtime on %q\n", to); 313 close(wfd); 314 return 0; 315 } 316 317 int 318 metafile(char *path, Dir *d) 319 { 320 Dir nd; 321 322 nulldir(&nd); 323 nd.gid = d->gid; 324 nd.mode = d->mode; 325 if(douid) 326 nd.uid = d->uid; 327 if(dirwstat(path, &nd) < 0){ 328 fprint(2, "dirwstat %q: %r\n", path); 329 return -1; 330 } 331 return 0; 332 } 333