aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/disk
diff options
context:
space:
mode:
authorRuss Cox <rsc@swtch.com>2012-07-17 19:10:45 -0400
committerRuss Cox <rsc@swtch.com>2012-07-17 19:10:45 -0400
commitd2173bb552d308d60a4e4a53cd3b8e0949b38dbc (patch)
tree196b3078e732ff170019734fb6990ceab2f80b01 /src/cmd/disk
parentf0add8ef24f83cb2085ef1c7534d16c57881e3f3 (diff)
downloadplan9port-d2173bb552d308d60a4e4a53cd3b8e0949b38dbc.tar.gz
plan9port-d2173bb552d308d60a4e4a53cd3b8e0949b38dbc.tar.bz2
plan9port-d2173bb552d308d60a4e4a53cd3b8e0949b38dbc.zip
disk/mkfs, disk/mkext: add from Plan 9
R=rsc, rsc http://codereview.appspot.com/6405057
Diffstat (limited to 'src/cmd/disk')
-rw-r--r--src/cmd/disk/mkext.c318
-rw-r--r--src/cmd/disk/mkfile11
-rw-r--r--src/cmd/disk/mkfs.c840
3 files changed, 1169 insertions, 0 deletions
diff --git a/src/cmd/disk/mkext.c b/src/cmd/disk/mkext.c
new file mode 100644
index 00000000..05190d04
--- /dev/null
+++ b/src/cmd/disk/mkext.c
@@ -0,0 +1,318 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+enum{
+ LEN = 8*1024,
+ NFLDS = 6, /* filename, modes, uid, gid, mtime, bytes */
+};
+
+int selected(char*, int, char*[]);
+void mkdirs(char*, char*);
+void mkdir(char*, ulong, ulong, char*, char*);
+void extract(char*, ulong, ulong, char*, char*, uvlong);
+void seekpast(uvlong);
+void error(char*, ...);
+void warn(char*, ...);
+void usage(void);
+#pragma varargck argpos warn 1
+#pragma varargck argpos error 1
+
+Biobuf bin;
+uchar binbuf[2*LEN];
+char linebuf[LEN];
+int uflag;
+int hflag;
+int vflag;
+int Tflag;
+
+void
+main(int argc, char **argv)
+{
+ Biobuf bout;
+ char *fields[NFLDS], name[2*LEN], *p, *namep;
+ char *uid, *gid;
+ ulong mode, mtime;
+ uvlong bytes;
+
+ quotefmtinstall();
+ namep = name;
+ ARGBEGIN{
+ case 'd':
+ p = ARGF();
+ if(strlen(p) >= LEN)
+ error("destination fs name too long\n");
+ strcpy(name, p);
+ namep = name + strlen(name);
+ break;
+ case 'h':
+ hflag = 1;
+ Binit(&bout, 1, OWRITE);
+ break;
+ case 'u':
+ uflag = 1;
+ Tflag = 1;
+ break;
+ case 'T':
+ Tflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ Binits(&bin, 0, OREAD, binbuf, sizeof binbuf);
+ while(p = Brdline(&bin, '\n')){
+ p[Blinelen(&bin)-1] = '\0';
+ strcpy(linebuf, p);
+ p = linebuf;
+ if(strcmp(p, "end of archive") == 0){
+ Bterm(&bout);
+ fprint(2, "done\n");
+ exits(0);
+ }
+ if (gettokens(p, fields, NFLDS, " \t") != NFLDS){
+ warn("too few fields in file header");
+ continue;
+ }
+ p = unquotestrdup(fields[0]);
+ strcpy(namep, p);
+ free(p);
+ mode = strtoul(fields[1], 0, 8);
+ uid = fields[2];
+ gid = fields[3];
+ mtime = strtoul(fields[4], 0, 10);
+ bytes = strtoull(fields[5], 0, 10);
+ if(argc){
+ if(!selected(namep, argc, argv)){
+ if(bytes)
+ seekpast(bytes);
+ continue;
+ }
+ mkdirs(name, namep);
+ }
+ if(hflag){
+ Bprint(&bout, "%q %luo %q %q %lud %llud\n",
+ name, mode, uid, gid, mtime, bytes);
+ if(bytes)
+ seekpast(bytes);
+ continue;
+ }
+ if(mode & DMDIR)
+ mkdir(name, mode, mtime, uid, gid);
+ else
+ extract(name, mode, mtime, uid, gid, bytes);
+ }
+ fprint(2, "premature end of archive\n");
+ exits("premature end of archive");
+}
+
+int
+fileprefix(char *prefix, char *s)
+{
+ while(*prefix)
+ if(*prefix++ != *s++)
+ return 0;
+ if(*s && *s != '/')
+ return 0;
+ return 1;
+}
+
+int
+selected(char *s, int argc, char *argv[])
+{
+ int i;
+
+ for(i=0; i<argc; i++)
+ if(fileprefix(argv[i], s))
+ return 1;
+ return 0;
+}
+
+void
+mkdirs(char *name, char *namep)
+{
+ char buf[2*LEN], *p;
+ int fd;
+
+ strcpy(buf, name);
+ for(p = &buf[namep - name]; p = utfrune(p, '/'); p++){
+ if(p[1] == '\0')
+ return;
+ *p = 0;
+ fd = create(buf, OREAD, 0775|DMDIR);
+ close(fd);
+ *p = '/';
+ }
+}
+
+void
+mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
+{
+ Dir *d, xd;
+ int fd;
+ char *p;
+ char olderr[256];
+
+ fd = create(name, OREAD, mode);
+ if(fd < 0){
+ rerrstr(olderr, sizeof(olderr));
+ if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){
+ free(d);
+ warn("can't make directory %q, mode %luo: %s", name, mode, olderr);
+ return;
+ }
+ free(d);
+ }
+ close(fd);
+
+ d = &xd;
+ nulldir(d);
+ p = utfrrune(name, L'/');
+ if(p)
+ p++;
+ else
+ p = name;
+ d->name = p;
+ if(uflag){
+ d->uid = uid;
+ d->gid = gid;
+ }
+ if(Tflag)
+ d->mtime = mtime;
+ d->mode = mode;
+ if(dirwstat(name, d) < 0)
+ warn("can't set modes for %q: %r", name);
+
+ if(uflag||Tflag){
+ if((d = dirstat(name)) == nil){
+ warn("can't reread modes for %q: %r", name);
+ return;
+ }
+ if(Tflag && d->mtime != mtime)
+ warn("%q: time mismatch %lud %lud\n", name, mtime, d->mtime);
+ if(uflag && strcmp(uid, d->uid))
+ warn("%q: uid mismatch %q %q", name, uid, d->uid);
+ if(uflag && strcmp(gid, d->gid))
+ warn("%q: gid mismatch %q %q", name, gid, d->gid);
+ }
+}
+
+void
+extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, uvlong bytes)
+{
+ Dir d, *nd;
+ Biobuf *b;
+ char buf[LEN];
+ char *p;
+ ulong n;
+ uvlong tot;
+
+ if(vflag)
+ print("x %q %llud bytes\n", name, bytes);
+
+ b = Bopen(name, OWRITE);
+ if(!b){
+ warn("can't make file %q: %r", name);
+ seekpast(bytes);
+ return;
+ }
+ for(tot = 0; tot < bytes; tot += n){
+ n = sizeof buf;
+ if(tot + n > bytes)
+ n = bytes - tot;
+ n = Bread(&bin, buf, n);
+ if(n <= 0)
+ error("premature eof reading %q", name);
+ if(Bwrite(b, buf, n) != n)
+ warn("error writing %q: %r", name);
+ }
+
+ nulldir(&d);
+ p = utfrrune(name, '/');
+ if(p)
+ p++;
+ else
+ p = name;
+ d.name = p;
+ if(uflag){
+ d.uid = uid;
+ d.gid = gid;
+ }
+ if(Tflag)
+ d.mtime = mtime;
+ d.mode = mode;
+ Bflush(b);
+ if(dirfwstat(Bfildes(b), &d) < 0)
+ warn("can't set modes for %q: %r", name);
+ if(uflag||Tflag){
+ if((nd = dirfstat(Bfildes(b))) == nil)
+ warn("can't reread modes for %q: %r", name);
+ else{
+ if(Tflag && nd->mtime != mtime)
+ warn("%q: time mismatch %lud %lud\n",
+ name, mtime, nd->mtime);
+ if(uflag && strcmp(uid, nd->uid))
+ warn("%q: uid mismatch %q %q",
+ name, uid, nd->uid);
+ if(uflag && strcmp(gid, nd->gid))
+ warn("%q: gid mismatch %q %q",
+ name, gid, nd->gid);
+ free(nd);
+ }
+ }
+ Bterm(b);
+}
+
+void
+seekpast(uvlong bytes)
+{
+ char buf[LEN];
+ long n;
+ uvlong tot;
+
+ for(tot = 0; tot < bytes; tot += n){
+ n = sizeof buf;
+ if(tot + n > bytes)
+ n = bytes - tot;
+ n = Bread(&bin, buf, n);
+ if(n < 0)
+ error("premature eof");
+ }
+}
+
+void
+error(char *fmt, ...)
+{
+ char buf[1024];
+ va_list arg;
+
+ sprint(buf, "%q: ", argv0);
+ va_start(arg, fmt);
+ vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%s\n", buf);
+ exits(0);
+}
+
+void
+warn(char *fmt, ...)
+{
+ char buf[1024];
+ va_list arg;
+
+ sprint(buf, "%q: ", argv0);
+ va_start(arg, fmt);
+ vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%s\n", buf);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n");
+ exits("usage");
+}
diff --git a/src/cmd/disk/mkfile b/src/cmd/disk/mkfile
new file mode 100644
index 00000000..d702a890
--- /dev/null
+++ b/src/cmd/disk/mkfile
@@ -0,0 +1,11 @@
+<$PLAN9/src/mkhdr
+
+TARG=\
+ mkext\
+ mkfs\
+
+BIN=$BIN/disk
+
+<$PLAN9/src/mkmany
+
+
diff --git a/src/cmd/disk/mkfs.c b/src/cmd/disk/mkfs.c
new file mode 100644
index 00000000..1e70ab76
--- /dev/null
+++ b/src/cmd/disk/mkfs.c
@@ -0,0 +1,840 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <bio.h>
+
+#define getmode plan9_getmode
+#define setuid plan9_setuid
+
+enum{
+ LEN = 8*1024,
+ HUNKS = 128,
+
+ /*
+ * types of destination file sytems
+ */
+ Kfs = 0,
+ Fs,
+ Archive,
+};
+
+typedef struct File File;
+
+struct File{
+ char *new;
+ char *elem;
+ char *old;
+ char *uid;
+ char *gid;
+ ulong mode;
+};
+
+void arch(Dir*);
+void copy(Dir*);
+int copyfile(File*, Dir*, int);
+void* emalloc(ulong);
+void error(char *, ...);
+void freefile(File*);
+File* getfile(File*);
+char* getmode(char*, ulong*);
+char* getname(char*, char**);
+char* getpath(char*);
+void kfscmd(char *);
+void mkdir(Dir*);
+int mkfile(File*);
+void mkfs(File*, int);
+char* mkpath(char*, char*);
+void mktree(File*, int);
+void mountkfs(char*);
+void printfile(File*);
+void setnames(File*);
+void setusers(void);
+void skipdir(void);
+char* strdup(char*);
+int uptodate(Dir*, char*);
+void usage(void);
+void warn(char *, ...);
+
+Biobuf *b;
+Biobuf bout; /* stdout when writing archive */
+uchar boutbuf[2*LEN];
+char newfile[LEN];
+char oldfile[LEN];
+char *proto;
+char *cputype;
+char *users;
+char *oldroot;
+char *newroot;
+char *prog = "mkfs";
+int lineno;
+char *buf;
+char *zbuf;
+int buflen = 1024-8;
+int indent;
+int verb;
+int modes;
+int ream;
+int debug;
+int xflag;
+int sfd;
+int fskind; /* Kfs, Fs, Archive */
+int setuid; /* on Fs: set uid and gid? */
+char *user;
+
+void
+main(int argc, char **argv)
+{
+ File file;
+ char *name;
+ int i, errs;
+
+ quotefmtinstall();
+ user = getuser();
+ name = "";
+ memset(&file, 0, sizeof file);
+ file.new = "";
+ file.old = 0;
+ oldroot = "";
+ newroot = "/n/kfs";
+ users = 0;
+ fskind = Kfs;
+ ARGBEGIN{
+ case 'a':
+ if(fskind != Kfs) {
+ fprint(2, "cannot use -a with -d\n");
+ usage();
+ }
+ fskind = Archive;
+ newroot = "";
+ Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf);
+ break;
+ case 'd':
+ if(fskind != Kfs) {
+ fprint(2, "cannot use -d with -a\n");
+ usage();
+ }
+ fskind = Fs;
+ newroot = ARGF();
+ break;
+ case 'D':
+ debug = 1;
+ break;
+ case 'n':
+ name = EARGF(usage());
+ break;
+ case 'p':
+ modes = 1;
+ break;
+ case 'r':
+ ream = 1;
+ break;
+ case 's':
+ oldroot = ARGF();
+ break;
+ case 'u':
+ users = ARGF();
+ break;
+ case 'U':
+ setuid = 1;
+ break;
+ case 'v':
+ verb = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case 'z':
+ buflen = atoi(ARGF())-8;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(!argc)
+ usage();
+
+ buf = emalloc(buflen);
+ zbuf = emalloc(buflen);
+ memset(zbuf, 0, buflen);
+
+ mountkfs(name);
+ kfscmd("allow");
+ proto = "users";
+ setusers();
+ cputype = getenv("cputype");
+ if(cputype == 0)
+ cputype = "68020";
+
+ errs = 0;
+ for(i = 0; i < argc; i++){
+ proto = argv[i];
+ fprint(2, "processing %q\n", proto);
+
+ b = Bopen(proto, OREAD);
+ if(!b){
+ fprint(2, "%q: can't open %q: skipping\n", prog, proto);
+ errs++;
+ continue;
+ }
+
+ lineno = 0;
+ indent = 0;
+ mkfs(&file, -1);
+ Bterm(b);
+ }
+ fprint(2, "file system made\n");
+ kfscmd("disallow");
+ kfscmd("sync");
+ if(errs)
+ exits("skipped protos");
+ if(fskind == Archive){
+ Bprint(&bout, "end of archive\n");
+ Bterm(&bout);
+ }
+ exits(0);
+}
+
+void
+mkfs(File *me, int level)
+{
+ File *child;
+ int rec;
+
+ child = getfile(me);
+ if(!child)
+ return;
+ if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
+ rec = child->elem[0] == '+';
+ free(child->new);
+ child->new = strdup(me->new);
+ setnames(child);
+ mktree(child, rec);
+ freefile(child);
+ child = getfile(me);
+ }
+ while(child && indent > level){
+ if(mkfile(child))
+ mkfs(child, indent);
+ freefile(child);
+ child = getfile(me);
+ }
+ if(child){
+ freefile(child);
+ Bseek(b, -Blinelen(b), 1);
+ lineno--;
+ }
+}
+
+void
+mktree(File *me, int rec)
+{
+ File child;
+ Dir *d;
+ int i, n, fd;
+
+ fd = open(oldfile, OREAD);
+ if(fd < 0){
+ warn("can't open %q: %r", oldfile);
+ return;
+ }
+
+ child = *me;
+ while((n = dirread(fd, &d)) > 0){
+ for(i = 0; i < n; i++){
+ child.new = mkpath(me->new, d[i].name);
+ if(me->old)
+ child.old = mkpath(me->old, d[i].name);
+ child.elem = d[i].name;
+ setnames(&child);
+ if(copyfile(&child, &d[i], 1) && rec)
+ mktree(&child, rec);
+ free(child.new);
+ if(child.old)
+ free(child.old);
+ }
+ }
+ close(fd);
+}
+
+int
+mkfile(File *f)
+{
+ Dir *dir;
+
+ if((dir = dirstat(oldfile)) == nil){
+ warn("can't stat file %q: %r", oldfile);
+ skipdir();
+ return 0;
+ }
+ return copyfile(f, dir, 0);
+}
+
+int
+copyfile(File *f, Dir *d, int permonly)
+{
+ ulong mode;
+ Dir nd;
+
+ if(xflag){
+ Bprint(&bout, "%q\t%ld\t%lld\n", f->new, d->mtime, d->length);
+ return (d->mode & DMDIR) != 0;
+ }
+ if(verb && (fskind == Archive || ream))
+ fprint(2, "%q\n", f->new);
+ d->name = f->elem;
+ if(d->type != 'M' && d->type != 'Z'){
+ d->uid = "sys";
+ d->gid = "sys";
+ mode = (d->mode >> 6) & 7;
+ d->mode |= mode | (mode << 3);
+ }
+ if(strcmp(f->uid, "-") != 0)
+ d->uid = f->uid;
+ if(strcmp(f->gid, "-") != 0)
+ d->gid = f->gid;
+ if(fskind == Fs && !setuid){
+ d->uid = "";
+ d->gid = "";
+ }
+ if(f->mode != ~0){
+ if(permonly)
+ d->mode = (d->mode & ~0666) | (f->mode & 0666);
+ else if((d->mode&DMDIR) != (f->mode&DMDIR))
+ warn("inconsistent mode for %q", f->new);
+ else
+ d->mode = f->mode;
+ }
+ if(!uptodate(d, newfile)){
+ if(verb && (fskind != Archive && ream == 0))
+ fprint(2, "%q\n", f->new);
+ if(d->mode & DMDIR)
+ mkdir(d);
+ else
+ copy(d);
+ }else if(modes){
+ nulldir(&nd);
+ nd.mode = d->mode;
+ nd.gid = d->gid;
+ nd.mtime = d->mtime;
+ if(verb && (fskind != Archive && ream == 0))
+ fprint(2, "%q\n", f->new);
+ if(dirwstat(newfile, &nd) < 0)
+ warn("can't set modes for %q: %r", f->new);
+ nulldir(&nd);
+ nd.uid = d->uid;
+ dirwstat(newfile, &nd);
+ }
+ return (d->mode & DMDIR) != 0;
+}
+
+/*
+ * check if file to is up to date with
+ * respect to the file represented by df
+ */
+int
+uptodate(Dir *df, char *to)
+{
+ int ret;
+ Dir *dt;
+
+ if(fskind == Archive || ream || (dt = dirstat(to)) == nil)
+ return 0;
+ ret = dt->mtime >= df->mtime;
+ free(dt);
+ return ret;
+}
+
+void
+copy(Dir *d)
+{
+ char cptmp[LEN], *p;
+ int f, t, n, needwrite, nowarnyet = 1;
+ vlong tot, len;
+ Dir nd;
+
+ f = open(oldfile, OREAD);
+ if(f < 0){
+ warn("can't open %q: %r", oldfile);
+ return;
+ }
+ t = -1;
+ if(fskind == Archive)
+ arch(d);
+ else{
+ strcpy(cptmp, newfile);
+ p = utfrrune(cptmp, L'/');
+ if(!p)
+ error("internal temporary file error");
+ strcpy(p+1, "__mkfstmp");
+ t = create(cptmp, OWRITE, 0666);
+ if(t < 0){
+ warn("can't create %q: %r", newfile);
+ close(f);
+ return;
+ }
+ }
+
+ needwrite = 0;
+ for(tot = 0; tot < d->length; tot += n){
+ len = d->length - tot;
+ /* don't read beyond d->length */
+ if (len > buflen)
+ len = buflen;
+ n = read(f, buf, len);
+ if(n <= 0) {
+ if(n < 0 && nowarnyet) {
+ warn("can't read %q: %r", oldfile);
+ nowarnyet = 0;
+ }
+ /*
+ * don't quit: pad to d->length (in pieces) to agree
+ * with the length in the header, already emitted.
+ */
+ memset(buf, 0, len);
+ n = len;
+ }
+ if(fskind == Archive){
+ if(Bwrite(&bout, buf, n) != n)
+ error("write error: %r");
+ }else if(memcmp(buf, zbuf, n) == 0){
+ if(seek(t, n, 1) < 0)
+ error("can't write zeros to %q: %r", newfile);
+ needwrite = 1;
+ }else{
+ if(write(t, buf, n) < n)
+ error("can't write %q: %r", newfile);
+ needwrite = 0;
+ }
+ }
+ close(f);
+ if(needwrite){
+ if(seek(t, -1, 1) < 0 || write(t, zbuf, 1) != 1)
+ error("can't write zero at end of %q: %r", newfile);
+ }
+ if(tot != d->length){
+ /* this should no longer happen */
+ warn("wrong number of bytes written to %q (was %lld should be %lld)\n",
+ newfile, tot, d->length);
+ if(fskind == Archive){
+ warn("seeking to proper position\n");
+ /* does no good if stdout is a pipe */
+ Bseek(&bout, d->length - tot, 1);
+ }
+ }
+ if(fskind == Archive)
+ return;
+ remove(newfile);
+ nulldir(&nd);
+ nd.mode = d->mode;
+ nd.gid = d->gid;
+ nd.mtime = d->mtime;
+ nd.name = d->name;
+ if(dirfwstat(t, &nd) < 0)
+ error("can't move tmp file to %q: %r", newfile);
+ nulldir(&nd);
+ nd.uid = d->uid;
+ dirfwstat(t, &nd);
+ close(t);
+}
+
+void
+mkdir(Dir *d)
+{
+ Dir *d1;
+ Dir nd;
+ int fd;
+
+ if(fskind == Archive){
+ arch(d);
+ return;
+ }
+ fd = create(newfile, OREAD, d->mode);
+ nulldir(&nd);
+ nd.mode = d->mode;
+ nd.gid = d->gid;
+ nd.mtime = d->mtime;
+ if(fd < 0){
+ if((d1 = dirstat(newfile)) == nil || !(d1->mode & DMDIR)){
+ free(d1);
+ error("can't create %q", newfile);
+ }
+ free(d1);
+ if(dirwstat(newfile, &nd) < 0)
+ warn("can't set modes for %q: %r", newfile);
+ nulldir(&nd);
+ nd.uid = d->uid;
+ dirwstat(newfile, &nd);
+ return;
+ }
+ if(dirfwstat(fd, &nd) < 0)
+ warn("can't set modes for %q: %r", newfile);
+ nulldir(&nd);
+ nd.uid = d->uid;
+ dirfwstat(fd, &nd);
+ close(fd);
+}
+
+void
+arch(Dir *d)
+{
+ Bprint(&bout, "%q %luo %q %q %lud %lld\n",
+ newfile, d->mode, d->uid, d->gid, d->mtime, d->length);
+}
+
+char *
+mkpath(char *prefix, char *elem)
+{
+ char *p;
+ int n;
+
+ n = strlen(prefix) + strlen(elem) + 2;
+ p = emalloc(n);
+ sprint(p, "%s/%s", prefix, elem);
+ return p;
+}
+
+char *
+strdup(char *s)
+{
+ char *t;
+
+ t = emalloc(strlen(s) + 1);
+ return strcpy(t, s);
+}
+
+void
+setnames(File *f)
+{
+ sprint(newfile, "%s%s", newroot, f->new);
+ if(f->old){
+ if(f->old[0] == '/')
+ sprint(oldfile, "%s%s", oldroot, f->old);
+ else
+ strcpy(oldfile, f->old);
+ }else
+ sprint(oldfile, "%s%s", oldroot, f->new);
+ if(strlen(newfile) >= sizeof newfile
+ || strlen(oldfile) >= sizeof oldfile)
+ error("name overfile");
+}
+
+void
+freefile(File *f)
+{
+ if(f->old)
+ free(f->old);
+ if(f->new)
+ free(f->new);
+ free(f);
+}
+
+/*
+ * skip all files in the proto that
+ * could be in the current dir
+ */
+void
+skipdir(void)
+{
+ char *p, c;
+ int level;
+
+ if(indent < 0 || b == nil) /* b is nil when copying adm/users */
+ return;
+ level = indent;
+ for(;;){
+ indent = 0;
+ p = Brdline(b, '\n');
+ lineno++;
+ if(!p){
+ indent = -1;
+ return;
+ }
+ while((c = *p++) != '\n')
+ if(c == ' ')
+ indent++;
+ else if(c == '\t')
+ indent += 8;
+ else
+ break;
+ if(indent <= level){
+ Bseek(b, -Blinelen(b), 1);
+ lineno--;
+ return;
+ }
+ }
+}
+
+File*
+getfile(File *old)
+{
+ File *f;
+ char *elem;
+ char *p;
+ int c;
+
+ if(indent < 0)
+ return 0;
+loop:
+ indent = 0;
+ p = Brdline(b, '\n');
+ lineno++;
+ if(!p){
+ indent = -1;
+ return 0;
+ }
+ while((c = *p++) != '\n')
+ if(c == ' ')
+ indent++;
+ else if(c == '\t')
+ indent += 8;
+ else
+ break;
+ if(c == '\n' || c == '#')
+ goto loop;
+ p--;
+ f = emalloc(sizeof *f);
+ p = getname(p, &elem);
+ if(debug)
+ fprint(2, "getfile: %q root %q\n", elem, old->new);
+ f->new = mkpath(old->new, elem);
+ f->elem = utfrrune(f->new, L'/') + 1;
+ p = getmode(p, &f->mode);
+ p = getname(p, &f->uid);
+ if(!*f->uid)
+ f->uid = "-";
+ p = getname(p, &f->gid);
+ if(!*f->gid)
+ f->gid = "-";
+ f->old = getpath(p);
+ if(f->old && strcmp(f->old, "-") == 0){
+ free(f->old);
+ f->old = 0;
+ }
+ setnames(f);
+
+ if(debug)
+ printfile(f);
+
+ return f;
+}
+
+char*
+getpath(char *p)
+{
+ char *q, *new;
+ int c, n;
+
+ while((c = *p) == ' ' || c == '\t')
+ p++;
+ q = p;
+ while((c = *q) != '\n' && c != ' ' && c != '\t')
+ q++;
+ if(q == p)
+ return 0;
+ n = q - p;
+ new = emalloc(n + 1);
+ memcpy(new, p, n);
+ new[n] = 0;
+ return new;
+}
+
+char*
+getname(char *p, char **buf)
+{
+ char *s, *start;
+ int c;
+
+ while((c = *p) == ' ' || c == '\t')
+ p++;
+
+ start = p;
+ while((c = *p) != '\n' && c != ' ' && c != '\t' && c != '\0')
+ p++;
+
+ *buf = malloc(p+1-start);
+ if(*buf == nil)
+ return nil;
+ memmove(*buf, start, p-start);
+ (*buf)[p-start] = '\0';
+
+ if(**buf == '$'){
+ s = getenv(*buf+1);
+ if(s == 0){
+ warn("can't read environment variable %q", *buf+1);
+ skipdir();
+ free(*buf);
+ return nil;
+ }
+ free(*buf);
+ *buf = s;
+ }
+ return p;
+}
+
+char*
+getmode(char *p, ulong *xmode)
+{
+ char *buf, *s;
+ ulong m;
+
+ *xmode = ~0;
+ p = getname(p, &buf);
+ if(p == nil)
+ return nil;
+
+ s = buf;
+ if(!*s || strcmp(s, "-") == 0)
+ return p;
+ m = 0;
+ if(*s == 'd'){
+ m |= DMDIR;
+ s++;
+ }
+ if(*s == 'a'){
+ m |= DMAPPEND;
+ s++;
+ }
+ if(*s == 'l'){
+ m |= DMEXCL;
+ s++;
+ }
+ if(s[0] < '0' || s[0] > '7'
+ || s[1] < '0' || s[1] > '7'
+ || s[2] < '0' || s[2] > '7'
+ || s[3]){
+ warn("bad mode specification %q", buf);
+ free(buf);
+ return p;
+ }
+ *xmode = m | strtoul(s, 0, 8);
+ free(buf);
+ return p;
+}
+
+void
+setusers(void)
+{
+ File file;
+ int m;
+
+ if(fskind != Kfs)
+ return;
+ m = modes;
+ modes = 1;
+ file.uid = "adm";
+ file.gid = "adm";
+ file.mode = DMDIR|0775;
+ file.new = "/adm";
+ file.elem = "adm";
+ file.old = 0;
+ setnames(&file);
+ strcpy(oldfile, file.new); /* Don't use root for /adm */
+ mkfile(&file);
+ file.new = "/adm/users";
+ file.old = users;
+ file.elem = "users";
+ file.mode = 0664;
+ setnames(&file);
+ if (file.old)
+ strcpy(oldfile, file.old); /* Don't use root for /adm/users */
+ mkfile(&file);
+ kfscmd("user");
+ mkfile(&file);
+ file.mode = DMDIR|0775;
+ file.new = "/adm";
+ file.old = "/adm";
+ file.elem = "adm";
+ setnames(&file);
+ strcpy(oldfile, file.old); /* Don't use root for /adm */
+ mkfile(&file);
+ modes = m;
+}
+
+void
+mountkfs(char *name)
+{
+ if(fskind != Kfs)
+ return;
+ sysfatal("no kfs: use -a or -d");
+}
+
+void
+kfscmd(char *cmd)
+{
+ char buf[4*1024];
+ int n;
+
+ if(fskind != Kfs)
+ return;
+ if(write(sfd, cmd, strlen(cmd)) != strlen(cmd)){
+ fprint(2, "%q: error writing %q: %r", prog, cmd);
+ return;
+ }
+ for(;;){
+ n = read(sfd, buf, sizeof buf - 1);
+ if(n <= 0)
+ return;
+ buf[n] = '\0';
+ if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0)
+ return;
+ if(strcmp(buf, "unknown command") == 0){
+ fprint(2, "%q: command %q not recognized\n", prog, cmd);
+ return;
+ }
+ }
+}
+
+void *
+emalloc(ulong n)
+{
+ void *p;
+
+ if((p = malloc(n)) == 0)
+ error("out of memory");
+ return p;
+}
+
+void
+error(char *fmt, ...)
+{
+ char buf[1024];
+ va_list arg;
+
+ sprint(buf, "%q: %q:%d: ", prog, proto, lineno);
+ va_start(arg, fmt);
+ vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%s\n", buf);
+ kfscmd("disallow");
+ kfscmd("sync");
+ exits(0);
+}
+
+void
+warn(char *fmt, ...)
+{
+ char buf[1024];
+ va_list arg;
+
+ sprint(buf, "%q: %q:%d: ", prog, proto, lineno);
+ va_start(arg, fmt);
+ vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%s\n", buf);
+}
+
+void
+printfile(File *f)
+{
+ if(f->old)
+ fprint(2, "%q from %q %q %q %lo\n", f->new, f->old, f->uid, f->gid, f->mode);
+ else
+ fprint(2, "%q %q %q %lo\n", f->new, f->uid, f->gid, f->mode);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %q [-aprvx] [-d root] [-n name] [-s source] [-u users] [-z n] proto ...\n", prog);
+ exits("usage");
+}