changelog shortlog tags branches changeset files revisions annotate raw help

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

changeset 7216: 249468ce1239
parent: 7e153241a8f6
author: cinap_lenrek@felloff.net
date: Tue, 07 May 2019 21:49:49 +0200
permissions: -rw-r--r--
description: wadfs: avoid comma operator after PBIT32() macros
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);
463  p += 8;
464  for(lp=lumps->l; lp!=lumps; p+=n, lp=lp->l){
465  n = lp->f->length;
466  if(lp->buf != nil)
467  memcpy(p, lp->buf, n);
468  else
469  readlump(lp, p, n);
470  }
471  PBIT32(l->buf + 8, p - l->buf);
472  ofs = Nhdr;
473  for(lp=lumps->l; lp!=lumps; ofs+=n, lp=lp->l){
474  n = lp->f->length;
475  PBIT32(p, ofs);
476  p += 4;
477  PBIT32(p, n);
478  p += 4;
479  memcpy(p, lp->name, 8), p += 8;
480  }
481  dirty = 0;
482 }
483 
484 void
485 fsread(Req *r)
486 {
487  vlong n, ofs, end;
488  File *f;
489  Lump *l;
490 
491  f = r->fid->file;
492  l = f->aux;
493  ofs = r->ifcall.offset + l->ofs;
494  end = l->ofs + f->length;
495  n = r->ifcall.count;
496  if(ofs + n >= end)
497  n = end - ofs;
498  if(n <= 0){
499  r->ofcall.count = 0;
500  respond(r, nil);
501  return;
502  }
503  if(f == fwad && dirty)
504  makewad();
505  if(l->buf != nil)
506  memcpy(r->ofcall.data, l->buf+ofs, n);
507  else{
508  Bseek(wad, ofs, 0);
509  n = Bread(wad, r->ofcall.data, n);
510  if(n < 0){
511  responderror(r);
512  return;
513  }
514  }
515  accessfile(f, AREAD);
516  r->ofcall.count = n;
517  respond(r, nil);
518 }
519 
520 int
521 addlump(Lump *l, File *dir)
522 {
523  Lump *lp;
524 
525  lp = lumps->lp;
526  if(dir != fs.tree->root){
527  lp = dir->aux;
528  lp = lp->type == LTmap ? sortmap(lp, l) : lastlump(lp);
529  }
530  if(l->type == LTend && lp->l->type == LTend && lp->l->f->parent == dir){
531  werrstr("an end marker already exists");
532  return -1;
533  }
534  link(l, lp, 0);
535  if(l->type == LTmrk){
536  strcat(l->name, "_START");
537  if(endldir(l, l) < 0)
538  return -1;
539  }else if(l->type == LTreg){
540  l->buf = emalloc9p(Nbuf);
541  l->nbuf = Nbuf;
542  }
543  dirty = 1;
544  return 0;
545 }
546 
547 Lump *
548 createlump(char *s, File *dir, int ismark)
549 {
550  int type;
551  Lump *l;
552 
553  if(!validname(s, dir, &type, 1, ismark))
554  return nil;
555  l = emalloc9p(sizeof *l);
556  l->type = type;
557  strupr(l->name, s);
558  return l;
559 }
560 
561 void
562 fscreate(Req *r)
563 {
564  int p;
565  File *f;
566  Lump *l;
567 
568  f = r->fid->file;
569  p = r->ifcall.perm;
570  if(p & DMDIR)
571  p = p & ~0777 | p & f->mode & 0777;
572  else
573  p = p & ~0666 | p & f->mode & 0666;
574  l = createlump(r->ifcall.name, f, p & DMDIR);
575  if(l == nil)
576  goto err;
577  f = createfile(f, r->ifcall.name, r->fid->uid, p, l);
578  if(f == nil){
579  free(l);
580  goto err;
581  }
582  l->f = f;
583  if(addlump(l, r->fid->file) < 0){
584  removefile(f);
585  goto err;
586  }
587  r->fid->file = f;
588  r->ofcall.qid = f->qid;
589  respond(r, nil);
590  return;
591 err:
592  responderror(r);
593 }
594 
595 void
596 fsopen(Req *r)
597 {
598  File *f;
599 
600  f = r->fid->file;
601  if((f->mode & DMAPPEND) == 0 && (r->ifcall.mode & OTRUNC) != 0
602  && f != fsig){
603  fwad->length -= f->length;
604  f->length = 0;
605  dirty = 1;
606  }
607  respond(r, nil);
608 }
609 
610 void
611 fsdestroyfile(File *f)
612 {
613  freelump(f->aux);
614 }
615 
616 Srv fs = {
617  .open = fsopen,
618  .create = fscreate,
619  .read = fsread,
620  .write = fswrite,
621  .remove = fsremove,
622  .wstat = fswstat
623 };
624 
625 int
626 get32(Biobuf *bf, u32int *v)
627 {
628  int n;
629  uchar u[4];
630 
631  n = Bread(bf, u, sizeof u);
632  if(n != sizeof u)
633  return -1;
634  *v = GBIT32(u);
635  return 0;
636 }
637 
638 File *
639 replacefile(File *dir, char *fname, int mode, Lump *l)
640 {
641  File *f;
642 
643  incref(dir);
644  f = walkfile(dir, fname);
645  if(f == nil)
646  return nil;
647  if(removefile(f) < 0)
648  return nil;
649  f = createfile(dir, fname, nil, mode, l);
650  return f;
651 }
652 
653 void
654 addsigfile(char *sig)
655 {
656  int n;
657  Lump *l;
658  File *f;
659 
660  n = strlen(sig) + 1;
661  l = emalloc9p(sizeof *l);
662  l->buf = (uchar *)estrdup9p(sig);
663  l->buf[n-1] = '\n';
664  f = createfile(fs.tree->root, "SIG", nil, rdonly ? 0444 : 0666, l);
665  if(f == nil)
666  sysfatal("addsigfile: %r");
667  else{
668  fsig = f;
669  f->length = n;
670  }
671 }
672 
673 void
674 addwadfile(void)
675 {
676  Lump *l;
677  File *f;
678 
679  l = emalloc9p(sizeof *l);
680  f = createfile(fs.tree->root, "WAD", nil, 0444, l);
681  if(f == nil)
682  sysfatal("addwadfile: %r");
683  else{
684  fwad = f;
685  f->length = Nhdr;
686  }
687  dirty++;
688 
689 }
690 
691 void
692 checkends(void)
693 {
694  Lump *lp;
695 
696  if(ldir == fs.tree->root)
697  return;
698  lp = ldir->aux;
699  if(lp->type != LTmap && endldir(lp, lastlump(lp)) < 0)
700  fprint(2, "checkends: %r\n");
701  ldir = ldir->parent;
702  checkends();
703 }
704 
705 int
706 addfile(Lump *l, u32int *len, int mode)
707 {
708  int err;
709  char fname[sizeof l->name], *s;
710  Lump *lp;
711  File *f;
712 
713  *len = 0;
714  if(get32(wad, &l->ofs) < 0 || get32(wad, len) < 0)
715  return -1;
716  if(Bread(wad, l->name, sizeof(l->name)-1) != sizeof(l->name)-1)
717  return -1;
718  strlwr(fname, l->name);
719 
720  lp = ldir->aux;
721  err = !validname(fname, ldir, &l->type, 0, 0);
722  switch(l->type){
723  case LTmap:
724  closefile(ldir);
725  ldir = fs.tree->root;
726  if(err && lp != nil && lp->type != LTmap){
727  fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
728  if(endldir(lp, lastlump(lp)) < 0)
729  fprint(2, "endldir: %r\n");
730  }
731  mode |= DMDIR|0111;
732  *len = 0;
733  break;
734  case LTmrk:
735  if(err){
736  if(lp != nil && lp->type == LTmap){
737  closefile(ldir);
738  ldir = fs.tree->root;
739  }else{
740  fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
741  if(endldir(lp, lastlump(lp)) < 0)
742  return -1;
743  ldir = ldir->parent;
744  }
745  }
746  s = strrchr(fname, '_');
747  *s = 0;
748  mode |= DMDIR|0111;
749  *len = 0;
750  break;
751  case LTend:
752  if(err){
753  ldir = ldir->parent;
754  return -1;
755  }
756  *len = 0;
757  break;
758  case LTreg:
759  if(err){
760  if(ismaplump(fname))
761  fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, *len);
762  else
763  ldir = fs.tree->root;
764  }
765  break;
766  default:
767  return -1;
768  }
769 
770  f = createfile(ldir, fname, nil, mode, l);
771  if(f == nil){
772  fprint(2, "createfile %s: %r\n", l->name);
773  if(mode & DMDIR)
774  return -1;
775  f = replacefile(ldir, fname, mode, l);
776  if(f == nil)
777  return -1;
778  }
779  if(mode & DMDIR)
780  ldir = f;
781  else if(l->type == LTend)
782  ldir = ldir->parent;
783  else
784  closefile(f);
785  f->length = *len;
786  l->f = f;
787  return 0;
788 }
789 
790 void
791 parsewad(void)
792 {
793  int n, ne, mode;
794  u32int len;
795  Lump *l;
796 
797  mode = rdonly ? 0444 : 0666;
798  ldir = fs.tree->root;
799  for(n=0, ne=nlmp, nlmp=0; n<ne; n++){
800  l = emalloc9p(sizeof *l);
801  if(addfile(l, &len, mode) < 0){
802  fprint(2, "addfile %s ofs=%#ux len=%#ux: %r\n", l->name, l->ofs, len);
803  free(l);
804  continue;
805  }
806  link(l, lumps->lp, len);
807  }
808  checkends();
809 }
810 
811 void
812 wadinfo(char *sig)
813 {
814  int n;
815  u32int dictofs;
816 
817  n = Bread(wad, sig, Nsig);
818  if(n != Nsig)
819  sysfatal("readwad: short read: %r");
820  sig[4] = 0;
821  if(strcmp(sig, "IWAD") != 0 && strcmp(sig, "PWAD") != 0)
822  sysfatal("invalid wad signature");
823  if(get32(wad, &nlmp) < 0 || get32(wad, &dictofs) < 0)
824  sysfatal("wadinfo: %r");
825  Bseek(wad, dictofs, 0);
826 }
827 
828 void
829 usage(void)
830 {
831  fprint(2, "usage: %s [-Dr] [-m mtpt] [-S srvname] [wad]\n", argv0);
832  exits("usage");
833 }
834 
835 void
836 main(int argc, char **argv)
837 {
838  int fl, p;
839  char *mtpt, *srvname, sig[Nsig+1] = "PWAD";
840 
841  mtpt = "/mnt/wad";
842  srvname = nil;
843  fl = MREPL|MCREATE;
844  p = DMDIR|0777;
845  ARGBEGIN{
846  case 'D': chatty9p++; break;
847  case 'S': srvname = EARGF(usage()); break;
848  case 'm': mtpt = EARGF(usage()); break;
849  case 'r': rdonly++; p &= ~0222; fl &= ~MCREATE; break;
850  default: usage();
851  }ARGEND
852  if(*argv != nil){
853  wad = Bopen(*argv, OREAD);
854  if(wad == nil)
855  sysfatal("Bopen: %r");
856  wadinfo(sig);
857  }
858  fs.tree = alloctree(nil, nil, p, fsdestroyfile);
859  addsigfile(sig);
860  addwadfile();
861  parsewad();
862  postmountsrv(&fs, srvname, mtpt, fl);
863  exits(nil);
864 }