disk/mkfs, disk/mkext: add from Plan 9

R=rsc, rsc
http://codereview.appspot.com/6405057
This commit is contained in:
Russ Cox 2012-07-17 19:10:45 -04:00
parent f0add8ef24
commit d2173bb552
5 changed files with 1356 additions and 0 deletions

0
bin/disk/.placeholder Normal file
View file

187
man/man8/mkfs.8 Normal file
View file

@ -0,0 +1,187 @@
.TH MKFS 8
.SH NAME
mkfs, mkext \- archive or update a file system
.SH SYNOPSIS
.B disk/mkfs
.RB [ -aprvxU ]
.RB [ -d
.IR root ]
.RB [ -n
.IR name ]
.RB [ -s
.IR source ]
.RB [ -u
.IR users ]
.RB [ -z
.IR n ]
.I proto ...
.PP
.B disk/mkext
.RB [ -d
.IR name ]
.RB [ -u ]
.RB [ -h ]
.RB [ -v ]
.RB [ -x ]
.RB [ -T ]
.I file ...
.SH DESCRIPTION
.I Mkfs
copies files from the file tree
.I source
(default
.BR / )
to a
.B kfs
file system (see
.IR kfs (4)).
The kfs service is mounted on
.I root
(default
.BR /n/kfs ),
and
.B /adm/users
is copied to
.IB root /adm/users\f1.
The
.I proto
files are read
(see
.IR proto (2)
for their format)
and any files specified in them that are out of date are copied to
.BR /n/kfs .
.PP
.I Mkfs
copies only those files that are out of date.
Such a file is first copied into a temporary
file in the appropriate destination directory
and then moved to the destination file.
Files in the
.I kfs
file system that are not specified in the
.I proto
file
are not updated and not removed.
.PP
The options to
.I mkfs
are:
.TF "s source"
.TP
.B a
Instead of writing to a
.B kfs
file system, write an archive file to standard output, suitable for
.IR mkext .
All files in
.IR proto ,
not just those out of date, are archived.
.TP
.B x
For use with
.BR -a ,
this option writes a list of file names, dates, and sizes to standard output
rather than producing an archive file.
.TP
.BI "d " root
Copy files into the tree rooted at
.I root
(default
.BR /n/kfs ).
This option suppresses setting the
.B uid
and
.B gid
fields when copying files.
Use
.B -U
to reenable it.
.TP
.BI "n " name
Use
.RI kfs. name
as the name of the kfs service (default
.BR kfs ).
.TP
.B p
Update the permissions of a file even if it is up to date.
.TP
.B r
Copy all files.
.TP
.BI "s " source
Copy from files rooted at the tree
.IR source .
.TP
.BI "u " users
Copy file
.I users
into
.B /adm/users
in the new system.
.TP
.B v
Print the names of all of the files as they are copied.
.TP
.BI "z " n
Copy files assuming kfs block
.I n
(default 1024)
bytes long.
If a block contains only 0-valued bytes, it is not copied.
.PD
.PP
.I Mkext
unpacks archive files made by the
.B -a
option of
.IR mkfs .
Each file on the command line is unpacked in one pass through the archive.
If the file is a directory,
all files and subdirectories of that directory are also unpacked.
When a file is unpacked, the entire path is created if it
does not exist.
If no files are specified, the entire archive is unpacked;
in this case, missing intermediate directories are not created.
The options are:
.TP
.B d
specifies a directory (default
.BR / )
to serve as the root of the unpacked file system.
.TP
.B u
sets the owners of the files created to correspond to
those in the archive and restores the modification times of the files.
.TP
.B T
restores only the modification times of the files.
.TP
.B v
prints the names and sizes of files as they are extracted.
.TP
.B h
prints headers for the files on standard output
instead of unpacking the files.
.PD
.SH EXAMPLES
.PP
Make an archive to establish a new file system:
.IP
.EX
disk/mkfs -a -u files/adm.users -s dist proto > arch
.EE
.PP
Unpack that archive onto a new file system:
.IP
.EX
disk/mkext -u -d /n/newfs < arch
.EE
.SH SOURCE
.B \*9/src/cmd/disk/mkfs.c
.br
.B \*9/src/cmd/disk/mkext.c
.SH "SEE ALSO"
.IR prep (8),
.IR tar (1)

318
src/cmd/disk/mkext.c Normal file
View file

@ -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");
}

11
src/cmd/disk/mkfile Normal file
View file

@ -0,0 +1,11 @@
<$PLAN9/src/mkhdr
TARG=\
mkext\
mkfs\
BIN=$BIN/disk
<$PLAN9/src/mkmany

840
src/cmd/disk/mkfs.c Normal file
View file

@ -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");
}