add games/wadfs
This commit is contained in:
parent
4be612946f
commit
16ef6e5596
3 changed files with 1054 additions and 0 deletions
192
sys/man/4/wadfs
Normal file
192
sys/man/4/wadfs
Normal file
|
@ -0,0 +1,192 @@
|
|||
.TH WADFS 4
|
||||
.SH NAME
|
||||
wadfs \- WAD file system
|
||||
.SH SYNOPSIS
|
||||
.B wadfs
|
||||
[
|
||||
.B -Dr
|
||||
] [
|
||||
.B -m
|
||||
.I mtpt
|
||||
] [
|
||||
.B -S
|
||||
.I srvname
|
||||
] [
|
||||
.I WAD
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.I Wadfs
|
||||
serves a file tree mounted at
|
||||
.I mtpt
|
||||
(default
|
||||
.BR /mnt/wad )
|
||||
that provides access to a
|
||||
.I WAD
|
||||
file's contents.
|
||||
.PP
|
||||
The command line options are:
|
||||
.TF "-S srvname"
|
||||
.TP
|
||||
.B -D
|
||||
Enable 9P debugging messages.
|
||||
.TP
|
||||
.B -r
|
||||
Set read-only file tree.
|
||||
.TP
|
||||
.BI -S \ srvname
|
||||
Post channel on
|
||||
.RI /srv/ srvname .
|
||||
.TP
|
||||
.BI -m \ mtpt
|
||||
Set mountpoint.
|
||||
.PD
|
||||
.PP
|
||||
A
|
||||
.I WAD
|
||||
is a concatenation of uncompressed files, referred to as lumps.
|
||||
A lump may contain either data,
|
||||
or be used as a marker to indicate the beginning or end of a section,
|
||||
segregating lumps of the same format.
|
||||
.PP
|
||||
.I Wadfs
|
||||
represents section start markers as directories,
|
||||
and regular lumps and end markers as files.
|
||||
For convenience, lump file names are in lower case,
|
||||
and are translated to the upper case internally.
|
||||
.PP
|
||||
At startup, if the path to a
|
||||
.I WAD
|
||||
file is provided as argument,
|
||||
.I wadfs
|
||||
will attempt to parse it and construct a file tree.
|
||||
Otherwise,
|
||||
.I wadfs
|
||||
starts with a blank tree instead.
|
||||
.PP
|
||||
Two additional files are provided in the file system's root directory:
|
||||
.L SIG
|
||||
and
|
||||
.LR WAD .
|
||||
Reading from and writing to
|
||||
.L SIG
|
||||
allows accessing and changing the
|
||||
.IR WAD 's
|
||||
type.
|
||||
The only possible values are
|
||||
.L PWAD
|
||||
(the default) and
|
||||
.LR IWAD .
|
||||
.PP
|
||||
.L WAD
|
||||
returns the new
|
||||
.I WAD
|
||||
file resulting from the recompilation of the lump tree.
|
||||
.SS "WAD file structure"
|
||||
There are few restrictions on the structure of
|
||||
.I WAD
|
||||
files.
|
||||
Excepting maps, sections can nest and may have no end marker,
|
||||
or one named differently than the section itself.
|
||||
Regular sections typically have one-letter names,
|
||||
and nested sections use the same name appended by a digit.
|
||||
By convention,
|
||||
lump names may only contain visible printing
|
||||
.SM ASCII
|
||||
characters,
|
||||
excepting lower-case letters.
|
||||
Map sections do not end at a marker but at the next non map lump,
|
||||
and use hardcoded names, depending on game version.
|
||||
.PP
|
||||
.I Wadfs
|
||||
imposes a number of additional restrictions on structure and naming:
|
||||
.IP • 3
|
||||
Lump names may not contain upper-case letters and the
|
||||
.L /
|
||||
character.
|
||||
.IP •
|
||||
A map section may only contain map lumps, which use hardcoded names.
|
||||
Ordering is significant, but is handled automatically.
|
||||
Map sections may not nest.
|
||||
.IP •
|
||||
Regular sections may not nest beyond one level,
|
||||
and may not contain more than one end marker.
|
||||
End markers may not exist outside of a section.
|
||||
Directory names omit the start marker's
|
||||
.L "_START"
|
||||
suffix.
|
||||
.IP •
|
||||
Excepting map lumps, no two lumps, including markers,
|
||||
may have the same name.
|
||||
.IP •
|
||||
Once created, a lump may not be renamed so as to change its type.
|
||||
.SS "Error recovery"
|
||||
Upon parsing the initial
|
||||
.I WAD
|
||||
file, if one of the restrictions for
|
||||
.I WAD
|
||||
file structure outlined in the sections above is not respected,
|
||||
a warning is issued, and the offending lump is potentially skipped.
|
||||
Some recovery is attempted,
|
||||
but one must systematically recheck the tree.
|
||||
When duplicate non marker lumps are encountered,
|
||||
each will overwrite the previous entry.
|
||||
.SH EXAMPLES
|
||||
Open
|
||||
.B doom2.wad
|
||||
and play a MUS file:
|
||||
.IP
|
||||
.EX
|
||||
% wadfs /sys/games/lib/doom/doom2.wad
|
||||
createfile SW18_7: file already exists
|
||||
% games/mus /mnt/wad/d_romero | games/midi
|
||||
.EE
|
||||
.PP
|
||||
Now create a blank
|
||||
.IR WAD ,
|
||||
then one section
|
||||
.LR FF ;
|
||||
copy a flat from
|
||||
.B doom2.wad
|
||||
to the directory,
|
||||
then rename the end marker to
|
||||
.L F_END
|
||||
to have the
|
||||
.B doom
|
||||
engine find the flat;
|
||||
finally, compile and save the new
|
||||
.I WAD
|
||||
file.
|
||||
.IP
|
||||
.EX
|
||||
% wadfs -m /mnt/wad2
|
||||
% cd /mnt/wad2
|
||||
% mkdir ff
|
||||
adding end marker FF_END
|
||||
% cp ../wad/f/f1/f_sky1 ff/
|
||||
% mv ff/ff_end ff/f_end
|
||||
% cp WAD /sys/games/lib/doom/sky.wad
|
||||
.EE
|
||||
.SH SOURCE
|
||||
.B /sys/src/games/wadfs.c
|
||||
.SH "SEE ALSO"
|
||||
.IR games (1),
|
||||
.IR mus (1)
|
||||
.SH HISTORY
|
||||
.I Wadfs
|
||||
first appeared in 9front (August, 2017).
|
||||
.SH BUGS
|
||||
Many
|
||||
.I WAD
|
||||
files in the wild do not conform to all the rules exposed above,
|
||||
in particular ones using
|
||||
.SM DeHackEd
|
||||
engine modifications.
|
||||
.IR WAD 's
|
||||
using end markers outside of a section,
|
||||
typically
|
||||
.LR F_END ,
|
||||
will lose them.
|
||||
.PP
|
||||
Repairing broken
|
||||
.I WAD
|
||||
files can be a pain.
|
|
@ -15,6 +15,7 @@ TARG=4s\
|
|||
packet\
|
||||
mandel\
|
||||
midi\
|
||||
wadfs\
|
||||
|
||||
OFILES=
|
||||
HFILES=
|
||||
|
|
861
sys/src/games/wadfs.c
Normal file
861
sys/src/games/wadfs.c
Normal file
|
@ -0,0 +1,861 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
#include <fcall.h>
|
||||
#include <thread.h>
|
||||
#include <9p.h>
|
||||
#include <bio.h>
|
||||
|
||||
enum{
|
||||
Nsig = 4,
|
||||
Nhdr = Nsig+4+4,
|
||||
Ndict = 4+4+8,
|
||||
Nname = 8,
|
||||
Nbuf = 8192,
|
||||
Maxsz = 0x7fffffff - Nhdr
|
||||
};
|
||||
|
||||
enum{
|
||||
LTnil,
|
||||
LTreg,
|
||||
LTmap,
|
||||
LTmrk,
|
||||
LTend
|
||||
};
|
||||
typedef struct Lump Lump;
|
||||
struct Lump{
|
||||
char name[Nname+1];
|
||||
u32int ofs;
|
||||
uchar *buf;
|
||||
ulong nbuf;
|
||||
int type;
|
||||
File *f;
|
||||
Lump *l;
|
||||
Lump *lp;
|
||||
};
|
||||
Lump l1 = {.l = &l1, .lp = &l1}, *lumps = &l1;
|
||||
|
||||
Biobuf *wad;
|
||||
u32int nlmp;
|
||||
File *ldir, *fsig, *fwad;
|
||||
int rdonly, dirty;
|
||||
|
||||
Srv fs;
|
||||
|
||||
char *mapn[] = {
|
||||
"things", "linedefs", "sidedefs", "vertexes", "segs",
|
||||
"ssectors", "nodes", "sectors", "reject", "blockmap"
|
||||
};
|
||||
|
||||
void
|
||||
strupr(char *s, char *p)
|
||||
{
|
||||
char c;
|
||||
|
||||
do{
|
||||
c = *p++;
|
||||
*s++ = toupper(c);
|
||||
}while(c != 0);
|
||||
}
|
||||
|
||||
void
|
||||
strlwr(char *s, char *p)
|
||||
{
|
||||
char c;
|
||||
|
||||
do{
|
||||
c = *p++;
|
||||
*s++ = tolower(c);
|
||||
}while(c != 0);
|
||||
}
|
||||
|
||||
void
|
||||
link(Lump *l, Lump *lp, int len)
|
||||
{
|
||||
l->lp = lp;
|
||||
l->l = lp->l;
|
||||
lp->l->lp = l;
|
||||
lp->l = l;
|
||||
nlmp++;
|
||||
fwad->length += Ndict + len;
|
||||
}
|
||||
|
||||
void
|
||||
unlink(Lump *l)
|
||||
{
|
||||
if(l->l == nil)
|
||||
return;
|
||||
l->lp->l = l->l;
|
||||
l->l->lp = l->lp;
|
||||
l->l = nil;
|
||||
nlmp--;
|
||||
fwad->length -= Ndict + (l->f != nil ? l->f->length : 0);
|
||||
}
|
||||
|
||||
void
|
||||
freelump(Lump *l)
|
||||
{
|
||||
unlink(l);
|
||||
free(l->buf);
|
||||
free(l);
|
||||
}
|
||||
|
||||
void
|
||||
readlump(Lump *l, uchar *p, long n)
|
||||
{
|
||||
if(n <= 0)
|
||||
return;
|
||||
Bseek(wad, l->ofs, 0);
|
||||
if(Bread(wad, p, n) != n)
|
||||
fprint(2, "readlump: short read: %r\n");
|
||||
}
|
||||
|
||||
void
|
||||
loadlump(File *f, ulong n)
|
||||
{
|
||||
Lump *l;
|
||||
|
||||
l = f->aux;
|
||||
if(f->length > n)
|
||||
n = f->length;
|
||||
l->buf = emalloc9p(n);
|
||||
l->nbuf = n;
|
||||
l->ofs = 0;
|
||||
readlump(l, l->buf, f->length);
|
||||
}
|
||||
|
||||
Lump *
|
||||
lastlump(Lump *lp)
|
||||
{
|
||||
File *f, *dir;
|
||||
|
||||
for(dir=lp->f, f=lp->l->f; lp->l!=lumps; lp=lp->l, f=lp->l->f)
|
||||
if(f->parent != dir && f->parent->parent != dir)
|
||||
break;
|
||||
if(lp->type == LTend && lp->f->parent == dir)
|
||||
lp = lp->lp;
|
||||
return lp;
|
||||
}
|
||||
|
||||
int
|
||||
nextmaplump(char *s)
|
||||
{
|
||||
char **p;
|
||||
|
||||
for(p=mapn; p<mapn+nelem(mapn); p++)
|
||||
if(strcmp(s, *p) == 0)
|
||||
return p-mapn;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Lump *
|
||||
sortmap(Lump *lp, Lump *l)
|
||||
{
|
||||
int ip, i;
|
||||
|
||||
i = nextmaplump(l->f->name);
|
||||
for(; lp->l != lumps; lp=lp->l){
|
||||
ip = nextmaplump(lp->l->f->name);
|
||||
if(ip < 0 || ip > i)
|
||||
break;
|
||||
}
|
||||
return lp;
|
||||
}
|
||||
|
||||
int
|
||||
ismaplump(char *s)
|
||||
{
|
||||
return nextmaplump(s) >= 0;
|
||||
}
|
||||
|
||||
int
|
||||
ismapname(char *s)
|
||||
{
|
||||
if(strncmp(s, "map", 3) == 0)
|
||||
return isdigit(s[3]) && isdigit(s[4]);
|
||||
return s[0] == 'e' && isdigit(s[1])
|
||||
&& s[2] == 'm' && isdigit(s[3]);
|
||||
}
|
||||
|
||||
int
|
||||
ismarkname(char *s, char *m)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = strstr(s, m);
|
||||
if(p == nil || p[strlen(m)] != 0)
|
||||
return 0;
|
||||
if(p - s > 2)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
validname(char *s, File *dir, int *type, int isnew, int isdir)
|
||||
{
|
||||
int n;
|
||||
char c, *p;
|
||||
Lump *lp;
|
||||
|
||||
*type = LTnil;
|
||||
n = strlen(s);
|
||||
if(n < 1 || n > sizeof(lp->name)-1){
|
||||
werrstr("invalid lump name");
|
||||
return 0;
|
||||
}
|
||||
for(p=s+n-1; c=*p, p-->=s;)
|
||||
if(!isprint(c) || isupper(c) || c == '/'){
|
||||
werrstr("invalid char %c in filename", c);
|
||||
return 0;
|
||||
}
|
||||
if(isnew && !ismaplump(s))
|
||||
for(lp=lumps->l; lp!=lumps; lp=lp->l)
|
||||
if(cistrcmp(s, lp->name) == 0){
|
||||
werrstr("duplicate non map lump");
|
||||
return 0;
|
||||
}
|
||||
*type = LTreg;
|
||||
lp = dir->aux;
|
||||
if(ismapname(s)){
|
||||
*type = LTmap;
|
||||
if(isnew && !isdir){
|
||||
werrstr("map marker not a directory");
|
||||
return 0;
|
||||
}else if(dir != fs.tree->root){
|
||||
werrstr("nested map directory");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}else if(ismarkname(s, "_end")){
|
||||
*type = LTend;
|
||||
if(dir == fs.tree->root || lp == nil || lp->type == LTmap){
|
||||
werrstr("orphaned end marker");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}else if(ismarkname(s, "_start")){
|
||||
*type = LTmrk;
|
||||
if(isnew){
|
||||
werrstr("not allowed");
|
||||
return 0;
|
||||
}
|
||||
goto mrkchk;
|
||||
}else if(isnew && isdir){
|
||||
*type = LTmrk;
|
||||
if(n > 2){
|
||||
werrstr("marker name too long");
|
||||
return 0;
|
||||
}
|
||||
mrkchk:
|
||||
if(dir->parent != fs.tree->root){
|
||||
werrstr("start marker nested too deep");
|
||||
return 0;
|
||||
}else if(lp != nil && lp->type == LTmap){
|
||||
werrstr("start marker within map directory");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}else if(ismaplump(s) ^ (lp != nil && lp->type == LTmap)){
|
||||
werrstr("map lump outside of map directory");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
endldir(Lump *lp, Lump *le)
|
||||
{
|
||||
char *s, name[sizeof lp->name];
|
||||
Lump *l;
|
||||
File *f;
|
||||
|
||||
l = emalloc9p(sizeof *l);
|
||||
strcpy(l->name, lp->name);
|
||||
s = strrchr(l->name, '_');
|
||||
strcpy(s, "_END");
|
||||
strlwr(name, l->name);
|
||||
fprint(2, "adding end marker %s\n", l->name);
|
||||
if(!validname(name, lp->f, &l->type, 1, 0) || l->type != LTend)
|
||||
goto err;
|
||||
f = createfile(lp->f, name, nil, lp->f->mode & 0666, l);
|
||||
if(f == nil)
|
||||
goto err;
|
||||
closefile(f);
|
||||
l->f = f;
|
||||
link(l, le, 0);
|
||||
return 0;
|
||||
err:
|
||||
free(l);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
accessfile(File *f, int mode)
|
||||
{
|
||||
f->atime = time(nil);
|
||||
if(mode & AWRITE){
|
||||
f->mtime = f->atime;
|
||||
f->qid.vers++;
|
||||
dirty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fswstat(Req *r)
|
||||
{
|
||||
int type;
|
||||
char *e;
|
||||
File *f, *fp;
|
||||
Lump *lp;
|
||||
|
||||
e = "permission denied";
|
||||
if(rdonly)
|
||||
goto err;
|
||||
if(r->d.mode != ~0 || r->d.gid[0] != 0)
|
||||
goto err;
|
||||
f = r->fid->file;
|
||||
lp = f->aux;
|
||||
if(r->d.length != ~0 && r->d.length != f->length){
|
||||
if(f == fsig || f->mode & DMDIR)
|
||||
goto err;
|
||||
if(!hasperm(f, r->fid->uid, AWRITE))
|
||||
goto err;
|
||||
if(r->d.length < 0){
|
||||
e = "invalid file length";
|
||||
goto err;
|
||||
}
|
||||
if(fwad->length - f->length + r->d.length >= Maxsz){
|
||||
e = "lump size exceeds wad limit";
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
if(r->d.name[0] != 0 && strcmp(r->d.name, f->name) != 0){
|
||||
fp = f->parent;
|
||||
if(fp == nil){
|
||||
e = "orphaned file";
|
||||
goto err;
|
||||
}
|
||||
if(!hasperm(fp, r->fid->uid, AWRITE))
|
||||
goto err;
|
||||
if(!validname(r->d.name, fp, &type, 1, f->mode & DMDIR)){
|
||||
responderror(r);
|
||||
return;
|
||||
}
|
||||
if(lp->type != type){
|
||||
e = "incompatible lump type";
|
||||
goto err;
|
||||
}
|
||||
incref(fp);
|
||||
fp = walkfile(fp, r->d.name);
|
||||
if(fp != nil){
|
||||
e = "file already exists";
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if(r->d.length != ~0 && r->d.length != f->length){
|
||||
if(lp->buf == nil)
|
||||
loadlump(f, r->d.length);
|
||||
fwad->length += r->d.length - f->length;
|
||||
f->length = r->d.length;
|
||||
}
|
||||
if(r->d.name[0] != 0 && strcmp(r->d.name, f->name) != 0){
|
||||
free(f->name);
|
||||
f->name = estrdup9p(r->d.name);
|
||||
strupr(lp->name, f->name);
|
||||
if(lp->type == LTmrk)
|
||||
strcat(lp->name, "_START");
|
||||
}
|
||||
accessfile(f, AWRITE);
|
||||
if(r->d.mtime != ~0)
|
||||
f->mtime = r->d.mtime;
|
||||
respond(r, nil);
|
||||
return;
|
||||
err:
|
||||
respond(r, e);
|
||||
}
|
||||
|
||||
void
|
||||
fsremove(Req *r)
|
||||
{
|
||||
File *f;
|
||||
Lump *lp;
|
||||
|
||||
f = r->fid->file;
|
||||
lp = f->aux;
|
||||
if(f == fsig || f == fwad){
|
||||
respond(r, "not allowed");
|
||||
return;
|
||||
}else if(lp->l->f != nil && lp->l->f->parent == f){
|
||||
respond(r, "has children");
|
||||
return;
|
||||
}
|
||||
unlink(f->aux);
|
||||
dirty = 1;
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
char *
|
||||
writesig(uchar *buf, char *s, vlong n)
|
||||
{
|
||||
if(n > Nsig+1 || strncmp(s, "IWAD", Nsig) != 0 && strncmp(s, "PWAD", Nsig) != 0)
|
||||
return "invalid wad signature";
|
||||
memcpy(buf, s, Nsig);
|
||||
dirty = 1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
fswrite(Req *r)
|
||||
{
|
||||
vlong n, m, ofs, end;
|
||||
File *f;
|
||||
Lump *l;
|
||||
|
||||
f = r->fid->file;
|
||||
n = r->ifcall.count;
|
||||
ofs = r->ifcall.offset;
|
||||
if(f->mode & DMAPPEND)
|
||||
ofs = f->length;
|
||||
end = ofs + n;
|
||||
l = f->aux;
|
||||
if(f == fsig){
|
||||
respond(r, writesig(l->buf, r->ifcall.data, n));
|
||||
return;
|
||||
}
|
||||
if(l->buf == nil)
|
||||
loadlump(f, end + Nbuf);
|
||||
if(end > l->nbuf){
|
||||
m = l->nbuf + Nbuf > end ? l->nbuf + Nbuf : end;
|
||||
if(fwad->length - l->nbuf + m >= Maxsz){
|
||||
respond(r, "lump size exceeds wad limit");
|
||||
return;
|
||||
}
|
||||
l->buf = erealloc9p(l->buf, m);
|
||||
l->nbuf = m;
|
||||
}
|
||||
memcpy(l->buf + ofs, r->ifcall.data, n);
|
||||
m = end - f->length;
|
||||
if(m > 0){
|
||||
f->length += m;
|
||||
fwad->length += m;
|
||||
}
|
||||
accessfile(f, AWRITE);
|
||||
r->ofcall.count = n;
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
void
|
||||
makewad(void)
|
||||
{
|
||||
vlong n;
|
||||
uchar *p;
|
||||
u32int ofs;
|
||||
Lump *l, *lp;
|
||||
|
||||
l = fwad->aux;
|
||||
free(l->buf);
|
||||
l->buf = emalloc9p(fwad->length);
|
||||
p = l->buf;
|
||||
lp = fsig->aux;
|
||||
memcpy(p, lp->buf, 4), p += 4;
|
||||
PBIT32(p, nlmp), p += 8;
|
||||
for(lp=lumps->l; lp!=lumps; p+=n, lp=lp->l){
|
||||
n = lp->f->length;
|
||||
if(lp->buf != nil)
|
||||
memcpy(p, lp->buf, n);
|
||||
else
|
||||
readlump(lp, p, n);
|
||||
}
|
||||
PBIT32(l->buf + 8, p - l->buf);
|
||||
ofs = Nhdr;
|
||||
for(lp=lumps->l; lp!=lumps; ofs+=n, lp=lp->l){
|
||||
n = lp->f->length;
|
||||
PBIT32(p, ofs), p += 4;
|
||||
PBIT32(p, n), p += 4;
|
||||
memcpy(p, lp->name, 8), p += 8;
|
||||
}
|
||||
dirty = 0;
|
||||
}
|
||||
|
||||
void
|
||||
fsread(Req *r)
|
||||
{
|
||||
vlong n, ofs, end;
|
||||
File *f;
|
||||
Lump *l;
|
||||
|
||||
f = r->fid->file;
|
||||
l = f->aux;
|
||||
ofs = r->ifcall.offset + l->ofs;
|
||||
end = l->ofs + f->length;
|
||||
n = r->ifcall.count;
|
||||
if(ofs + n >= end)
|
||||
n = end - ofs;
|
||||
if(n <= 0){
|
||||
r->ofcall.count = 0;
|
||||
respond(r, nil);
|
||||
return;
|
||||
}
|
||||
if(f == fwad && dirty)
|
||||
makewad();
|
||||
if(l->buf != nil)
|
||||
memcpy(r->ofcall.data, l->buf+ofs, n);
|
||||
else{
|
||||
Bseek(wad, ofs, 0);
|
||||
n = Bread(wad, r->ofcall.data, n);
|
||||
if(n < 0){
|
||||
responderror(r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
accessfile(f, AREAD);
|
||||
r->ofcall.count = n;
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
int
|
||||
addlump(Lump *l, File *dir)
|
||||
{
|
||||
Lump *lp;
|
||||
|
||||
lp = lumps->lp;
|
||||
if(dir != fs.tree->root){
|
||||
lp = dir->aux;
|
||||
lp = lp->type == LTmap ? sortmap(lp, l) : lastlump(lp);
|
||||
}
|
||||
if(l->type == LTend && lp->l->type == LTend && lp->l->f->parent == dir){
|
||||
werrstr("an end marker already exists");
|
||||
return -1;
|
||||
}
|
||||
link(l, lp, 0);
|
||||
if(l->type == LTmrk){
|
||||
strcat(l->name, "_START");
|
||||
if(endldir(l, l) < 0)
|
||||
return -1;
|
||||
}else if(l->type == LTreg){
|
||||
l->buf = emalloc9p(Nbuf);
|
||||
l->nbuf = Nbuf;
|
||||
}
|
||||
dirty = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Lump *
|
||||
createlump(char *s, File *dir, int ismark)
|
||||
{
|
||||
int type;
|
||||
Lump *l;
|
||||
|
||||
if(!validname(s, dir, &type, 1, ismark))
|
||||
return nil;
|
||||
l = emalloc9p(sizeof *l);
|
||||
l->type = type;
|
||||
strupr(l->name, s);
|
||||
return l;
|
||||
}
|
||||
|
||||
void
|
||||
fscreate(Req *r)
|
||||
{
|
||||
int p;
|
||||
File *f;
|
||||
Lump *l;
|
||||
|
||||
f = r->fid->file;
|
||||
p = r->ifcall.perm;
|
||||
if(p & DMDIR)
|
||||
p = p & ~0777 | p & f->mode & 0777;
|
||||
else
|
||||
p = p & ~0666 | p & f->mode & 0666;
|
||||
l = createlump(r->ifcall.name, f, p & DMDIR);
|
||||
if(l == nil)
|
||||
goto err;
|
||||
f = createfile(f, r->ifcall.name, r->fid->uid, p, l);
|
||||
if(f == nil){
|
||||
free(l);
|
||||
goto err;
|
||||
}
|
||||
l->f = f;
|
||||
if(addlump(l, r->fid->file) < 0){
|
||||
removefile(f);
|
||||
goto err;
|
||||
}
|
||||
r->fid->file = f;
|
||||
r->ofcall.qid = f->qid;
|
||||
respond(r, nil);
|
||||
return;
|
||||
err:
|
||||
responderror(r);
|
||||
}
|
||||
|
||||
void
|
||||
fsopen(Req *r)
|
||||
{
|
||||
File *f;
|
||||
|
||||
f = r->fid->file;
|
||||
if((f->mode & DMAPPEND) == 0 && (r->ifcall.mode & OTRUNC) != 0
|
||||
&& f != fsig){
|
||||
fwad->length -= f->length;
|
||||
f->length = 0;
|
||||
dirty = 1;
|
||||
}
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
void
|
||||
fsdestroyfile(File *f)
|
||||
{
|
||||
freelump(f->aux);
|
||||
}
|
||||
|
||||
Srv fs = {
|
||||
.open = fsopen,
|
||||
.create = fscreate,
|
||||
.read = fsread,
|
||||
.write = fswrite,
|
||||
.remove = fsremove,
|
||||
.wstat = fswstat
|
||||
};
|
||||
|
||||
int
|
||||
get32(Biobuf *bf, u32int *v)
|
||||
{
|
||||
int n;
|
||||
uchar u[4];
|
||||
|
||||
n = Bread(bf, u, sizeof u);
|
||||
if(n != sizeof u)
|
||||
return -1;
|
||||
*v = GBIT32(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
File *
|
||||
replacefile(File *dir, char *fname, int mode, Lump *l)
|
||||
{
|
||||
File *f;
|
||||
|
||||
incref(dir);
|
||||
f = walkfile(dir, fname);
|
||||
if(f == nil)
|
||||
return nil;
|
||||
if(removefile(f) < 0)
|
||||
return nil;
|
||||
f = createfile(dir, fname, nil, mode, l);
|
||||
return f;
|
||||
}
|
||||
|
||||
void
|
||||
addsigfile(char *sig)
|
||||
{
|
||||
int n;
|
||||
Lump *l;
|
||||
File *f;
|
||||
|
||||
n = strlen(sig) + 1;
|
||||
l = emalloc9p(sizeof *l);
|
||||
l->buf = (uchar *)estrdup9p(sig);
|
||||
l->buf[n-1] = '\n';
|
||||
f = createfile(fs.tree->root, "SIG", nil, rdonly ? 0444 : 0666, l);
|
||||
if(f == nil)
|
||||
sysfatal("addsigfile: %r");
|
||||
else{
|
||||
fsig = f;
|
||||
f->length = n;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
addwadfile(void)
|
||||
{
|
||||
Lump *l;
|
||||
File *f;
|
||||
|
||||
l = emalloc9p(sizeof *l);
|
||||
f = createfile(fs.tree->root, "WAD", nil, 0444, l);
|
||||
if(f == nil)
|
||||
sysfatal("addwadfile: %r");
|
||||
else{
|
||||
fwad = f;
|
||||
f->length = Nhdr;
|
||||
}
|
||||
dirty++;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
checkends(void)
|
||||
{
|
||||
Lump *lp;
|
||||
|
||||
if(ldir == fs.tree->root)
|
||||
return;
|
||||
lp = ldir->aux;
|
||||
if(lp->type != LTmap && endldir(lp, lastlump(lp)) < 0)
|
||||
fprint(2, "checkends: %r\n");
|
||||
ldir = ldir->parent;
|
||||
checkends();
|
||||
}
|
||||
|
||||
int
|
||||
addfile(Lump *l, u32int *len, int mode)
|
||||
{
|
||||
int err;
|
||||
char fname[sizeof l->name], *s;
|
||||
Lump *lp;
|
||||
File *f;
|
||||
|
||||
*len = 0;
|
||||
if(get32(wad, &l->ofs) < 0 || get32(wad, len) < 0)
|
||||
return -1;
|
||||
if(Bread(wad, l->name, sizeof(l->name)-1) != sizeof(l->name)-1)
|
||||
return -1;
|
||||
strlwr(fname, l->name);
|
||||
|
||||
lp = ldir->aux;
|
||||
err = !validname(fname, ldir, &l->type, 0, 0);
|
||||
switch(l->type){
|
||||
case LTmap:
|
||||
closefile(ldir);
|
||||
ldir = fs.tree->root;
|
||||
if(err && lp != nil && lp->type != LTmap){
|
||||
fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
|
||||
if(endldir(lp, lastlump(lp)) < 0)
|
||||
fprint(2, "endldir: %r\n");
|
||||
}
|
||||
mode |= DMDIR|0111;
|
||||
*len = 0;
|
||||
break;
|
||||
case LTmrk:
|
||||
if(err){
|
||||
if(lp != nil && lp->type == LTmap){
|
||||
closefile(ldir);
|
||||
ldir = fs.tree->root;
|
||||
}else{
|
||||
fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
|
||||
if(endldir(lp, lastlump(lp)) < 0)
|
||||
return -1;
|
||||
ldir = ldir->parent;
|
||||
}
|
||||
}
|
||||
s = strrchr(fname, '_');
|
||||
*s = 0;
|
||||
mode |= DMDIR|0111;
|
||||
*len = 0;
|
||||
break;
|
||||
case LTend:
|
||||
if(err){
|
||||
ldir = ldir->parent;
|
||||
return -1;
|
||||
}
|
||||
*len = 0;
|
||||
break;
|
||||
case LTreg:
|
||||
if(err){
|
||||
if(ismaplump(fname))
|
||||
fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
|
||||
else
|
||||
ldir = fs.tree->root;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
f = createfile(ldir, fname, nil, mode, l);
|
||||
if(f == nil){
|
||||
fprint(2, "createfile %s: %r\n", l->name);
|
||||
if(mode & DMDIR)
|
||||
return -1;
|
||||
f = replacefile(ldir, fname, mode, l);
|
||||
if(f == nil)
|
||||
return -1;
|
||||
}
|
||||
if(mode & DMDIR)
|
||||
ldir = f;
|
||||
else if(l->type == LTend)
|
||||
ldir = ldir->parent;
|
||||
else
|
||||
closefile(f);
|
||||
f->length = *len;
|
||||
l->f = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
parsewad(void)
|
||||
{
|
||||
int n, ne, mode;
|
||||
u32int len;
|
||||
Lump *l;
|
||||
|
||||
mode = rdonly ? 0444 : 0666;
|
||||
ldir = fs.tree->root;
|
||||
for(n=0, ne=nlmp, nlmp=0; n<ne; n++){
|
||||
l = emalloc9p(sizeof *l);
|
||||
if(addfile(l, &len, mode) < 0){
|
||||
fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, len);
|
||||
free(l);
|
||||
continue;
|
||||
}
|
||||
link(l, lumps->lp, len);
|
||||
}
|
||||
checkends();
|
||||
}
|
||||
|
||||
void
|
||||
wadinfo(char *sig)
|
||||
{
|
||||
int n;
|
||||
u32int dictofs;
|
||||
|
||||
n = Bread(wad, sig, Nsig);
|
||||
if(n != Nsig)
|
||||
sysfatal("readwad: short read: %r");
|
||||
sig[4] = 0;
|
||||
if(strcmp(sig, "IWAD") != 0 && strcmp(sig, "PWAD") != 0)
|
||||
sysfatal("invalid wad signature");
|
||||
if(get32(wad, &nlmp) < 0 || get32(wad, &dictofs) < 0)
|
||||
sysfatal("wadinfo: %r");
|
||||
Bseek(wad, dictofs, 0);
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s [-Dr] [-m mtpt] [-S srvname] [wad]\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int fl, p;
|
||||
char *mtpt, *srvname, sig[Nsig+1] = "PWAD";
|
||||
|
||||
mtpt = "/mnt/wad";
|
||||
srvname = nil;
|
||||
fl = MREPL|MCREATE;
|
||||
p = DMDIR|0777;
|
||||
ARGBEGIN{
|
||||
case 'D': chatty9p++; break;
|
||||
case 'S': srvname = EARGF(usage()); break;
|
||||
case 'm': mtpt = EARGF(usage()); break;
|
||||
case 'r': rdonly++; p &= ~0222; fl &= ~MCREATE; break;
|
||||
default: usage();
|
||||
}ARGEND
|
||||
if(*argv != nil){
|
||||
wad = Bopen(*argv, OREAD);
|
||||
if(wad == nil)
|
||||
sysfatal("Bopen: %r");
|
||||
wadinfo(sig);
|
||||
}
|
||||
fs.tree = alloctree(nil, nil, p, fsdestroyfile);
|
||||
addsigfile(sig);
|
||||
addwadfile();
|
||||
parsewad();
|
||||
postmountsrv(&fs, srvname, mtpt, fl);
|
||||
exits(nil);
|
||||
}
|
Loading…
Reference in a new issue