1implement Archfs; 2 3include "sys.m"; 4 sys: Sys; 5include "draw.m"; 6 7include "bufio.m"; 8 bufio: Bufio; 9 10include "string.m"; 11 str: String; 12 13include "daytime.m"; 14 daytime: Daytime; 15 16include "styx.m"; 17 styx: Styx; 18 NOFID: import Styx; 19 20include "arg.m"; 21 22Archfs: module 23{ 24 init: fn(nil: ref Draw->Context, nil: list of string); 25}; 26 27Ahdr: adt { 28 name: string; 29 modestr: string; 30 d: ref Sys->Dir; 31}; 32 33Archive: adt { 34 b: ref Bufio->Iobuf; 35 nexthdr: big; 36 canseek: int; 37 hdr: ref Ahdr; 38 err: string; 39}; 40 41Iobuf: import bufio; 42Tmsg, Rmsg: import styx; 43 44Einuse : con "fid already in use"; 45Ebadfid : con "bad fid"; 46Eopen : con "fid already opened"; 47Enotfound : con "file does not exist"; 48Enotdir : con "not a directory"; 49Eperm : con "permission denied"; 50 51UID: con "inferno"; 52GID: con "inferno"; 53 54debug := 0; 55 56Dir: adt { 57 dir: Sys->Dir; 58 offset: big; 59 parent: cyclic ref Dir; 60 child: cyclic ref Dir; 61 sibling: cyclic ref Dir; 62}; 63 64Fid: adt { 65 fid: int; 66 open: int; 67 dir: ref Dir; 68}; 69 70HTSZ: con 32; 71fidtab := array[HTSZ] of list of ref Fid; 72 73root: ref Dir; 74qid: int; 75mtpt := "/mnt/arch"; 76bio: ref Iobuf; 77buf: array of byte; 78skip := 0; 79 80init(nil: ref Draw->Context, args: list of string) 81{ 82 sys = load Sys Sys->PATH; 83 bufio = load Bufio Bufio->PATH; 84 str = load String String->PATH; 85 daytime = load Daytime Daytime->PATH; 86 styx = load Styx Styx->PATH; 87 if(bufio == nil || styx == nil || daytime == nil || str == nil) 88 fatal("failed to load modules"); 89 styx->init(); 90 91 flags := Sys->MREPL; 92 arg := load Arg Arg->PATH; 93 if(arg == nil) 94 fatal("failed to load "+Arg->PATH); 95 arg->init(args); 96 arg->setusage("archfs [-ab] [-m mntpt] archive [prefix ...]"); 97 while((c := arg->opt()) != 0){ 98 case c { 99 'D' => 100 debug = 1; 101 'a' => 102 flags = Sys->MAFTER; 103 'b' => 104 flags = Sys->MBEFORE; 105 'm' => 106 mtpt = arg->earg(); 107 's' => 108 skip = 1; 109 * => 110 arg->usage(); 111 } 112 } 113 args = arg->argv(); 114 if(args == nil) 115 arg->usage(); 116 arg = nil; 117 118 buf = array[Sys->ATOMICIO] of byte; 119 # root = newdir("/", UID, GID, 8r755|Sys->DMDIR, daytime->now()); 120 root = newdir(basename(mtpt), UID, GID, 8r555|Sys->DMDIR, daytime->now()); 121 root.parent = root; 122 readarch(hd args, tl args); 123 p := array[2] of ref Sys->FD; 124 if(sys->pipe(p) < 0) 125 fatal("can't create pipe"); 126 pidch := chan of int; 127 spawn serve(p[1], pidch); 128 <- pidch; 129 if(sys->mount(p[0], nil, mtpt, flags, nil) < 0) 130 fatal(sys->sprint("cannot mount archive on %s: %r", mtpt)); 131} 132 133reply(fd: ref Sys->FD, m: ref Rmsg): int 134{ 135 if(debug) 136 sys->fprint(sys->fildes(2), "-> %s\n", m.text()); 137 s := m.pack(); 138 if(s == nil) 139 return -1; 140 return sys->write(fd, s, len s); 141} 142 143error(fd: ref Sys->FD, m: ref Tmsg, e: string) 144{ 145 reply(fd, ref Rmsg.Error(m.tag, e)); 146} 147 148serve(fd: ref Sys->FD, pidch: chan of int) 149{ 150 e: string; 151 f: ref Fid; 152 153 pidch <-= sys->pctl(Sys->NEWNS|Sys->NEWFD, 1 :: 2 :: fd.fd :: bio.fd.fd :: nil); 154 bio.fd = sys->fildes(bio.fd.fd); 155 fd = sys->fildes(fd.fd); 156Work: 157 while((m0 := Tmsg.read(fd, Styx->MAXRPC)) != nil){ 158 if(debug) 159 sys->fprint(sys->fildes(2), "<- %s\n", m0.text()); 160 pick m := m0 { 161 Readerror => 162 fatal("read error on styx server"); 163 Version => 164 (s, v) := styx->compatible(m, Styx->MAXRPC, Styx->VERSION); 165 reply(fd, ref Rmsg.Version(m.tag, s, v)); 166 Auth => 167 error(fd, m, "authentication not required"); 168 Flush => 169 reply(fd, ref Rmsg.Flush(m.tag)); 170 Walk => 171 (f, e) = mapfid(m.fid); 172 if(e != nil){ 173 error(fd, m, e); 174 continue; 175 } 176 if(f.open){ 177 error(fd, m, Eopen); 178 continue; 179 } 180 dir := f.dir; 181 nq := 0; 182 nn := len m.names; 183 qids := array[nn] of Sys->Qid; 184 if(nn > 0){ 185 for(k := 0; k < nn; k++){ 186 if((dir.dir.mode & Sys->DMDIR) == 0){ 187 if(k == 0){ 188 error(fd, m, Enotdir); 189 continue Work; 190 } 191 break; 192 } 193 dir = lookup(dir, m.names[k]); 194 if(dir == nil){ 195 if(k == 0){ 196 error(fd, m, Enotfound); 197 continue Work; 198 } 199 break; 200 } 201 qids[nq++] = dir.dir.qid; 202 } 203 } 204 if(nq < nn) 205 qids = qids[0: nq]; 206 if(nq == nn){ 207 if(m.newfid != m.fid){ 208 f = newfid(m.newfid); 209 if(f == nil){ 210 error(fd, m, Einuse); 211 continue Work; 212 } 213 } 214 f.dir = dir; 215 } 216 reply(fd, ref Rmsg.Walk(m.tag, qids)); 217 Open => 218 (f, e) = mapfid(m.fid); 219 if(e != nil){ 220 error(fd, m, e); 221 continue; 222 } 223 if(m.mode != Sys->OREAD){ 224 error(fd, m, Eperm); 225 continue; 226 } 227 f.open = 1; 228 reply(fd, ref Rmsg.Open(m.tag, f.dir.dir.qid, Styx->MAXFDATA)); 229 Create => 230 error(fd, m, Eperm); 231 Read => 232 (f, e) = mapfid(m.fid); 233 if(e != nil){ 234 error(fd, m, e); 235 continue; 236 } 237 data := read(f.dir, m.offset, m.count); 238 reply(fd, ref Rmsg.Read(m.tag, data)); 239 Write => 240 error(fd, m, Eperm); 241 Clunk => 242 (f, e) = mapfid(m.fid); 243 if(e != nil){ 244 error(fd, m, e); 245 continue; 246 } 247 freefid(f); 248 reply(fd, ref Rmsg.Clunk(m.tag)); 249 Stat => 250 (f, e) = mapfid(m.fid); 251 if(e != nil){ 252 error(fd, m, e); 253 continue; 254 } 255 reply(fd, ref Rmsg.Stat(m.tag, f.dir.dir)); 256 Remove => 257 error(fd, m, Eperm); 258 Wstat => 259 error(fd, m, Eperm); 260 Attach => 261 f = newfid(m.fid); 262 if(f == nil){ 263 error(fd, m, Einuse); 264 continue; 265 } 266 f.dir = root; 267 reply(fd, ref Rmsg.Attach(m.tag, f.dir.dir.qid)); 268 * => 269 fatal("unknown styx message"); 270 } 271 } 272} 273 274newfid(fid: int): ref Fid 275{ 276 if(fid == NOFID) 277 return nil; 278 hv := hashval(fid); 279 ff: ref Fid; 280 for(l := fidtab[hv]; l != nil; l = tl l){ 281 f := hd l; 282 if(f.fid == fid) 283 return nil; 284 if(ff == nil && f.fid == NOFID) 285 ff = f; 286 } 287 if((f := ff) == nil){ 288 f = ref Fid; 289 fidtab[hv] = f :: fidtab[hv]; 290 } 291 f.fid = fid; 292 f.open = 0; 293 return f; 294} 295 296freefid(f: ref Fid) 297{ 298 hv := hashval(f.fid); 299 for(l := fidtab[hv]; l != nil; l = tl l) 300 if(hd l == f){ 301 f.fid = NOFID; 302 f.dir = nil; 303 f.open = 0; 304 return; 305 } 306 fatal("cannot find fid"); 307} 308 309mapfid(fid: int): (ref Fid, string) 310{ 311 if(fid == NOFID) 312 return (nil, Ebadfid); 313 hv := hashval(fid); 314 for(l := fidtab[hv]; l != nil; l = tl l){ 315 f := hd l; 316 if(f.fid == fid){ 317 if(f.dir == nil) 318 return (nil, Enotfound); 319 return (f, nil); 320 } 321 } 322 return (nil, Ebadfid); 323} 324 325hashval(n: int): int 326{ 327 n %= HTSZ; 328 if(n < 0) 329 n += HTSZ; 330 return n; 331} 332 333readarch(f: string, args: list of string) 334{ 335 ar := openarch(f); 336 if(ar == nil || ar.b == nil) 337 fatal(sys->sprint("cannot open %s: %r", f)); 338 bio = ar.b; 339 while((a := gethdr(ar)) != nil){ 340 if(args != nil){ 341 if(!selected(a.name, args)){ 342 if(skip) 343 return; 344 #drain(ar, int a.d.length); 345 continue; 346 } 347 mkdirs("/", a.name); 348 } 349 d := mkdir(a.name, a.d.mode, a.d.mtime, a.d.uid, a.d.gid, 0); 350 if((a.d.mode & Sys->DMDIR) == 0){ 351 d.dir.length = a.d.length; 352 d.offset = bio.offset(); 353 } 354 #drain(ar, int a.d.length); 355 } 356 if(ar.err != nil) 357 fatal(ar.err); 358} 359 360selected(s: string, args: list of string): int 361{ 362 for(; args != nil; args = tl args) 363 if(fileprefix(hd args, s)) 364 return 1; 365 return 0; 366} 367 368fileprefix(prefix, s: string): int 369{ 370 n := len prefix; 371 m := len s; 372 if(n > m || !str->prefix(prefix, s)) 373 return 0; 374 if(m > n && s[n] != '/') 375 return 0; 376 return 1; 377} 378 379basename(f: string): string 380{ 381 for(i := len f; i > 0; ) 382 if(f[--i] == '/') 383 return f[i+1:]; 384 return f; 385} 386 387split(p: string): (string, string) 388{ 389 if(p == nil) 390 fatal("nil string in split"); 391 if(p[0] != '/') 392 fatal("p0 not / in split"); 393 while(p[0] == '/') 394 p = p[1:]; 395 i := 0; 396 while(i < len p && p[i] != '/') 397 i++; 398 if(i == len p) 399 return (p, nil); 400 else 401 return (p[0:i], p[i:]); 402} 403 404mkdirs(basedir, name: string) 405{ 406 (nil, names) := sys->tokenize(name, "/"); 407 while(names != nil){ 408 # sys->print("mkdir %s\n", basedir); 409 mkdir(basedir, 8r775|Sys->DMDIR, daytime->now(), UID, GID, 1); 410 if(tl names == nil) 411 break; 412 basedir = basedir + "/" + hd names; 413 names = tl names; 414 } 415} 416 417read(d: ref Dir, offset: big, n: int): array of byte 418{ 419 if(d.dir.mode & Sys->DMDIR) 420 return readdir(d, int offset, n); 421 return readfile(d, offset, n); 422} 423 424readdir(d: ref Dir, o: int, n: int): array of byte 425{ 426 k := 0; 427 m := 0; 428 b := array[n] of byte; 429 for(s := d.child; s != nil; s = s.sibling){ 430 l := styx->packdirsize(s.dir); 431 if(k < o){ 432 k += l; 433 continue; 434 } 435 if(m+l > n) 436 break; 437 b[m: ] = styx->packdir(s.dir); 438 m += l; 439 } 440 return b[0: m]; 441} 442 443readfile(d: ref Dir, offset: big, n: int): array of byte 444{ 445 if(offset+big n > d.dir.length) 446 n = int(d.dir.length-offset); 447 if(n <= 0 || offset < big 0) 448 return nil; 449 bio.seek(d.offset+offset, Bufio->SEEKSTART); 450 a := array[n] of byte; 451 p := 0; 452 m := 0; 453 for( ; n != 0; n -= m){ 454 l := len buf; 455 if(n < l) 456 l = n; 457 m = bio.read(buf, l); 458 if(m <= 0 || m != l) 459 fatal("premature eof"); 460 a[p:] = buf[0:m]; 461 p += m; 462 } 463 return a; 464} 465 466mkdir(f: string, mode: int, mtime: int, uid: string, gid: string, existsok: int): ref Dir 467{ 468 if(f == "/") 469 return nil; 470 d := newdir(basename(f), uid, gid, mode, mtime); 471 addfile(d, f, existsok); 472 return d; 473} 474 475addfile(d: ref Dir, path: string, existsok: int) 476{ 477 elem: string; 478 479 opath := path; 480 p := prev := root; 481 basedir := ""; 482# sys->print("addfile %s: %s\n", d.dir.name, path); 483 while(path != nil){ 484 (elem, path) = split(path); 485 basedir += "/" + elem; 486 op := p; 487 p = lookup(p, elem); 488 if(path == nil){ 489 if(p != nil){ 490 if(!existsok && (p.dir.mode&Sys->DMDIR) == 0) 491 sys->fprint(sys->fildes(2), "addfile: %s already there", opath); 492 # fatal(sys->sprint("addfile: %s already there", opath)); 493 return; 494 } 495 if(prev.child == nil) 496 prev.child = d; 497 else { 498 for(s := prev.child; s.sibling != nil; s = s.sibling) 499 ; 500 s.sibling = d; 501 } 502 d.parent = prev; 503 } 504 else { 505 if(p == nil){ 506 mkdir(basedir, 8r775|Sys->DMDIR, daytime->now(), UID, GID, 1); 507 p = lookup(op, elem); 508 if(p == nil) 509 fatal("bad file system"); 510 } 511 } 512 prev = p; 513 } 514} 515 516lookup(p: ref Dir, f: string): ref Dir 517{ 518 if((p.dir.mode&Sys->DMDIR) == 0) 519 fatal("not a directory in lookup"); 520 if(f == ".") 521 return p; 522 if(f == "..") 523 return p.parent; 524 for(d := p.child; d != nil; d = d.sibling) 525 if(d.dir.name == f) 526 return d; 527 return nil; 528} 529 530newdir(name, uid, gid: string, mode, mtime: int): ref Dir 531{ 532 dir := sys->zerodir; 533 dir.name = name; 534 dir.uid = uid; 535 dir.gid = gid; 536 dir.mode = mode; 537 dir.qid.path = big (qid++); 538 dir.qid.qtype = mode>>24; 539 dir.qid.vers = 0; 540 dir.atime = dir.mtime = mtime; 541 dir.length = big 0; 542 543 d := ref Dir; 544 d.dir = dir; 545 d.offset = big 0; 546 return d; 547} 548 549prd(d: ref Dir) 550{ 551 dir := d.dir; 552 sys->print("%q %q %q %bx %x %x %d %d %bd %d %d %bd\n", 553 dir.name, dir.uid, dir.gid, dir.qid.path, dir.qid.vers, dir.mode, dir.atime, dir.mtime, dir.length, dir.dtype, dir.dev, d.offset); 554} 555 556fatal(e: string) 557{ 558 sys->fprint(sys->fildes(2), "archfs: %s\n", e); 559 raise "fail:error"; 560} 561 562openarch(file: string): ref Archive 563{ 564 b := bufio->open(file, Bufio->OREAD); 565 if(b == nil) 566 return nil; 567 ar := ref Archive; 568 ar.b = b; 569 ar.nexthdr = big 0; 570 ar.canseek = 1; 571 ar.hdr = ref Ahdr; 572 ar.hdr.d = ref Sys->Dir; 573 return ar; 574} 575 576NFLDS: con 6; 577 578gethdr(ar: ref Archive): ref Ahdr 579{ 580 a := ar.hdr; 581 b := ar.b; 582 m := b.offset(); 583 n := ar.nexthdr; 584 if(m != n){ 585 if(ar.canseek) 586 b.seek(n, Bufio->SEEKSTART); 587 else { 588 if(m > n) 589 fatal(sys->sprint("bad offset in gethdr: m=%bd n=%bd", m, n)); 590 if(drain(ar, int(n-m)) < 0) 591 return nil; 592 } 593 } 594 if((s := b.gets('\n')) == nil){ 595 ar.err = "premature end of archive"; 596 return nil; 597 } 598 if(s == "end of archive\n") 599 return nil; 600 (nf, fs) := sys->tokenize(s, " \t\n"); 601 if(nf != NFLDS){ 602 ar.err = "too few fields in file header"; 603 return nil; 604 } 605 a.name = hd fs; fs = tl fs; 606 (a.d.mode, nil) = str->toint(hd fs, 8); fs = tl fs; 607 a.d.uid = hd fs; fs = tl fs; 608 a.d.gid = hd fs; fs = tl fs; 609 (a.d.mtime, nil) = str->toint(hd fs, 10); fs = tl fs; 610 (tmp, nil) := str->toint(hd fs, 10); fs = tl fs; 611 a.d.length = big tmp; 612 ar.nexthdr = b.offset()+a.d.length; 613 return a; 614} 615 616drain(ar: ref Archive, n: int): int 617{ 618 while(n > 0){ 619 m := n; 620 if(m > len buf) 621 m = len buf; 622 p := ar.b.read(buf, m); 623 if(p != m){ 624 ar.err = "unexpectedly short read"; 625 return -1; 626 } 627 n -= m; 628 } 629 return 0; 630} 631