#include <u.h>#include <libc.h>#include <disk.h>#include <auth.h>#include <bio.h> enum{ LEN = 4096, /* * types of destination file sytems */ Fs = 0, Archive,}; void protowarn(char *msg, void *);void protoenum(char *new, char *old, Dir *d, void *); void arch(Dir*);void copy(Dir*);void error(char *, ...);void mkdir(Dir*);int uptodate(Dir*, char*);void usage(void);void warn(char *, ...); Biobufhdr bout; /* stdout when writing archive */uchar boutbuf[2*LEN];char newfile[LEN];char oldfile[LEN];char *proto;char *oldroot;char *newroot;char *prog = "mkfs";char *buf;char *zbuf;int buflen = 1024-8;int verb;int modes;int ream;int debug;int xflag;int oflag;int sfd;int fskind; /* Fs, Archive */int setuid; /* on Fs: set uid and gid? */char *user; voidmain(int argc, char **argv){ int i, errs; quotefmtinstall(); user = getuser(); oldroot = ""; newroot = "/n/newfs"; fskind = Fs; ARGBEGIN{ case 'a': fskind = Archive; newroot = ""; Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf); break; case 'd': if(fskind != Fs) { fprint(2, "cannot use -d with -a\n"); usage(); } fskind = Fs; newroot = EARGF(usage()); break; case 'D': debug = 1; break; case 'p': modes = 1; break; case 'r': ream = 1; break; case 's': oldroot = EARGF(usage()); break; case 'U': setuid = 1; break; case 'v': verb = 1; break; case 'o': oflag = 1; break; case 'x': xflag = 1; break; case 'z': buflen = atoi(EARGF(usage()))-8; break; default: usage(); }ARGEND if(!argc) usage(); if((xflag || oflag) && fskind != Archive){ fprint(2, "cannot use -x and -o without -a\n"); usage(); } buf = malloc(buflen); zbuf = malloc(buflen); memset(zbuf, 0, buflen); errs = 0; for(i = 0; i < argc; i++){ proto = argv[i]; fprint(2, "processing %q\n", proto); if(rdproto(proto, oldroot, protoenum, protowarn, nil) < 0){ fprint(2, "%q: can't open %q: skipping\n", prog, proto); errs++; continue; } } fprint(2, "file system made\n"); if(errs) exits("skipped protos"); if(fskind == Archive){ if(!xflag && !oflag) Bprint(&bout, "end of archive\n"); Bterm(&bout); } exits(0);} /* * check if file to is up to date with * respect to the file represented by df */intuptodate(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;} voidcopy(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);} voidmkdir(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);} voidarch(Dir *d){ Bprint(&bout, "%q %luo %q %q %lud %lld\n", newfile, d->mode, d->uid, d->gid, d->mtime, d->length);} voidprotowarn(char *msg, void *){ warn("%s", msg);} voidprotoenum(char *new, char *old, Dir *d, void *){ Dir nd; sprint(newfile, "%s%s", newroot, new); sprint(oldfile, "%s", old); if(oflag){ if(!(d->mode & DMDIR)) Bprint(&bout, "%q\n", cleanname(oldfile)); return; } if(xflag){ Bprint(&bout, "%q\t%ld\t%lld\n", new, d->mtime, d->length); return; } if(verb && (fskind == Archive || ream)) fprint(2, "%q\n", new); if(fskind == Fs && !setuid){ d->uid = ""; d->gid = ""; } if(!uptodate(d, newfile)){ if(verb && (fskind != Archive && ream == 0)) fprint(2, "%q\n", 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", new); if(dirwstat(newfile, &nd) < 0) warn("can't set modes for %q: %r", new); nulldir(&nd); nd.uid = d->uid; dirwstat(newfile, &nd); }} voiderror(char *fmt, ...){ char buf[1024]; va_list arg; sprint(buf, "%q: %q: ", prog, proto); va_start(arg, fmt); vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%s\n", buf); exits(0);} voidwarn(char *fmt, ...){ char buf[1024]; va_list arg; sprint(buf, "%q: %q: ", prog, proto); va_start(arg, fmt); vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%s\n", buf);} voidusage(void){ fprint(2, "usage: %q [-adprvoxUD] [-d root] [-s source] [-z n] proto ...\n", prog); exits("usage");}
#include <u.h>
#include <libc.h>
#include <disk.h>
#include <auth.h>
#include <bio.h>
enum{
LEN = 4096,
/*
* types of destination file sytems
*/
Fs = 0,
Archive,
};
void protowarn(char *msg, void *);
void protoenum(char *new, char *old, Dir *d, void *);
void arch(Dir*);
void copy(Dir*);
void error(char *, ...);
void mkdir(Dir*);
int uptodate(Dir*, char*);
void usage(void);
void warn(char *, ...);
Biobufhdr bout; /* stdout when writing archive */
uchar boutbuf[2*LEN];
char newfile[LEN];
char oldfile[LEN];
char *proto;
char *oldroot;
char *newroot;
char *prog = "mkfs";
char *buf;
char *zbuf;
int buflen = 1024-8;
int verb;
int modes;
int ream;
int debug;
int xflag;
int oflag;
int sfd;
int fskind; /* Fs, Archive */
int setuid; /* on Fs: set uid and gid? */
char *user;
void
main(int argc, char **argv)
{
int i, errs;
quotefmtinstall();
user = getuser();
oldroot = "";
newroot = "/n/newfs";
fskind = Fs;
ARGBEGIN{
case 'a':
fskind = Archive;
newroot = "";
Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf);
break;
case 'd':
if(fskind != Fs) {
fprint(2, "cannot use -d with -a\n");
usage();
}
fskind = Fs;
newroot = EARGF(usage());
break;
case 'D':
debug = 1;
break;
case 'p':
modes = 1;
break;
case 'r':
ream = 1;
break;
case 's':
oldroot = EARGF(usage());
break;
case 'U':
setuid = 1;
break;
case 'v':
verb = 1;
break;
case 'o':
oflag = 1;
break;
case 'x':
xflag = 1;
break;
case 'z':
buflen = atoi(EARGF(usage()))-8;
break;
default:
usage();
}ARGEND
if(!argc)
usage();
if((xflag || oflag) && fskind != Archive){
fprint(2, "cannot use -x and -o without -a\n");
usage();
}
buf = malloc(buflen);
zbuf = malloc(buflen);
memset(zbuf, 0, buflen);
errs = 0;
for(i = 0; i < argc; i++){
proto = argv[i];
fprint(2, "processing %q\n", proto);
if(rdproto(proto, oldroot, protoenum, protowarn, nil) < 0){
fprint(2, "%q: can't open %q: skipping\n", prog, proto);
errs++;
continue;
}
}
fprint(2, "file system made\n");
if(errs)
exits("skipped protos");
if(fskind == Archive){
if(!xflag && !oflag)
Bprint(&bout, "end of archive\n");
Bterm(&bout);
}
exits(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);
}
void
protowarn(char *msg, void *)
{
warn("%s", msg);
}
void
protoenum(char *new, char *old, Dir *d, void *)
{
Dir nd;
sprint(newfile, "%s%s", newroot, new);
sprint(oldfile, "%s", old);
if(oflag){
if(!(d->mode & DMDIR))
Bprint(&bout, "%q\n", cleanname(oldfile));
return;
}
if(xflag){
Bprint(&bout, "%q\t%ld\t%lld\n", new, d->mtime, d->length);
return;
}
if(verb && (fskind == Archive || ream))
fprint(2, "%q\n", new);
if(fskind == Fs && !setuid){
d->uid = "";
d->gid = "";
}
if(!uptodate(d, newfile)){
if(verb && (fskind != Archive && ream == 0))
fprint(2, "%q\n", 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", new);
if(dirwstat(newfile, &nd) < 0)
warn("can't set modes for %q: %r", new);
nulldir(&nd);
nd.uid = d->uid;
dirwstat(newfile, &nd);
}
}
void
error(char *fmt, ...)
{
char buf[1024];
va_list arg;
sprint(buf, "%q: %q: ", prog, proto);
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: %q: ", prog, proto);
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: %q [-adprvoxUD] [-d root] [-s source] [-z n] proto ...\n", prog);
exits("usage");
}