changelog shortlog tags branches changeset files revisions annotate raw help

Mercurial > hg > plan9front / sys/src/games/wadfs.c

changeset 6125: 7e153241a8f6
child: 249468ce1239
author: qwx
date: Thu, 10 Aug 2017 11:39:18 +0200
permissions: -rw-r--r--
description: add games/wadfs
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
7 #include <bio.h>
8 
9 enum{
10  Nsig = 4,
11  Nhdr = Nsig+4+4,
12  Ndict = 4+4+8,
13  Nname = 8,
14  Nbuf = 8192,
15  Maxsz = 0x7fffffff - Nhdr
16 };
17 
18 enum{
19  LTnil,
20  LTreg,
21  LTmap,
22  LTmrk,
23  LTend
24 };
25 typedef struct Lump Lump;
26 struct Lump{
27  char name[Nname+1];
28  u32int ofs;
29  uchar *buf;
30  ulong nbuf;
31  int type;
32  File *f;
33  Lump *l;
34  Lump *lp;
35 };
36 Lump l1 = {.l = &l1, .lp = &l1}, *lumps = &l1;
37 
38 Biobuf *wad;
39 u32int nlmp;
40 File *ldir, *fsig, *fwad;
41 int rdonly, dirty;
42 
43 Srv fs;
44 
45 char *mapn[] = {
46  "things", "linedefs", "sidedefs", "vertexes", "segs",
47  "ssectors", "nodes", "sectors", "reject", "blockmap"
48 };
49 
50 void
51 strupr(char *s, char *p)
52 {
53  char c;
54 
55  do{
56  c = *p++;
57  *s++ = toupper(c);
58  }while(c != 0);
59 }
60 
61 void
62 strlwr(char *s, char *p)
63 {
64  char c;
65 
66  do{
67  c = *p++;
68  *s++ = tolower(c);
69  }while(c != 0);
70 }
71 
72 void
73 link(Lump *l, Lump *lp, int len)
74 {
75  l->lp = lp;
76  l->l = lp->l;
77  lp->l->lp = l;
78  lp->l = l;
79  nlmp++;
80  fwad->length += Ndict + len;
81 }
82 
83 void
84 unlink(Lump *l)
85 {
86  if(l->l == nil)
87  return;
88  l->lp->l = l->l;
89  l->l->lp = l->lp;
90  l->l = nil;
91  nlmp--;
92  fwad->length -= Ndict + (l->f != nil ? l->f->length : 0);
93 }
94 
95 void
96 freelump(Lump *l)
97 {
98  unlink(l);
99  free(l->buf);
100  free(l);
101 }
102 
103 void
104 readlump(Lump *l, uchar *p, long n)
105 {
106  if(n <= 0)
107  return;
108  Bseek(wad, l->ofs, 0);
109  if(Bread(wad, p, n) != n)
110  fprint(2, "readlump: short read: %r\n");
111 }
112 
113 void
114 loadlump(File *f, ulong n)
115 {
116  Lump *l;
117 
118  l = f->aux;
119  if(f->length > n)
120  n = f->length;
121  l->buf = emalloc9p(n);
122  l->nbuf = n;
123  l->ofs = 0;
124  readlump(l, l->buf, f->length);
125 }
126 
127 Lump *
128 lastlump(Lump *lp)
129 {
130  File *f, *dir;
131 
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)
134  break;
135  if(lp->type == LTend && lp->f->parent == dir)
136  lp = lp->lp;
137  return lp;
138 }
139 
140 int
141 nextmaplump(char *s)
142 {
143  char **p;
144 
145  for(p=mapn; p<mapn+nelem(mapn); p++)
146  if(strcmp(s, *p) == 0)
147  return p-mapn;
148  return -1;
149 }
150 
151 Lump *
152 sortmap(Lump *lp, Lump *l)
153 {
154  int ip, i;
155 
156  i = nextmaplump(l->f->name);
157  for(; lp->l != lumps; lp=lp->l){
158  ip = nextmaplump(lp->l->f->name);
159  if(ip < 0 || ip > i)
160  break;
161  }
162  return lp;
163 }
164 
165 int
166 ismaplump(char *s)
167 {
168  return nextmaplump(s) >= 0;
169 }
170 
171 int
172 ismapname(char *s)
173 {
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]);
178 }
179 
180 int
181 ismarkname(char *s, char *m)
182 {
183  char *p;
184 
185  p = strstr(s, m);
186  if(p == nil || p[strlen(m)] != 0)
187  return 0;
188  if(p - s > 2)
189  return 0;
190  return 1;
191 }
192 
193 int
194 validname(char *s, File *dir, int *type, int isnew, int isdir)
195 {
196  int n;
197  char c, *p;
198  Lump *lp;
199 
200  *type = LTnil;
201  n = strlen(s);
202  if(n < 1 || n > sizeof(lp->name)-1){
203  werrstr("invalid lump name");
204  return 0;
205  }
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);
209  return 0;
210  }
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");
215  return 0;
216  }
217  *type = LTreg;
218  lp = dir->aux;
219  if(ismapname(s)){
220  *type = LTmap;
221  if(isnew && !isdir){
222  werrstr("map marker not a directory");
223  return 0;
224  }else if(dir != fs.tree->root){
225  werrstr("nested map directory");
226  return 0;
227  }
228  return 1;
229  }else if(ismarkname(s, "_end")){
230  *type = LTend;
231  if(dir == fs.tree->root || lp == nil || lp->type == LTmap){
232  werrstr("orphaned end marker");
233  return 0;
234  }
235  return 1;
236  }else if(ismarkname(s, "_start")){
237  *type = LTmrk;
238  if(isnew){
239  werrstr("not allowed");
240  return 0;
241  }
242  goto mrkchk;
243  }else if(isnew && isdir){
244  *type = LTmrk;
245  if(n > 2){
246  werrstr("marker name too long");
247  return 0;
248  }
249 mrkchk:
250  if(dir->parent != fs.tree->root){
251  werrstr("start marker nested too deep");
252  return 0;
253  }else if(lp != nil && lp->type == LTmap){
254  werrstr("start marker within map directory");
255  return 0;
256  }
257  return 1;
258  }else if(ismaplump(s) ^ (lp != nil && lp->type == LTmap)){
259  werrstr("map lump outside of map directory");
260  return 0;
261  }
262  return 1;
263 }
264 
265 int
266 endldir(Lump *lp, Lump *le)
267 {
268  char *s, name[sizeof lp->name];
269  Lump *l;
270  File *f;
271 
272  l = emalloc9p(sizeof *l);
273  strcpy(l->name, lp->name);
274  s = strrchr(l->name, '_');
275  strcpy(s, "_END");
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)
279  goto err;
280  f = createfile(lp->f, name, nil, lp->f->mode & 0666, l);
281  if(f == nil)
282  goto err;
283  closefile(f);
284  l->f = f;
285  link(l, le, 0);
286  return 0;
287 err:
288  free(l);
289  return -1;
290 }
291 
292 void
293 accessfile(File *f, int mode)
294 {
295  f->atime = time(nil);
296  if(mode & AWRITE){
297  f->mtime = f->atime;
298  f->qid.vers++;
299  dirty = 1;
300  }
301 }
302 
303 void
304 fswstat(Req *r)
305 {
306  int type;
307  char *e;
308  File *f, *fp;
309  Lump *lp;
310 
311  e = "permission denied";
312  if(rdonly)
313  goto err;
314  if(r->d.mode != ~0 || r->d.gid[0] != 0)
315  goto err;
316  f = r->fid->file;
317  lp = f->aux;
318  if(r->d.length != ~0 && r->d.length != f->length){
319  if(f == fsig || f->mode & DMDIR)
320  goto err;
321  if(!hasperm(f, r->fid->uid, AWRITE))
322  goto err;
323  if(r->d.length < 0){
324  e = "invalid file length";
325  goto err;
326  }
327  if(fwad->length - f->length + r->d.length >= Maxsz){
328  e = "lump size exceeds wad limit";
329  goto err;
330  }
331  }
332  if(r->d.name[0] != 0 && strcmp(r->d.name, f->name) != 0){
333  fp = f->parent;
334  if(fp == nil){
335  e = "orphaned file";
336  goto err;
337  }
338  if(!hasperm(fp, r->fid->uid, AWRITE))
339  goto err;
340  if(!validname(r->d.name, fp, &type, 1, f->mode & DMDIR)){
341  responderror(r);
342  return;
343  }
344  if(lp->type != type){
345  e = "incompatible lump type";
346  goto err;
347  }
348  incref(fp);
349  fp = walkfile(fp, r->d.name);
350  if(fp != nil){
351  e = "file already exists";
352  goto err;
353  }
354  }
355 
356  if(r->d.length != ~0 && r->d.length != f->length){
357  if(lp->buf == nil)
358  loadlump(f, r->d.length);
359  fwad->length += r->d.length - f->length;
360  f->length = r->d.length;
361  }
362  if(r->d.name[0] != 0 && strcmp(r->d.name, f->name) != 0){
363  free(f->name);
364  f->name = estrdup9p(r->d.name);
365  strupr(lp->name, f->name);
366  if(lp->type == LTmrk)
367  strcat(lp->name, "_START");
368  }
369  accessfile(f, AWRITE);
370  if(r->d.mtime != ~0)
371  f->mtime = r->d.mtime;
372  respond(r, nil);
373  return;
374 err:
375  respond(r, e);
376 }
377 
378 void
379 fsremove(Req *r)
380 {
381  File *f;
382  Lump *lp;
383 
384  f = r->fid->file;
385  lp = f->aux;
386  if(f == fsig || f == fwad){
387  respond(r, "not allowed");
388  return;
389  }else if(lp->l->f != nil && lp->l->f->parent == f){
390  respond(r, "has children");
391  return;
392  }
393  unlink(f->aux);
394  dirty = 1;
395  respond(r, nil);
396 }
397 
398 char *
399 writesig(uchar *buf, char *s, vlong n)
400 {
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);
404  dirty = 1;
405  return nil;
406 }
407 
408 void
409 fswrite(Req *r)
410 {
411  vlong n, m, ofs, end;
412  File *f;
413  Lump *l;
414 
415  f = r->fid->file;
416  n = r->ifcall.count;
417  ofs = r->ifcall.offset;
418  if(f->mode & DMAPPEND)
419  ofs = f->length;
420  end = ofs + n;
421  l = f->aux;
422  if(f == fsig){
423  respond(r, writesig(l->buf, r->ifcall.data, n));
424  return;
425  }
426  if(l->buf == nil)
427  loadlump(f, end + Nbuf);
428  if(end > l->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");
432  return;
433  }
434  l->buf = erealloc9p(l->buf, m);
435  l->nbuf = m;
436  }
437  memcpy(l->buf + ofs, r->ifcall.data, n);
438  m = end - f->length;
439  if(m > 0){
440  f->length += m;
441  fwad->length += m;
442  }
443  accessfile(f, AWRITE);
444  r->ofcall.count = n;
445  respond(r, nil);
446 }
447 
448 void
449 makewad(void)
450 {
451  vlong n;
452  uchar *p;
453  u32int ofs;
454  Lump *l, *lp;
455 
456  l = fwad->aux;
457  free(l->buf);
458  l->buf = emalloc9p(fwad->length);
459  p = l->buf;
460  lp = fsig->aux;
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){
464  n = lp->f->length;
465  if(lp->buf != nil)
466  memcpy(p, lp->buf, n);
467  else
468  readlump(lp, p, n);
469  }
470  PBIT32(l->buf + 8, p - l->buf);
471  ofs = Nhdr;
472  for(lp=lumps->l; lp!=lumps; ofs+=n, lp=lp->l){
473  n = lp->f->length;
474  PBIT32(p, ofs), p += 4;
475  PBIT32(p, n), p += 4;
476  memcpy(p, lp->name, 8), p += 8;
477  }
478  dirty = 0;
479 }
480 
481 void
482 fsread(Req *r)
483 {
484  vlong n, ofs, end;
485  File *f;
486  Lump *l;
487 
488  f = r->fid->file;
489  l = f->aux;
490  ofs = r->ifcall.offset + l->ofs;
491  end = l->ofs + f->length;
492  n = r->ifcall.count;
493  if(ofs + n >= end)
494  n = end - ofs;
495  if(n <= 0){
496  r->ofcall.count = 0;
497  respond(r, nil);
498  return;
499  }
500  if(f == fwad && dirty)
501  makewad();
502  if(l->buf != nil)
503  memcpy(r->ofcall.data, l->buf+ofs, n);
504  else{
505  Bseek(wad, ofs, 0);
506  n = Bread(wad, r->ofcall.data, n);
507  if(n < 0){
508  responderror(r);
509  return;
510  }
511  }
512  accessfile(f, AREAD);
513  r->ofcall.count = n;
514  respond(r, nil);
515 }
516 
517 int
518 addlump(Lump *l, File *dir)
519 {
520  Lump *lp;
521 
522  lp = lumps->lp;
523  if(dir != fs.tree->root){
524  lp = dir->aux;
525  lp = lp->type == LTmap ? sortmap(lp, l) : lastlump(lp);
526  }
527  if(l->type == LTend && lp->l->type == LTend && lp->l->f->parent == dir){
528  werrstr("an end marker already exists");
529  return -1;
530  }
531  link(l, lp, 0);
532  if(l->type == LTmrk){
533  strcat(l->name, "_START");
534  if(endldir(l, l) < 0)
535  return -1;
536  }else if(l->type == LTreg){
537  l->buf = emalloc9p(Nbuf);
538  l->nbuf = Nbuf;
539  }
540  dirty = 1;
541  return 0;
542 }
543 
544 Lump *
545 createlump(char *s, File *dir, int ismark)
546 {
547  int type;
548  Lump *l;
549 
550  if(!validname(s, dir, &type, 1, ismark))
551  return nil;
552  l = emalloc9p(sizeof *l);
553  l->type = type;
554  strupr(l->name, s);
555  return l;
556 }
557 
558 void
559 fscreate(Req *r)
560 {
561  int p;
562  File *f;
563  Lump *l;
564 
565  f = r->fid->file;
566  p = r->ifcall.perm;
567  if(p & DMDIR)
568  p = p & ~0777 | p & f->mode & 0777;
569  else
570  p = p & ~0666 | p & f->mode & 0666;
571  l = createlump(r->ifcall.name, f, p & DMDIR);
572  if(l == nil)
573  goto err;
574  f = createfile(f, r->ifcall.name, r->fid->uid, p, l);
575  if(f == nil){
576  free(l);
577  goto err;
578  }
579  l->f = f;
580  if(addlump(l, r->fid->file) < 0){
581  removefile(f);
582  goto err;
583  }
584  r->fid->file = f;
585  r->ofcall.qid = f->qid;
586  respond(r, nil);
587  return;
588 err:
589  responderror(r);
590 }
591 
592 void
593 fsopen(Req *r)
594 {
595  File *f;
596 
597  f = r->fid->file;
598  if((f->mode & DMAPPEND) == 0 && (r->ifcall.mode & OTRUNC) != 0
599  && f != fsig){
600  fwad->length -= f->length;
601  f->length = 0;
602  dirty = 1;
603  }
604  respond(r, nil);
605 }
606 
607 void
608 fsdestroyfile(File *f)
609 {
610  freelump(f->aux);
611 }
612 
613 Srv fs = {
614  .open = fsopen,
615  .create = fscreate,
616  .read = fsread,
617  .write = fswrite,
618  .remove = fsremove,
619  .wstat = fswstat
620 };
621 
622 int
623 get32(Biobuf *bf, u32int *v)
624 {
625  int n;
626  uchar u[4];
627 
628  n = Bread(bf, u, sizeof u);
629  if(n != sizeof u)
630  return -1;
631  *v = GBIT32(u);
632  return 0;
633 }
634 
635 File *
636 replacefile(File *dir, char *fname, int mode, Lump *l)
637 {
638  File *f;
639 
640  incref(dir);
641  f = walkfile(dir, fname);
642  if(f == nil)
643  return nil;
644  if(removefile(f) < 0)
645  return nil;
646  f = createfile(dir, fname, nil, mode, l);
647  return f;
648 }
649 
650 void
651 addsigfile(char *sig)
652 {
653  int n;
654  Lump *l;
655  File *f;
656 
657  n = strlen(sig) + 1;
658  l = emalloc9p(sizeof *l);
659  l->buf = (uchar *)estrdup9p(sig);
660  l->buf[n-1] = '\n';
661  f = createfile(fs.tree->root, "SIG", nil, rdonly ? 0444 : 0666, l);
662  if(f == nil)
663  sysfatal("addsigfile: %r");
664  else{
665  fsig = f;
666  f->length = n;
667  }
668 }
669 
670 void
671 addwadfile(void)
672 {
673  Lump *l;
674  File *f;
675 
676  l = emalloc9p(sizeof *l);
677  f = createfile(fs.tree->root, "WAD", nil, 0444, l);
678  if(f == nil)
679  sysfatal("addwadfile: %r");
680  else{
681  fwad = f;
682  f->length = Nhdr;
683  }
684  dirty++;
685 
686 }
687 
688 void
689 checkends(void)
690 {
691  Lump *lp;
692 
693  if(ldir == fs.tree->root)
694  return;
695  lp = ldir->aux;
696  if(lp->type != LTmap && endldir(lp, lastlump(lp)) < 0)
697  fprint(2, "checkends: %r\n");
698  ldir = ldir->parent;
699  checkends();
700 }
701 
702 int
703 addfile(Lump *l, u32int *len, int mode)
704 {
705  int err;
706  char fname[sizeof l->name], *s;
707  Lump *lp;
708  File *f;
709 
710  *len = 0;
711  if(get32(wad, &l->ofs) < 0 || get32(wad, len) < 0)
712  return -1;
713  if(Bread(wad, l->name, sizeof(l->name)-1) != sizeof(l->name)-1)
714  return -1;
715  strlwr(fname, l->name);
716 
717  lp = ldir->aux;
718  err = !validname(fname, ldir, &l->type, 0, 0);
719  switch(l->type){
720  case LTmap:
721  closefile(ldir);
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");
727  }
728  mode |= DMDIR|0111;
729  *len = 0;
730  break;
731  case LTmrk:
732  if(err){
733  if(lp != nil && lp->type == LTmap){
734  closefile(ldir);
735  ldir = fs.tree->root;
736  }else{
737  fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
738  if(endldir(lp, lastlump(lp)) < 0)
739  return -1;
740  ldir = ldir->parent;
741  }
742  }
743  s = strrchr(fname, '_');
744  *s = 0;
745  mode |= DMDIR|0111;
746  *len = 0;
747  break;
748  case LTend:
749  if(err){
750  ldir = ldir->parent;
751  return -1;
752  }
753  *len = 0;
754  break;
755  case LTreg:
756  if(err){
757  if(ismaplump(fname))
758  fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
759  else
760  ldir = fs.tree->root;
761  }
762  break;
763  default:
764  return -1;
765  }
766 
767  f = createfile(ldir, fname, nil, mode, l);
768  if(f == nil){
769  fprint(2, "createfile %s: %r\n", l->name);
770  if(mode & DMDIR)
771  return -1;
772  f = replacefile(ldir, fname, mode, l);
773  if(f == nil)
774  return -1;
775  }
776  if(mode & DMDIR)
777  ldir = f;
778  else if(l->type == LTend)
779  ldir = ldir->parent;
780  else
781  closefile(f);
782  f->length = *len;
783  l->f = f;
784  return 0;
785 }
786 
787 void
788 parsewad(void)
789 {
790  int n, ne, mode;
791  u32int len;
792  Lump *l;
793 
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);
800  free(l);
801  continue;
802  }
803  link(l, lumps->lp, len);
804  }
805  checkends();
806 }
807 
808 void
809 wadinfo(char *sig)
810 {
811  int n;
812  u32int dictofs;
813 
814  n = Bread(wad, sig, Nsig);
815  if(n != Nsig)
816  sysfatal("readwad: short read: %r");
817  sig[4] = 0;
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);
823 }
824 
825 void
826 usage(void)
827 {
828  fprint(2, "usage: %s [-Dr] [-m mtpt] [-S srvname] [wad]\n", argv0);
829  exits("usage");
830 }
831 
832 void
833 main(int argc, char **argv)
834 {
835  int fl, p;
836  char *mtpt, *srvname, sig[Nsig+1] = "PWAD";
837 
838  mtpt = "/mnt/wad";
839  srvname = nil;
840  fl = MREPL|MCREATE;
841  p = DMDIR|0777;
842  ARGBEGIN{
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;
847  default: usage();
848  }ARGEND
849  if(*argv != nil){
850  wad = Bopen(*argv, OREAD);
851  if(wad == nil)
852  sysfatal("Bopen: %r");
853  wadinfo(sig);
854  }
855  fs.tree = alloctree(nil, nil, p, fsdestroyfile);
856  addsigfile(sig);
857  addwadfile();
858  parsewad();
859  postmountsrv(&fs, srvname, mtpt, fl);
860  exits(nil);
861 }