15 Maxsz = 0x7fffffff - Nhdr
25 typedef struct Lump Lump;
36 Lump l1 = {.l = &l1, .lp = &l1}, *lumps = &l1;
40 File *ldir, *fsig, *fwad;
46 "things", "linedefs", "sidedefs", "vertexes", "segs",
47 "ssectors", "nodes", "sectors", "reject", "blockmap"
51 strupr(char *s, char *p)
62 strlwr(char *s, char *p)
73 link(Lump *l, Lump *lp, int len)
80 fwad->length += Ndict + len;
92 fwad->length -= Ndict + (l->f != nil ? l->f->length : 0);
104 readlump(Lump *l, uchar *p, long n)
108 Bseek(wad, l->ofs, 0);
109 if(Bread(wad, p, n) != n)
110 fprint(2, "readlump: short read: %r\n");
114 loadlump(File *f, ulong n)
121 l->buf = emalloc9p(n);
124 readlump(l, l->buf, f->length);
132 for(dir=lp->f, f=lp->l->f; lp->l!=lumps; lp=lp->l, f=lp->l->f)
133 if(f->parent != dir && f->parent->parent != dir)
135 if(lp->type == LTend && lp->f->parent == dir)
145 for(p=mapn; p<mapn+nelem(mapn); p++)
146 if(strcmp(s, *p) == 0)
152 sortmap(Lump *lp, Lump *l)
156 i = nextmaplump(l->f->name);
157 for(; lp->l != lumps; lp=lp->l){
158 ip = nextmaplump(lp->l->f->name);
168 return nextmaplump(s) >= 0;
174 if(strncmp(s, "map", 3) == 0)
175 return isdigit(s[3]) && isdigit(s[4]);
176 return s[0] == 'e' && isdigit(s[1])
177 && s[2] == 'm' && isdigit(s[3]);
181 ismarkname(char *s, char *m)
186 if(p == nil || p[strlen(m)] != 0)
194 validname(char *s, File *dir, int *type, int isnew, int isdir)
202 if(n < 1 || n > sizeof(lp->name)-1){
203 werrstr("invalid lump name");
206 for(p=s+n-1; c=*p, p-->=s;)
207 if(!isprint(c) || isupper(c) || c == '/'){
208 werrstr("invalid char %c in filename", c);
211 if(isnew && !ismaplump(s))
212 for(lp=lumps->l; lp!=lumps; lp=lp->l)
213 if(cistrcmp(s, lp->name) == 0){
214 werrstr("duplicate non map lump");
222 werrstr("map marker not a directory");
224 }else if(dir != fs.tree->root){
225 werrstr("nested map directory");
229 }else if(ismarkname(s, "_end")){
231 if(dir == fs.tree->root || lp == nil || lp->type == LTmap){
232 werrstr("orphaned end marker");
236 }else if(ismarkname(s, "_start")){
239 werrstr("not allowed");
243 }else if(isnew && isdir){
246 werrstr("marker name too long");
250 if(dir->parent != fs.tree->root){
251 werrstr("start marker nested too deep");
253 }else if(lp != nil && lp->type == LTmap){
254 werrstr("start marker within map directory");
258 }else if(ismaplump(s) ^ (lp != nil && lp->type == LTmap)){
259 werrstr("map lump outside of map directory");
266 endldir(Lump *lp, Lump *le)
268 char *s, name[sizeof lp->name];
272 l = emalloc9p(sizeof *l);
273 strcpy(l->name, lp->name);
274 s = strrchr(l->name, '_');
276 strlwr(name, l->name);
277 fprint(2, "adding end marker %s\n", l->name);
278 if(!validname(name, lp->f, &l->type, 1, 0) || l->type != LTend)
280 f = createfile(lp->f, name, nil, lp->f->mode & 0666, l);
293 accessfile(File *f, int mode)
295 f->atime = time(nil);
311 e = "permission denied";
314 if(r->d.mode != ~0 || r->d.gid[0] != 0)
318 if(r->d.length != ~0 && r->d.length != f->length){
319 if(f == fsig || f->mode & DMDIR)
321 if(!hasperm(f, r->fid->uid, AWRITE))
324 e = "invalid file length";
327 if(fwad->length - f->length + r->d.length >= Maxsz){
328 e = "lump size exceeds wad limit";
332 if(r->d.name[0] != 0 && strcmp(r->d.name, f->name) != 0){
338 if(!hasperm(fp, r->fid->uid, AWRITE))
340 if(!validname(r->d.name, fp, &type, 1, f->mode & DMDIR)){
344 if(lp->type != type){
345 e = "incompatible lump type";
349 fp = walkfile(fp, r->d.name);
351 e = "file already exists";
356 if(r->d.length != ~0 && r->d.length != f->length){
358 loadlump(f, r->d.length);
359 fwad->length += r->d.length - f->length;
360 f->length = r->d.length;
362 if(r->d.name[0] != 0 && strcmp(r->d.name, f->name) != 0){
364 f->name = estrdup9p(r->d.name);
365 strupr(lp->name, f->name);
366 if(lp->type == LTmrk)
367 strcat(lp->name, "_START");
369 accessfile(f, AWRITE);
371 f->mtime = r->d.mtime;
386 if(f == fsig || f == fwad){
387 respond(r, "not allowed");
389 }else if(lp->l->f != nil && lp->l->f->parent == f){
390 respond(r, "has children");
399 writesig(uchar *buf, char *s, vlong n)
401 if(n > Nsig+1 || strncmp(s, "IWAD", Nsig) != 0 && strncmp(s, "PWAD", Nsig) != 0)
402 return "invalid wad signature";
403 memcpy(buf, s, Nsig);
411 vlong n, m, ofs, end;
417 ofs = r->ifcall.offset;
418 if(f->mode & DMAPPEND)
423 respond(r, writesig(l->buf, r->ifcall.data, n));
427 loadlump(f, end + Nbuf);
429 m = l->nbuf + Nbuf > end ? l->nbuf + Nbuf : end;
430 if(fwad->length - l->nbuf + m >= Maxsz){
431 respond(r, "lump size exceeds wad limit");
434 l->buf = erealloc9p(l->buf, m);
437 memcpy(l->buf + ofs, r->ifcall.data, n);
443 accessfile(f, AWRITE);
458 l->buf = emalloc9p(fwad->length);
461 memcpy(p, lp->buf, 4), p += 4;
462 PBIT32(p, nlmp), p += 8;
463 for(lp=lumps->l; lp!=lumps; p+=n, lp=lp->l){
466 memcpy(p, lp->buf, n);
470 PBIT32(l->buf + 8, p - l->buf);
472 for(lp=lumps->l; lp!=lumps; ofs+=n, lp=lp->l){
474 PBIT32(p, ofs), p += 4;
475 PBIT32(p, n), p += 4;
476 memcpy(p, lp->name, 8), p += 8;
490 ofs = r->ifcall.offset + l->ofs;
491 end = l->ofs + f->length;
500 if(f == fwad && dirty)
503 memcpy(r->ofcall.data, l->buf+ofs, n);
506 n = Bread(wad, r->ofcall.data, n);
512 accessfile(f, AREAD);
518 addlump(Lump *l, File *dir)
523 if(dir != fs.tree->root){
525 lp = lp->type == LTmap ? sortmap(lp, l) : lastlump(lp);
527 if(l->type == LTend && lp->l->type == LTend && lp->l->f->parent == dir){
528 werrstr("an end marker already exists");
532 if(l->type == LTmrk){
533 strcat(l->name, "_START");
534 if(endldir(l, l) < 0)
536 }else if(l->type == LTreg){
537 l->buf = emalloc9p(Nbuf);
545 createlump(char *s, File *dir, int ismark)
550 if(!validname(s, dir, &type, 1, ismark))
552 l = emalloc9p(sizeof *l);
568 p = p & ~0777 | p & f->mode & 0777;
570 p = p & ~0666 | p & f->mode & 0666;
571 l = createlump(r->ifcall.name, f, p & DMDIR);
574 f = createfile(f, r->ifcall.name, r->fid->uid, p, l);
580 if(addlump(l, r->fid->file) < 0){
585 r->ofcall.qid = f->qid;
598 if((f->mode & DMAPPEND) == 0 && (r->ifcall.mode & OTRUNC) != 0
600 fwad->length -= f->length;
608 fsdestroyfile(File *f)
623 get32(Biobuf *bf, u32int *v)
628 n = Bread(bf, u, sizeof u);
636 replacefile(File *dir, char *fname, int mode, Lump *l)
641 f = walkfile(dir, fname);
644 if(removefile(f) < 0)
646 f = createfile(dir, fname, nil, mode, l);
651 addsigfile(char *sig)
658 l = emalloc9p(sizeof *l);
659 l->buf = (uchar *)estrdup9p(sig);
661 f = createfile(fs.tree->root, "SIG", nil, rdonly ? 0444 : 0666, l);
663 sysfatal("addsigfile: %r");
676 l = emalloc9p(sizeof *l);
677 f = createfile(fs.tree->root, "WAD", nil, 0444, l);
679 sysfatal("addwadfile: %r");
693 if(ldir == fs.tree->root)
696 if(lp->type != LTmap && endldir(lp, lastlump(lp)) < 0)
697 fprint(2, "checkends: %r\n");
703 addfile(Lump *l, u32int *len, int mode)
706 char fname[sizeof l->name], *s;
711 if(get32(wad, &l->ofs) < 0 || get32(wad, len) < 0)
713 if(Bread(wad, l->name, sizeof(l->name)-1) != sizeof(l->name)-1)
715 strlwr(fname, l->name);
718 err = !validname(fname, ldir, &l->type, 0, 0);
722 ldir = fs.tree->root;
723 if(err && lp != nil && lp->type != LTmap){
724 fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
725 if(endldir(lp, lastlump(lp)) < 0)
726 fprint(2, "endldir: %r\n");
733 if(lp != nil && lp->type == LTmap){
735 ldir = fs.tree->root;
737 fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
738 if(endldir(lp, lastlump(lp)) < 0)
743 s = strrchr(fname, '_');
758 fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
760 ldir = fs.tree->root;
767 f = createfile(ldir, fname, nil, mode, l);
769 fprint(2, "createfile %s: %r\n", l->name);
772 f = replacefile(ldir, fname, mode, l);
778 else if(l->type == LTend)
794 mode = rdonly ? 0444 : 0666;
795 ldir = fs.tree->root;
796 for(n=0, ne=nlmp, nlmp=0; n<ne; n++){
797 l = emalloc9p(sizeof *l);
798 if(addfile(l, &len, mode) < 0){
799 fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, len);
803 link(l, lumps->lp, len);
814 n = Bread(wad, sig, Nsig);
816 sysfatal("readwad: short read: %r");
818 if(strcmp(sig, "IWAD") != 0 && strcmp(sig, "PWAD") != 0)
819 sysfatal("invalid wad signature");
820 if(get32(wad, &nlmp) < 0 || get32(wad, &dictofs) < 0)
821 sysfatal("wadinfo: %r");
822 Bseek(wad, dictofs, 0);
828 fprint(2, "usage: %s [-Dr] [-m mtpt] [-S srvname] [wad]\n", argv0);
833 main(int argc, char **argv)
836 char *mtpt, *srvname, sig[Nsig+1] = "PWAD";
843 case 'D': chatty9p++; break;
844 case 'S': srvname = EARGF(usage()); break;
845 case 'm': mtpt = EARGF(usage()); break;
846 case 'r': rdonly++; p &= ~0222; fl &= ~MCREATE; break;
850 wad = Bopen(*argv, OREAD);
852 sysfatal("Bopen: %r");
855 fs.tree = alloctree(nil, nil, p, fsdestroyfile);
859 postmountsrv(&fs, srvname, mtpt, fl);