diff options
author | Russ Cox <rsc@swtch.com> | 2012-07-17 19:10:45 -0400 |
---|---|---|
committer | Russ Cox <rsc@swtch.com> | 2012-07-17 19:10:45 -0400 |
commit | d2173bb552d308d60a4e4a53cd3b8e0949b38dbc (patch) | |
tree | 196b3078e732ff170019734fb6990ceab2f80b01 /src/cmd/disk/mkfs.c | |
parent | f0add8ef24f83cb2085ef1c7534d16c57881e3f3 (diff) | |
download | plan9port-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/mkfs.c')
-rw-r--r-- | src/cmd/disk/mkfs.c | 840 |
1 files changed, 840 insertions, 0 deletions
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"); +} |