URI:
       tmkfs.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tmkfs.c (14332B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <auth.h>
            4 #include <bio.h>
            5 
            6 #define mkdir plan9mkdir
            7 #define getmode plan9_getmode
            8 #define setuid plan9_setuid
            9 
           10 enum{
           11         LEN        = 8*1024,
           12         HUNKS        = 128,
           13 
           14         /*
           15          * types of destination file sytems
           16          */
           17         Kfs = 0,
           18         Fs,
           19         Archive,
           20 };
           21 
           22 typedef struct File        File;
           23 
           24 struct File{
           25         char        *new;
           26         char        *elem;
           27         char        *old;
           28         char        *uid;
           29         char        *gid;
           30         ulong        mode;
           31 };
           32 
           33 void        arch(Dir*);
           34 void        copy(Dir*);
           35 int        copyfile(File*, Dir*, int);
           36 void*        emalloc(ulong);
           37 void        error(char *, ...);
           38 void        freefile(File*);
           39 File*        getfile(File*);
           40 char*        getmode(char*, ulong*);
           41 char*        getname(char*, char**);
           42 char*        getpath(char*);
           43 void        kfscmd(char *);
           44 void        mkdir(Dir*);
           45 int        mkfile(File*);
           46 void        mkfs(File*, int);
           47 char*        mkpath(char*, char*);
           48 void        mktree(File*, int);
           49 void        mountkfs(char*);
           50 void        printfile(File*);
           51 void        setnames(File*);
           52 void        setusers(void);
           53 void        skipdir(void);
           54 char*        strdup(char*);
           55 int        uptodate(Dir*, char*);
           56 void        usage(void);
           57 void        warn(char *, ...);
           58 
           59 Biobuf        *b;
           60 Biobuf        bout;                        /* stdout when writing archive */
           61 uchar        boutbuf[2*LEN];
           62 char        newfile[LEN];
           63 char        oldfile[LEN];
           64 char        *proto;
           65 char        *cputype;
           66 char        *users;
           67 char        *oldroot;
           68 char        *newroot;
           69 char        *prog = "mkfs";
           70 int        lineno;
           71 char        *buf;
           72 char        *zbuf;
           73 int        buflen = 1024-8;
           74 int        indent;
           75 int        verb;
           76 int        modes;
           77 int        ream;
           78 int        debug;
           79 int        xflag;
           80 int        sfd;
           81 int        fskind;                        /* Kfs, Fs, Archive */
           82 int        setuid;                        /* on Fs: set uid and gid? */
           83 char        *user;
           84 
           85 void
           86 main(int argc, char **argv)
           87 {
           88         File file;
           89         char *name;
           90         int i, errs;
           91 
           92         quotefmtinstall();
           93         user = getuser();
           94         name = "";
           95         memset(&file, 0, sizeof file);
           96         file.new = "";
           97         file.old = 0;
           98         oldroot = "";
           99         newroot = "/n/kfs";
          100         users = 0;
          101         fskind = Kfs;
          102         ARGBEGIN{
          103         case 'a':
          104                 if(fskind != Kfs) {
          105                         fprint(2, "cannot use -a with -d\n");
          106                         usage();
          107                 }
          108                 fskind = Archive;
          109                 newroot = "";
          110                 Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf);
          111                 break;
          112         case 'd':
          113                 if(fskind != Kfs) {
          114                         fprint(2, "cannot use -d with -a\n");
          115                         usage();
          116                 }
          117                 fskind = Fs;
          118                 newroot = ARGF();
          119                 break;
          120         case 'D':
          121                 debug = 1;
          122                 break;
          123         case 'n':
          124                 name = EARGF(usage());
          125                 break;
          126         case 'p':
          127                 modes = 1;
          128                 break;
          129         case 'r':
          130                 ream = 1;
          131                 break;
          132         case 's':
          133                 oldroot = ARGF();
          134                 break;
          135         case 'u':
          136                 users = ARGF();
          137                 break;
          138         case 'U':
          139                 setuid = 1;
          140                 break;
          141         case 'v':
          142                 verb = 1;
          143                 break;
          144         case 'x':
          145                 xflag = 1;
          146                 break;
          147         case 'z':
          148                 buflen = atoi(ARGF())-8;
          149                 break;
          150         default:
          151                 usage();
          152         }ARGEND
          153 
          154         if(!argc)
          155                 usage();
          156 
          157         buf = emalloc(buflen);
          158         zbuf = emalloc(buflen);
          159         memset(zbuf, 0, buflen);
          160 
          161         mountkfs(name);
          162         kfscmd("allow");
          163         proto = "users";
          164         setusers();
          165         cputype = getenv("cputype");
          166         if(cputype == 0)
          167                 cputype = "68020";
          168 
          169         errs = 0;
          170         for(i = 0; i < argc; i++){
          171                 proto = argv[i];
          172                 fprint(2, "processing %q\n", proto);
          173 
          174                 b = Bopen(proto, OREAD);
          175                 if(!b){
          176                         fprint(2, "%q: can't open %q: skipping\n", prog, proto);
          177                         errs++;
          178                         continue;
          179                 }
          180 
          181                 lineno = 0;
          182                 indent = 0;
          183                 mkfs(&file, -1);
          184                 Bterm(b);
          185         }
          186         fprint(2, "file system made\n");
          187         kfscmd("disallow");
          188         kfscmd("sync");
          189         if(errs)
          190                 exits("skipped protos");
          191         if(fskind == Archive){
          192                 Bprint(&bout, "end of archive\n");
          193                 Bterm(&bout);
          194         }
          195         exits(0);
          196 }
          197 
          198 void
          199 mkfs(File *me, int level)
          200 {
          201         File *child;
          202         int rec;
          203 
          204         child = getfile(me);
          205         if(!child)
          206                 return;
          207         if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
          208                 rec = child->elem[0] == '+';
          209                 free(child->new);
          210                 child->new = strdup(me->new);
          211                 setnames(child);
          212                 mktree(child, rec);
          213                 freefile(child);
          214                 child = getfile(me);
          215         }
          216         while(child && indent > level){
          217                 if(mkfile(child))
          218                         mkfs(child, indent);
          219                 freefile(child);
          220                 child = getfile(me);
          221         }
          222         if(child){
          223                 freefile(child);
          224                 Bseek(b, -Blinelen(b), 1);
          225                 lineno--;
          226         }
          227 }
          228 
          229 void
          230 mktree(File *me, int rec)
          231 {
          232         File child;
          233         Dir *d;
          234         int i, n, fd;
          235 
          236         fd = open(oldfile, OREAD);
          237         if(fd < 0){
          238                 warn("can't open %q: %r", oldfile);
          239                 return;
          240         }
          241 
          242         child = *me;
          243         while((n = dirread(fd, &d)) > 0){
          244                 for(i = 0; i < n; i++){
          245                         child.new = mkpath(me->new, d[i].name);
          246                         if(me->old)
          247                                 child.old = mkpath(me->old, d[i].name);
          248                         child.elem = d[i].name;
          249                         setnames(&child);
          250                         if(copyfile(&child, &d[i], 1) && rec)
          251                                 mktree(&child, rec);
          252                         free(child.new);
          253                         if(child.old)
          254                                 free(child.old);
          255                 }
          256         }
          257         close(fd);
          258 }
          259 
          260 int
          261 mkfile(File *f)
          262 {
          263         Dir *dir;
          264 
          265         if((dir = dirstat(oldfile)) == nil){
          266                 warn("can't stat file %q: %r", oldfile);
          267                 skipdir();
          268                 return 0;
          269         }
          270         return copyfile(f, dir, 0);
          271 }
          272 
          273 int
          274 copyfile(File *f, Dir *d, int permonly)
          275 {
          276         ulong mode;
          277         Dir nd;
          278 
          279         if(xflag){
          280                 Bprint(&bout, "%q\t%ld\t%lld\n", f->new, d->mtime, d->length);
          281                 return (d->mode & DMDIR) != 0;
          282         }
          283         if(verb && (fskind == Archive || ream))
          284                 fprint(2, "%q\n", f->new);
          285         d->name = f->elem;
          286         if(d->type != 'M' && d->type != 'Z'){
          287                 d->uid = "sys";
          288                 d->gid = "sys";
          289                 mode = (d->mode >> 6) & 7;
          290                 d->mode |= mode | (mode << 3);
          291         }
          292         if(strcmp(f->uid, "-") != 0)
          293                 d->uid = f->uid;
          294         if(strcmp(f->gid, "-") != 0)
          295                 d->gid = f->gid;
          296         if(fskind == Fs && !setuid){
          297                 d->uid = "";
          298                 d->gid = "";
          299         }
          300         if(f->mode != ~0){
          301                 if(permonly)
          302                         d->mode = (d->mode & ~0666) | (f->mode & 0666);
          303                 else if((d->mode&DMDIR) != (f->mode&DMDIR))
          304                         warn("inconsistent mode for %q", f->new);
          305                 else
          306                         d->mode = f->mode;
          307         }
          308         if(!uptodate(d, newfile)){
          309                 if(verb && (fskind != Archive && ream == 0))
          310                         fprint(2, "%q\n", f->new);
          311                 if(d->mode & DMDIR)
          312                         mkdir(d);
          313                 else
          314                         copy(d);
          315         }else if(modes){
          316                 nulldir(&nd);
          317                 nd.mode = d->mode;
          318                 nd.gid = d->gid;
          319                 nd.mtime = d->mtime;
          320                 if(verb && (fskind != Archive && ream == 0))
          321                         fprint(2, "%q\n", f->new);
          322                 if(dirwstat(newfile, &nd) < 0)
          323                         warn("can't set modes for %q: %r", f->new);
          324                 nulldir(&nd);
          325                 nd.uid = d->uid;
          326                 dirwstat(newfile, &nd);
          327         }
          328         return (d->mode & DMDIR) != 0;
          329 }
          330 
          331 /*
          332  * check if file to is up to date with
          333  * respect to the file represented by df
          334  */
          335 int
          336 uptodate(Dir *df, char *to)
          337 {
          338         int ret;
          339         Dir *dt;
          340 
          341         if(fskind == Archive || ream || (dt = dirstat(to)) == nil)
          342                 return 0;
          343         ret = dt->mtime >= df->mtime;
          344         free(dt);
          345         return ret;
          346 }
          347 
          348 void
          349 copy(Dir *d)
          350 {
          351         char cptmp[LEN], *p;
          352         int f, t, n, needwrite, nowarnyet = 1;
          353         vlong tot, len;
          354         Dir nd;
          355 
          356         f = open(oldfile, OREAD);
          357         if(f < 0){
          358                 warn("can't open %q: %r", oldfile);
          359                 return;
          360         }
          361         t = -1;
          362         if(fskind == Archive)
          363                 arch(d);
          364         else{
          365                 strcpy(cptmp, newfile);
          366                 p = utfrrune(cptmp, L'/');
          367                 if(!p)
          368                         error("internal temporary file error");
          369                 strcpy(p+1, "__mkfstmp");
          370                 t = create(cptmp, OWRITE, 0666);
          371                 if(t < 0){
          372                         warn("can't create %q: %r", newfile);
          373                         close(f);
          374                         return;
          375                 }
          376         }
          377 
          378         needwrite = 0;
          379         for(tot = 0; tot < d->length; tot += n){
          380                 len = d->length - tot;
          381                 /* don't read beyond d->length */
          382                 if (len > buflen)
          383                         len = buflen;
          384                 n = read(f, buf, len);
          385                 if(n <= 0) {
          386                         if(n < 0 && nowarnyet) {
          387                                 warn("can't read %q: %r", oldfile);
          388                                 nowarnyet = 0;
          389                         }
          390                         /*
          391                          * don't quit: pad to d->length (in pieces) to agree
          392                          * with the length in the header, already emitted.
          393                          */
          394                         memset(buf, 0, len);
          395                         n = len;
          396                 }
          397                 if(fskind == Archive){
          398                         if(Bwrite(&bout, buf, n) != n)
          399                                 error("write error: %r");
          400                 }else if(memcmp(buf, zbuf, n) == 0){
          401                         if(seek(t, n, 1) < 0)
          402                                 error("can't write zeros to %q: %r", newfile);
          403                         needwrite = 1;
          404                 }else{
          405                         if(write(t, buf, n) < n)
          406                                 error("can't write %q: %r", newfile);
          407                         needwrite = 0;
          408                 }
          409         }
          410         close(f);
          411         if(needwrite){
          412                 if(seek(t, -1, 1) < 0 || write(t, zbuf, 1) != 1)
          413                         error("can't write zero at end of %q: %r", newfile);
          414         }
          415         if(tot != d->length){
          416                 /* this should no longer happen */
          417                 warn("wrong number of bytes written to %q (was %lld should be %lld)\n",
          418                         newfile, tot, d->length);
          419                 if(fskind == Archive){
          420                         warn("seeking to proper position\n");
          421                         /* does no good if stdout is a pipe */
          422                         Bseek(&bout, d->length - tot, 1);
          423                 }
          424         }
          425         if(fskind == Archive)
          426                 return;
          427         remove(newfile);
          428         nulldir(&nd);
          429         nd.mode = d->mode;
          430         nd.gid = d->gid;
          431         nd.mtime = d->mtime;
          432         nd.name = d->name;
          433         if(dirfwstat(t, &nd) < 0)
          434                 error("can't move tmp file to %q: %r", newfile);
          435         nulldir(&nd);
          436         nd.uid = d->uid;
          437         dirfwstat(t, &nd);
          438         close(t);
          439 }
          440 
          441 void
          442 mkdir(Dir *d)
          443 {
          444         Dir *d1;
          445         Dir nd;
          446         int fd;
          447 
          448         if(fskind == Archive){
          449                 arch(d);
          450                 return;
          451         }
          452         fd = create(newfile, OREAD, d->mode);
          453         nulldir(&nd);
          454         nd.mode = d->mode;
          455         nd.gid = d->gid;
          456         nd.mtime = d->mtime;
          457         if(fd < 0){
          458                 if((d1 = dirstat(newfile)) == nil || !(d1->mode & DMDIR)){
          459                         free(d1);
          460                         error("can't create %q", newfile);
          461                 }
          462                 free(d1);
          463                 if(dirwstat(newfile, &nd) < 0)
          464                         warn("can't set modes for %q: %r", newfile);
          465                 nulldir(&nd);
          466                 nd.uid = d->uid;
          467                 dirwstat(newfile, &nd);
          468                 return;
          469         }
          470         if(dirfwstat(fd, &nd) < 0)
          471                 warn("can't set modes for %q: %r", newfile);
          472         nulldir(&nd);
          473         nd.uid = d->uid;
          474         dirfwstat(fd, &nd);
          475         close(fd);
          476 }
          477 
          478 void
          479 arch(Dir *d)
          480 {
          481         Bprint(&bout, "%q %luo %q %q %lud %lld\n",
          482                 newfile, d->mode, d->uid, d->gid, d->mtime, d->length);
          483 }
          484 
          485 char *
          486 mkpath(char *prefix, char *elem)
          487 {
          488         char *p;
          489         int n;
          490 
          491         n = strlen(prefix) + strlen(elem) + 2;
          492         p = emalloc(n);
          493         sprint(p, "%s/%s", prefix, elem);
          494         return p;
          495 }
          496 
          497 char *
          498 strdup(char *s)
          499 {
          500         char *t;
          501 
          502         t = emalloc(strlen(s) + 1);
          503         return strcpy(t, s);
          504 }
          505 
          506 void
          507 setnames(File *f)
          508 {
          509         sprint(newfile, "%s%s", newroot, f->new);
          510         if(f->old){
          511                 if(f->old[0] == '/')
          512                         sprint(oldfile, "%s%s", oldroot, f->old);
          513                 else
          514                         strcpy(oldfile, f->old);
          515         }else
          516                 sprint(oldfile, "%s%s", oldroot, f->new);
          517         if(strlen(newfile) >= sizeof newfile
          518         || strlen(oldfile) >= sizeof oldfile)
          519                 error("name overfile");
          520 }
          521 
          522 void
          523 freefile(File *f)
          524 {
          525         if(f->old)
          526                 free(f->old);
          527         if(f->new)
          528                 free(f->new);
          529         free(f);
          530 }
          531 
          532 /*
          533  * skip all files in the proto that
          534  * could be in the current dir
          535  */
          536 void
          537 skipdir(void)
          538 {
          539         char *p, c;
          540         int level;
          541 
          542         if(indent < 0 || b == nil)        /* b is nil when copying adm/users */
          543                 return;
          544         level = indent;
          545         for(;;){
          546                 indent = 0;
          547                 p = Brdline(b, '\n');
          548                 lineno++;
          549                 if(!p){
          550                         indent = -1;
          551                         return;
          552                 }
          553                 while((c = *p++) != '\n')
          554                         if(c == ' ')
          555                                 indent++;
          556                         else if(c == '\t')
          557                                 indent += 8;
          558                         else
          559                                 break;
          560                 if(indent <= level){
          561                         Bseek(b, -Blinelen(b), 1);
          562                         lineno--;
          563                         return;
          564                 }
          565         }
          566 }
          567 
          568 File*
          569 getfile(File *old)
          570 {
          571         File *f;
          572         char *elem;
          573         char *p;
          574         int c;
          575 
          576         if(indent < 0)
          577                 return 0;
          578 loop:
          579         indent = 0;
          580         p = Brdline(b, '\n');
          581         lineno++;
          582         if(!p){
          583                 indent = -1;
          584                 return 0;
          585         }
          586         while((c = *p++) != '\n')
          587                 if(c == ' ')
          588                         indent++;
          589                 else if(c == '\t')
          590                         indent += 8;
          591                 else
          592                         break;
          593         if(c == '\n' || c == '#')
          594                 goto loop;
          595         p--;
          596         f = emalloc(sizeof *f);
          597         p = getname(p, &elem);
          598         if(debug)
          599                 fprint(2, "getfile: %q root %q\n", elem, old->new);
          600         f->new = mkpath(old->new, elem);
          601         f->elem = utfrrune(f->new, L'/') + 1;
          602         p = getmode(p, &f->mode);
          603         p = getname(p, &f->uid);
          604         if(!*f->uid)
          605                 f->uid = "-";
          606         p = getname(p, &f->gid);
          607         if(!*f->gid)
          608                 f->gid = "-";
          609         f->old = getpath(p);
          610         if(f->old && strcmp(f->old, "-") == 0){
          611                 free(f->old);
          612                 f->old = 0;
          613         }
          614         setnames(f);
          615 
          616         if(debug)
          617                 printfile(f);
          618 
          619         return f;
          620 }
          621 
          622 char*
          623 getpath(char *p)
          624 {
          625         char *q, *new;
          626         int c, n;
          627 
          628         while((c = *p) == ' ' || c == '\t')
          629                 p++;
          630         q = p;
          631         while((c = *q) != '\n' && c != ' ' && c != '\t')
          632                 q++;
          633         if(q == p)
          634                 return 0;
          635         n = q - p;
          636         new = emalloc(n + 1);
          637         memcpy(new, p, n);
          638         new[n] = 0;
          639         return new;
          640 }
          641 
          642 char*
          643 getname(char *p, char **buf)
          644 {
          645         char *s, *start;
          646         int c;
          647 
          648         while((c = *p) == ' ' || c == '\t')
          649                 p++;
          650 
          651         start = p;
          652         while((c = *p) != '\n' && c != ' ' && c != '\t' && c != '\0')
          653                 p++;
          654 
          655         *buf = malloc(p+1-start);
          656         if(*buf == nil)
          657                 return nil;
          658         memmove(*buf, start, p-start);
          659         (*buf)[p-start] = '\0';
          660 
          661         if(**buf == '$'){
          662                 s = getenv(*buf+1);
          663                 if(s == 0){
          664                         warn("can't read environment variable %q", *buf+1);
          665                         skipdir();
          666                         free(*buf);
          667                         return nil;
          668                 }
          669                 free(*buf);
          670                 *buf = s;
          671         }
          672         return p;
          673 }
          674 
          675 char*
          676 getmode(char *p, ulong *xmode)
          677 {
          678         char *buf, *s;
          679         ulong m;
          680 
          681         *xmode = ~0;
          682         p = getname(p, &buf);
          683         if(p == nil)
          684                 return nil;
          685 
          686         s = buf;
          687         if(!*s || strcmp(s, "-") == 0)
          688                 return p;
          689         m = 0;
          690         if(*s == 'd'){
          691                 m |= DMDIR;
          692                 s++;
          693         }
          694         if(*s == 'a'){
          695                 m |= DMAPPEND;
          696                 s++;
          697         }
          698         if(*s == 'l'){
          699                 m |= DMEXCL;
          700                 s++;
          701         }
          702         if(s[0] < '0' || s[0] > '7'
          703         || s[1] < '0' || s[1] > '7'
          704         || s[2] < '0' || s[2] > '7'
          705         || s[3]){
          706                 warn("bad mode specification %q", buf);
          707                 free(buf);
          708                 return p;
          709         }
          710         *xmode = m | strtoul(s, 0, 8);
          711         free(buf);
          712         return p;
          713 }
          714 
          715 void
          716 setusers(void)
          717 {
          718         File file;
          719         int m;
          720 
          721         if(fskind != Kfs)
          722                 return;
          723         m = modes;
          724         modes = 1;
          725         file.uid = "adm";
          726         file.gid = "adm";
          727         file.mode = DMDIR|0775;
          728         file.new = "/adm";
          729         file.elem = "adm";
          730         file.old = 0;
          731         setnames(&file);
          732         strcpy(oldfile, file.new);        /* Don't use root for /adm */
          733         mkfile(&file);
          734         file.new = "/adm/users";
          735         file.old = users;
          736         file.elem = "users";
          737         file.mode = 0664;
          738         setnames(&file);
          739         if (file.old)
          740                 strcpy(oldfile, file.old);        /* Don't use root for /adm/users */
          741         mkfile(&file);
          742         kfscmd("user");
          743         mkfile(&file);
          744         file.mode = DMDIR|0775;
          745         file.new = "/adm";
          746         file.old = "/adm";
          747         file.elem = "adm";
          748         setnames(&file);
          749         strcpy(oldfile, file.old);        /* Don't use root for /adm */
          750         mkfile(&file);
          751         modes = m;
          752 }
          753 
          754 void
          755 mountkfs(char *name)
          756 {
          757         if(fskind != Kfs)
          758                 return;
          759         sysfatal("no kfs: use -a or -d");
          760 }
          761 
          762 void
          763 kfscmd(char *cmd)
          764 {
          765         char buf[4*1024];
          766         int n;
          767 
          768         if(fskind != Kfs)
          769                 return;
          770         if(write(sfd, cmd, strlen(cmd)) != strlen(cmd)){
          771                 fprint(2, "%q: error writing %q: %r", prog, cmd);
          772                 return;
          773         }
          774         for(;;){
          775                 n = read(sfd, buf, sizeof buf - 1);
          776                 if(n <= 0)
          777                         return;
          778                 buf[n] = '\0';
          779                 if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0)
          780                         return;
          781                 if(strcmp(buf, "unknown command") == 0){
          782                         fprint(2, "%q: command %q not recognized\n", prog, cmd);
          783                         return;
          784                 }
          785         }
          786 }
          787 
          788 void *
          789 emalloc(ulong n)
          790 {
          791         void *p;
          792 
          793         if((p = malloc(n)) == 0)
          794                 error("out of memory");
          795         return p;
          796 }
          797 
          798 void
          799 error(char *fmt, ...)
          800 {
          801         char buf[1024];
          802         va_list arg;
          803 
          804         sprint(buf, "%q: %q:%d: ", prog, proto, lineno);
          805         va_start(arg, fmt);
          806         vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
          807         va_end(arg);
          808         fprint(2, "%s\n", buf);
          809         kfscmd("disallow");
          810         kfscmd("sync");
          811         exits(0);
          812 }
          813 
          814 void
          815 warn(char *fmt, ...)
          816 {
          817         char buf[1024];
          818         va_list arg;
          819 
          820         sprint(buf, "%q: %q:%d: ", prog, proto, lineno);
          821         va_start(arg, fmt);
          822         vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
          823         va_end(arg);
          824         fprint(2, "%s\n", buf);
          825 }
          826 
          827 void
          828 printfile(File *f)
          829 {
          830         if(f->old)
          831                 fprint(2, "%q from %q %q %q %lo\n", f->new, f->old, f->uid, f->gid, f->mode);
          832         else
          833                 fprint(2, "%q %q %q %lo\n", f->new, f->uid, f->gid, f->mode);
          834 }
          835 
          836 void
          837 usage(void)
          838 {
          839         fprint(2, "usage: %q [-aprvx] [-d root] [-n name] [-s source] [-u users] [-z n] proto ...\n", prog);
          840         exits("usage");
          841 }