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