]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/replica/applychanges.c
ip/cifsd: dont return garbage in upper 32 bit of unix extension stat fields
[plan9front.git] / sys / src / cmd / replica / applychanges.c
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         Dir *xd, d;
152         Entry *e;
153
154         quotefmtinstall();
155         proto = "/sys/lib/sysconfig/proto/allproto";
156         ARGBEGIN{
157         case 'n':
158                 justshow = 1;
159                 verbose = 1;
160                 break;
161         case 'p':
162                 proto = EARGF(usage());
163                 break;
164         case 'u':
165                 douid = 1;
166                 break;
167         case 'v':
168                 verbose = 1;
169                 break;
170         case 'x':
171                 if(nx%16 == 0)
172                         x = erealloc(x, (nx+16)*sizeof(x[0]));
173                 x[nx++] = EARGF(usage());
174                 break;
175         default:
176                 usage();
177         }ARGEND
178
179         if(argc < 3)
180                 usage();
181
182         db = opendb(argv[0]);
183         clientroot = argv[1];
184         serverroot = argv[2];
185         match = argv+3;
186         nmatch = argc-3;
187
188
189         if(revrdproto(proto, clientroot, serverroot, walk, nil, nil) < 0)
190                 sysfatal("rdproto: %r");
191
192         for(e = (Entry*)avlmax(db->avl); e != nil; e = (Entry*)avlprev(e)){
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 && (e->d.mode&xd->mode&DMDIR)==0){
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                         free(xd);
220                 }
221         }
222
223         if(conflicts)
224                 exits("conflicts");
225         exits(nil);
226 }
227
228 enum { DEFB = 8192 };
229
230 static int
231 copy1(int fdf, int fdt, char *from, char *to)
232 {
233         char buf[DEFB];
234         long n, n1, rcount;
235         int rv;
236         char err[ERRMAX];
237
238         /* clear any residual error */
239         err[0] = '\0';
240         errstr(err, ERRMAX);
241         rv = 0;
242         for(rcount=0;; rcount++) {
243                 n = read(fdf, buf, DEFB);
244                 if(n <= 0)
245                         break;
246                 n1 = write(fdt, buf, n);
247                 if(n1 != n) {
248                         fprint(2, "error writing %q: %r\n", to);
249                         rv = -1;
250                         break;
251                 }
252         }
253         if(n < 0) {
254                 fprint(2, "error reading %q: %r\n", from);
255                 rv = -1;
256         }
257         return rv;
258 }
259
260 int
261 copyfile(char *from, char *to, Dir *d, int dowstat)
262 {
263         Dir nd;
264         int rfd, wfd, didcreate;
265         
266         if((rfd = open(from, OREAD)) < 0)
267                 return -1;
268
269         didcreate = 0;
270         if(d->mode&DMDIR){
271                 if((wfd = create(to, OREAD, DMDIR)) < 0){
272                         fprint(2, "mkdir %q: %r\n", to);
273                         close(rfd);
274                         return -1;
275                 }
276         }else{
277                 if((wfd = open(to, OTRUNC|OWRITE)) < 0){
278                         if((wfd = create(to, OWRITE, 0)) < 0){
279                                 close(rfd);
280                                 return -1;
281                         }
282                         didcreate = 1;
283                 }
284                 if(copy1(rfd, wfd, from, to) < 0){
285                         close(rfd);
286                         close(wfd);
287                         return -1;
288                 }
289         }
290         close(rfd);
291         if(didcreate || dowstat){
292                 nulldir(&nd);
293                 nd.mode = d->mode;
294                 if(dirfwstat(wfd, &nd) < 0)
295                         fprint(2, "warning: cannot set mode on %q\n", to);
296                 nulldir(&nd);
297                 nd.gid = d->gid;
298                 if(dirfwstat(wfd, &nd) < 0)
299                         fprint(2, "warning: cannot set gid on %q\n", to);
300                 if(douid){
301                         nulldir(&nd);
302                         nd.uid = d->uid;
303                         if(dirfwstat(wfd, &nd) < 0)
304                                 fprint(2, "warning: cannot set uid on %q\n", to);
305                 }
306         }
307         nulldir(&nd);
308         nd.mtime = d->mtime;
309         if(dirfwstat(wfd, &nd) < 0)
310                 fprint(2, "warning: cannot set mtime on %q\n", to);
311         close(wfd);
312         return 0;
313 }
314
315 int
316 metafile(char *path, Dir *d)
317 {
318         Dir nd;
319
320         nulldir(&nd);
321         nd.gid = d->gid;
322         nd.mode = d->mode;
323         if(douid)
324                 nd.uid = d->uid;
325         if(dirwstat(path, &nd) < 0){
326                 fprint(2, "dirwstat %q: %r\n", path);
327                 return -1;
328         }
329         return 0;
330 }