11 * types of destination file sytems
18 typedef struct File File;
31 int copyfile(File*, Dir*, int);
33 void error(char *, ...);
36 char* getmode(char*, ulong*);
37 char* getname(char*, char**);
42 void mkfs(File*, int);
43 char* mkpath(char*, char*);
44 void mktree(File*, int);
46 void printfile(File*);
51 int uptodate(Dir*, char*);
53 void warn(char *, ...);
56 Biobufhdr bout; /* stdout when writing archive */
77 int fskind; /* Kfs, Fs, Archive */
78 int setuid; /* on Fs: set uid and gid? */
82 main(int argc, char **argv)
91 memset(&file, 0, sizeof file);
101 fprint(2, "cannot use -a with -d\n");
106 Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf);
110 fprint(2, "cannot use -d with -a\n");
120 name = EARGF(usage());
144 buflen = atoi(ARGF())-8;
153 buf = emalloc(buflen);
154 zbuf = emalloc(buflen);
155 memset(zbuf, 0, buflen);
161 cputype = getenv("cputype");
166 for(i = 0; i < argc; i++){
168 fprint(2, "processing %q\n", proto);
170 b = Bopen(proto, OREAD);
172 fprint(2, "%q: can't open %q: skipping\n", prog, proto);
182 fprint(2, "file system made\n");
186 exits("skipped protos");
187 if(fskind == Archive){
188 Bprint(&bout, "end of archive\n");
195 mkfs(File *me, int level)
203 if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
204 rec = child->elem[0] == '+';
206 child->new = strdup(me->new);
212 while(child && indent > level){
220 Bseek(b, -Blinelen(b), 1);
226 mktree(File *me, int rec)
232 fd = open(oldfile, OREAD);
234 warn("can't open %q: %r", oldfile);
239 while((n = dirread(fd, &d)) > 0){
240 for(i = 0; i < n; i++){
241 child.new = mkpath(me->new, d[i].name);
243 child.old = mkpath(me->old, d[i].name);
244 child.elem = d[i].name;
246 if(copyfile(&child, &d[i], 1) && rec)
261 if((dir = dirstat(oldfile)) == nil){
262 warn("can't stat file %q: %r", oldfile);
266 return copyfile(f, dir, 0);
270 copyfile(File *f, Dir *d, int permonly)
276 Bprint(&bout, "%q\t%ld\t%lld\n", f->new, d->mtime, d->length);
277 return (d->mode & DMDIR) != 0;
279 if(verb && (fskind == Archive || ream))
280 fprint(2, "%q\n", f->new);
285 mode = (d->mode >> 6) & 7;
286 d->mode |= mode | (mode << 3);
288 if(strcmp(f->uid, "-") != 0)
290 if(strcmp(f->gid, "-") != 0)
292 if(fskind == Fs && !setuid){
298 d->mode = (d->mode & ~0666) | (f->mode & 0666);
299 else if((d->mode&DMDIR) != (f->mode&DMDIR))
300 warn("inconsistent mode for %q", f->new);
304 if(!uptodate(d, newfile)){
305 if(verb && (fskind != Archive && ream == 0))
306 fprint(2, "%q\n", f->new);
316 if(verb && (fskind != Archive && ream == 0))
317 fprint(2, "%q\n", f->new);
318 if(dirwstat(newfile, &nd) < 0)
319 warn("can't set modes for %q: %r", f->new);
322 dirwstat(newfile, &nd);
324 return (d->mode & DMDIR) != 0;
328 * check if file to is up to date with
329 * respect to the file represented by df
332 uptodate(Dir *df, char *to)
337 if(fskind == Archive || ream || (dt = dirstat(to)) == nil)
339 ret = dt->mtime >= df->mtime;
348 int f, t, n, needwrite, nowarnyet = 1;
352 f = open(oldfile, OREAD);
354 warn("can't open %q: %r", oldfile);
358 if(fskind == Archive)
361 strcpy(cptmp, newfile);
362 p = utfrrune(cptmp, L'/');
364 error("internal temporary file error");
365 strcpy(p+1, "__mkfstmp");
366 t = create(cptmp, OWRITE, 0666);
368 warn("can't create %q: %r", newfile);
375 for(tot = 0; tot < d->length; tot += n){
376 len = d->length - tot;
377 /* don't read beyond d->length */
380 n = read(f, buf, len);
382 if(n < 0 && nowarnyet) {
383 warn("can't read %q: %r", oldfile);
387 * don't quit: pad to d->length (in pieces) to agree
388 * with the length in the header, already emitted.
393 if(fskind == Archive){
394 if(Bwrite(&bout, buf, n) != n)
395 error("write error: %r");
396 }else if(memcmp(buf, zbuf, n) == 0){
397 if(seek(t, n, 1) < 0)
398 error("can't write zeros to %q: %r", newfile);
401 if(write(t, buf, n) < n)
402 error("can't write %q: %r", newfile);
408 if(seek(t, -1, 1) < 0 || write(t, zbuf, 1) != 1)
409 error("can't write zero at end of %q: %r", newfile);
411 if(tot != d->length){
412 /* this should no longer happen */
413 warn("wrong number of bytes written to %q (was %lld should be %lld)\n",
414 newfile, tot, d->length);
415 if(fskind == Archive){
416 warn("seeking to proper position\n");
417 /* does no good if stdout is a pipe */
418 Bseek(&bout, d->length - tot, 1);
421 if(fskind == Archive)
429 if(dirfwstat(t, &nd) < 0)
430 error("can't move tmp file to %q: %r", newfile);
444 if(fskind == Archive){
448 fd = create(newfile, OREAD, d->mode);
454 if((d1 = dirstat(newfile)) == nil || !(d1->mode & DMDIR)){
456 error("can't create %q", newfile);
459 if(dirwstat(newfile, &nd) < 0)
460 warn("can't set modes for %q: %r", newfile);
463 dirwstat(newfile, &nd);
466 if(dirfwstat(fd, &nd) < 0)
467 warn("can't set modes for %q: %r", newfile);
477 Bprint(&bout, "%q %luo %q %q %lud %lld\n",
478 newfile, d->mode, d->uid, d->gid, d->mtime, d->length);
482 mkpath(char *prefix, char *elem)
487 n = strlen(prefix) + strlen(elem) + 2;
489 sprint(p, "%s/%s", prefix, elem);
498 t = emalloc(strlen(s) + 1);
505 sprint(newfile, "%s%s", newroot, f->new);
508 sprint(oldfile, "%s%s", oldroot, f->old);
510 strcpy(oldfile, f->old);
512 sprint(oldfile, "%s%s", oldroot, f->new);
513 if(strlen(newfile) >= sizeof newfile
514 || strlen(oldfile) >= sizeof oldfile)
515 error("name overfile");
529 * skip all files in the proto that
530 * could be in the current dir
538 if(indent < 0 || b == nil) /* b is nil when copying adm/users */
543 p = Brdline(b, '\n');
549 while((c = *p++) != '\n')
557 Bseek(b, -Blinelen(b), 1);
576 p = Brdline(b, '\n');
582 while((c = *p++) != '\n')
589 if(c == '\n' || c == '#')
592 f = emalloc(sizeof *f);
593 p = getname(p, &elem);
595 fprint(2, "getfile: %q root %q\n", elem, old->new);
596 f->new = mkpath(old->new, elem);
597 f->elem = utfrrune(f->new, L'/') + 1;
598 p = getmode(p, &f->mode);
599 p = getname(p, &f->uid);
602 p = getname(p, &f->gid);
606 if(f->old && strcmp(f->old, "-") == 0){
624 while((c = *p) == ' ' || c == '\t')
627 while((c = *q) != '\n' && c != ' ' && c != '\t')
632 new = emalloc(n + 1);
639 getname(char *p, char **buf)
644 while((c = *p) == ' ' || c == '\t')
648 while((c = *p) != '\n' && c != ' ' && c != '\t' && c != '\0')
651 *buf = malloc(p+1-start);
654 memmove(*buf, start, p-start);
655 (*buf)[p-start] = '\0';
660 warn("can't read environment variable %q", *buf+1);
672 getmode(char *p, ulong *xmode)
678 p = getname(p, &buf);
683 if(!*s || strcmp(s, "-") == 0)
698 if(s[0] < '0' || s[0] > '7'
699 || s[1] < '0' || s[1] > '7'
700 || s[2] < '0' || s[2] > '7'
702 warn("bad mode specification %q", buf);
706 *xmode = m | strtoul(s, 0, 8);
723 file.mode = DMDIR|0775;
728 strcpy(oldfile, file.new); /* Don't use root for /adm */
730 file.new = "/adm/users";
736 strcpy(oldfile, file.old); /* Don't use root for /adm/users */
740 file.mode = DMDIR|0775;
745 strcpy(oldfile, file.old); /* Don't use root for /adm */
758 snprint(kname, sizeof kname, "/srv/kfs.%s", name);
760 strcpy(kname, "/srv/kfs");
761 sfd = open(kname, ORDWR);
763 fprint(2, "can't open %q\n", kname);
764 exits("open /srv/kfs");
766 if(mount(sfd, -1, "/n/kfs", MREPL|MCREATE, "") < 0){
767 fprint(2, "can't mount kfs on /n/kfs\n");
771 strcat(kname, ".cmd");
772 sfd = open(kname, ORDWR);
774 fprint(2, "can't open %q\n", kname);
775 exits("open /srv/kfs");
787 if(write(sfd, cmd, strlen(cmd)) != strlen(cmd)){
788 fprint(2, "%q: error writing %q: %r", prog, cmd);
792 n = read(sfd, buf, sizeof buf - 1);
796 if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0)
798 if(strcmp(buf, "unknown command") == 0){
799 fprint(2, "%q: command %q not recognized\n", prog, cmd);
810 if((p = malloc(n)) == 0)
811 error("out of memory");
816 error(char *fmt, ...)
821 sprint(buf, "%q: %q:%d: ", prog, proto, lineno);
823 vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
825 fprint(2, "%s\n", buf);
837 sprint(buf, "%q: %q:%d: ", prog, proto, lineno);
839 vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
841 fprint(2, "%s\n", buf);
848 fprint(2, "%q from %q %q %q %lo\n", f->new, f->old, f->uid, f->gid, f->mode);
850 fprint(2, "%q %q %q %lo\n", f->new, f->uid, f->gid, f->mode);
856 fprint(2, "usage: %q [-aprvx] [-d root] [-n name] [-s source] [-u users] [-z n] proto ...\n", prog);