1implement MemFS; 2 3include "sys.m"; 4 sys: Sys; 5 OTRUNC, ORCLOSE, OREAD, OWRITE: import Sys; 6include "styx.m"; 7 styx: Styx; 8 Tmsg, Rmsg: import styx; 9include "styxlib.m"; 10 styxlib: Styxlib; 11 Styxserver: import styxlib; 12include "draw.m"; 13include "arg.m"; 14 15MemFS: module { 16 init: fn(ctxt: ref Draw->Context, args: list of string); 17}; 18 19 20blksz : con 512; 21Efull : con "filesystem full"; 22 23Memfile : adt { 24 name : string; 25 owner : string; 26 qid : Sys->Qid; 27 perm : int; 28 atime : int; 29 mtime : int; 30 nopen : int; 31 data : array of array of byte; # allocated in blks, no holes 32 length : int; 33 parent : cyclic ref Memfile; # Dir entry linkage 34 kids : cyclic ref Memfile; 35 prev : cyclic ref Memfile; 36 next : cyclic ref Memfile; 37 hashnext : cyclic ref Memfile; # Qid hash linkage 38}; 39 40Qidhash : adt { 41 buckets : array of ref Memfile; 42 nextqid : int; 43 new : fn () : ref Qidhash; 44 add : fn (h : self ref Qidhash, mf : ref Memfile); 45 remove : fn (h : self ref Qidhash, mf : ref Memfile); 46 lookup : fn (h : self ref Qidhash, qid : Sys->Qid) : ref Memfile; 47}; 48 49timefd: ref Sys->FD; 50 51init(nil: ref Draw->Context, argv: list of string) 52{ 53 sys = load Sys Sys->PATH; 54 styx = checkload(load Styx Styx->PATH, Styx->PATH); 55 styxlib = checkload(load Styxlib Styxlib->PATH, Styxlib->PATH); 56 arg := checkload(load Arg Arg->PATH, Arg->PATH); 57 58 amode := Sys->MREPL; 59 maxsz := 16r7fffffff; 60 srv := 0; 61 mntpt := "/tmp"; 62 63 arg->init(argv); 64 arg->setusage("memfs [-s] [-rab] [-m size] [mountpoint]"); 65 while((opt := arg->opt()) != 0) { 66 case opt{ 67 's' => 68 srv = 1; 69 'r' => 70 amode = Sys->MREPL; 71 'a' => 72 amode = Sys->MAFTER; 73 'b' => 74 amode = Sys->MBEFORE; 75 'm' => 76 maxsz = int arg->earg(); 77 * => 78 arg->usage(); 79 } 80 } 81 argv = arg->argv(); 82 arg = nil; 83 if (argv != nil) 84 mntpt = hd argv; 85 86 srvfd: ref Sys->FD; 87 mntfd: ref Sys->FD; 88 if (srv) 89 srvfd = sys->fildes(0); 90 else { 91 p := array [2] of ref Sys->FD; 92 if (sys->pipe(p) == -1) 93 error(sys->sprint("cannot create pipe: %r")); 94 mntfd = p[0]; 95 srvfd = p[1]; 96 } 97 styx->init(); 98 styxlib->init(styx); 99 timefd = sys->open("/dev/time", sys->OREAD); 100 101 (tc, styxsrv) := Styxserver.new(srvfd); 102 if (srv) 103 memfs(maxsz, tc, styxsrv, nil); 104 else { 105 sync := chan of int; 106 spawn memfs(maxsz, tc, styxsrv, sync); 107 <-sync; 108 if (sys->mount(mntfd, nil, mntpt, amode | Sys->MCREATE, nil) == -1) 109 error(sys->sprint("failed to mount onto %s: %r", mntpt)); 110 } 111} 112 113checkload[T](x: T, p: string): T 114{ 115 if(x == nil) 116 error(sys->sprint("cannot load %s: %r", p)); 117 return x; 118} 119 120stderr(): ref Sys->FD 121{ 122 return sys->fildes(2); 123} 124 125error(e: string) 126{ 127 sys->fprint(stderr(), "memfs: %s\n", e); 128 raise "fail:error"; 129} 130 131freeblks: int; 132 133memfs(maxsz : int, tc : chan of ref Tmsg, srv : ref Styxserver, sync: chan of int) 134{ 135 sys->pctl(Sys->NEWNS, nil); 136 if (sync != nil) 137 sync <-= 1; 138 freeblks = (maxsz / blksz); 139 qhash := Qidhash.new(); 140 141 # init root 142 root := newmf(qhash, nil, "memfs", srv.uname, 8r755 | Sys->DMDIR); 143 root.parent = root; 144 145 while((tmsg := <-tc) != nil) { 146# sys->print("%s\n", tmsg.text()); 147 Msg: 148 pick tm := tmsg { 149 Readerror => 150 break; 151 Version => 152 srv.devversion(tm); 153 Auth => 154 srv.devauth(tm); 155 Flush => 156 srv.reply(ref Rmsg.Flush(tm.tag)); 157 Walk => 158 (err, c, mf) := fidtomf(srv, qhash, tm.fid); 159 if (err != "") { 160 srv.reply(ref Rmsg.Error(tm.tag, err)); 161 continue; 162 } 163 nc: ref styxlib->Chan; 164 if (tm.newfid != tm.fid) { 165 nc = srv.clone(c, tm.newfid); 166 if (nc == nil) { 167 srv.reply(ref Rmsg.Error(tm.tag, "fid in use")); 168 continue; 169 } 170 c = nc; 171 } 172 qids: array of Sys->Qid; 173 if (len tm.names > 0) { 174 oqid := c.qid; 175 opath := c.path; 176 qids = array[len tm.names] of Sys->Qid; 177 wmf := mf; 178 for (i := 0; i < len tm.names; i++) { 179 wmf = dirlookup(wmf, tm.names[i]); 180 if (wmf == nil) { 181 if (nc == nil) { 182 c.qid = oqid; 183 c.path = opath; 184 } else 185 srv.chanfree(nc); 186 if (i == 0) 187 srv.reply(ref Rmsg.Error(tm.tag, Styxlib->Enotfound)); 188 else 189 srv.reply(ref Rmsg.Walk(tm.tag, qids[0:i])); 190 break Msg; 191 } 192 c.qid = wmf.qid; 193 qids[i] = wmf.qid; 194 } 195 } 196 srv.reply(ref Rmsg.Walk(tm.tag, qids)); 197 Open => 198 (err, c, mf) := fidtomf(srv, qhash, tm.fid); 199 if (err == "" && c.open) 200 err = Styxlib->Eopen; 201 if (err == "" && !modeok(tm.mode, mf.perm, c.uname, mf.owner)) 202 err = Styxlib->Eperm; 203 if (err == "" && (mf.perm & Sys->DMDIR) && (tm.mode & (OTRUNC|OWRITE|ORCLOSE))) 204 err = Styxlib->Eperm; 205 if (err == "" && (tm.mode & ORCLOSE)) { 206 p := mf.parent; 207 if (p == nil || !modeok(OWRITE, p.perm, c.uname, p.owner)) 208 err = Styxlib->Eperm; 209 } 210 211 if (err != "") { 212 srv.reply(ref Rmsg.Error(tm.tag, err)); 213 continue; 214 } 215 216 c.open = 1; 217 c.mode = tm.mode; 218 c.qid.vers = mf.qid.vers; 219 mf.nopen++; 220 if ((tm.mode & OTRUNC) && !(mf.perm & Sys->DMAPPEND)) { 221 # OTRUNC cannot be set for a directory 222 # always at least one blk so don't need to check fs limit 223 freeblks += (len mf.data); 224 mf.data = nil; 225 freeblks--; 226 mf.data = array[1] of {* => array [blksz] of byte}; 227 mf.length = 0; 228 mf.mtime = now(); 229 } 230 srv.reply(ref Rmsg.Open(tm.tag, mf.qid, Styx->MAXFDATA)); 231 Create => 232 (err, c, parent) := fidtomf(srv, qhash, tm.fid); 233 if (err == "" && c.open) 234 err = Styxlib->Eopen; 235 if (err == "" && !(parent.qid.qtype & Sys->QTDIR)) 236 err = Styxlib->Enotdir; 237 if (err == "" && !modeok(OWRITE, parent.perm, c.uname, parent.owner)) 238 err = Styxlib->Eperm; 239 if (err == "" && (tm.perm & Sys->DMDIR) && (tm.mode & (OTRUNC|OWRITE|ORCLOSE))) 240 err = Styxlib->Eperm; 241 if (err == "" && dirlookup(parent, tm.name) != nil) 242 err = Styxlib->Eexists; 243 244 if (err != "") { 245 srv.reply(ref Rmsg.Error(tm.tag, err)); 246 continue; 247 } 248 249 isdir := tm.perm & Sys->DMDIR; 250 if (!isdir && freeblks <= 0) { 251 srv.reply(ref Rmsg.Error(tm.tag, Efull)); 252 continue; 253 } 254 255 # modify perms as per Styx specification... 256 perm : int; 257 if (isdir) 258 perm = (tm.perm&~8r777) | (parent.perm&tm.perm&8r777); 259 else 260 perm = (tm.perm&(~8r777|8r111)) | (parent.perm&tm.perm& 8r666); 261 262 nmf := newmf(qhash, parent, tm.name, c.uname, perm); 263 if (!isdir) { 264 freeblks--; 265 nmf.data = array[1] of {* => array [blksz] of byte}; 266 } 267 268 # link in the new MemFile 269 nmf.next = parent.kids; 270 if (parent.kids != nil) 271 parent.kids.prev = nmf; 272 parent.kids = nmf; 273 274 c.open = 1; 275 c.mode = tm.mode; 276 c.qid = nmf.qid; 277 nmf.nopen = 1; 278 srv.reply(ref Rmsg.Create(tm.tag, nmf.qid, Styx->MAXFDATA)); 279 Read => 280 (err, c, mf) := fidtomf(srv, qhash, tm.fid); 281 if (err == "" && !c.open) 282 err = Styxlib->Ebadfid; 283 284 if (err != "") { 285 srv.reply(ref Rmsg.Error(tm.tag, err)); 286 continue; 287 } 288 data: array of byte = nil; 289 if (mf.perm & Sys->DMDIR) 290 data = dirdata(mf, int tm.offset, tm.count); 291 else 292 data = filedata(mf, int tm.offset, tm.count); 293 mf.atime = now(); 294 srv.reply(ref Rmsg.Read(tm.tag, data)); 295 Write => 296 (err, c, mf) := fidtomf(srv, qhash, tm.fid); 297 if (c != nil && !c.open) 298 err = Styxlib->Ebadfid; 299 if (err == nil && (mf.perm & Sys->DMDIR)) 300 err = Styxlib->Eperm; 301 if (err == nil) 302 err = writefile(mf, int tm.offset, tm.data); 303 if (err != nil) { 304 srv.reply(ref Rmsg.Error(tm.tag, err)); 305 continue; 306 } 307 srv.reply(ref Rmsg.Write(tm.tag, len tm.data)); 308 Clunk => 309 (err, c, mf) := fidtomf(srv, qhash, tm.fid); 310 if (c != nil) 311 srv.chanfree(c); 312 if (err != nil) { 313 srv.reply(ref Rmsg.Error(tm.tag, err)); 314 continue; 315 } 316 if (c.open) { 317 if (c.mode & ORCLOSE) 318 unlink(mf); 319 mf.nopen--; 320 freeblks += delfile(qhash, mf); 321 } 322 srv.reply(ref Rmsg.Clunk(tm.tag)); 323 Stat => 324 (err, nil, mf) := fidtomf(srv, qhash, tm.fid); 325 if (err != nil) { 326 srv.reply(ref Rmsg.Error(tm.tag, err)); 327 continue; 328 } 329 srv.reply(ref Rmsg.Stat(tm.tag, fileinfo(mf))); 330 Remove => 331 (err, c, mf) := fidtomf(srv, qhash, tm.fid); 332 if (err != nil) { 333 srv.reply(ref Rmsg.Error(tm.tag, err)); 334 continue; 335 } 336 srv.chanfree(c); 337 parent := mf.parent; 338 if (!modeok(OWRITE, parent.perm, c.uname, parent.owner)) 339 err = Styxlib->Eperm; 340 if (err == "" && (mf.perm & Sys->DMDIR) && mf.kids != nil) 341 err = "directory not empty"; 342 if (err == "" && mf == root) 343 err = "root directory"; 344 if (err != nil) { 345 srv.reply(ref Rmsg.Error(tm.tag, err)); 346 continue; 347 } 348 349 unlink(mf); 350 if (c.open) 351 mf.nopen--; 352 freeblks += delfile(qhash, mf); 353 srv.reply(ref Rmsg.Remove(tm.tag)); 354 Wstat => 355 (err, c, mf) := fidtomf(srv, qhash, tm.fid); 356 stat := tm.stat; 357 358 if (err == nil && stat.name != mf.name) { 359 parent := mf.parent; 360 if (!modeok(OWRITE, parent.perm, c.uname, parent.owner)) 361 err = Styxlib->Eperm; 362 else if (dirlookup(parent, stat.name) != nil) 363 err = Styxlib->Eexists; 364 } 365 if (err == nil && (stat.mode != mf.perm || stat.mtime != mf.mtime)) { 366 if (c.uname != mf.owner) 367 err = Styxlib->Eperm; 368 } 369 if (err != nil) { 370 srv.reply(ref Rmsg.Error(tm.tag, err)); 371 continue; 372 } 373 isdir := mf.perm & Sys->DMDIR; 374 if(stat.name != nil) 375 mf.name = stat.name; 376 if(stat.mode != ~0) 377 mf.perm = stat.mode | isdir; 378 if(stat.mtime != ~0) 379 mf.mtime = stat.mtime; 380 if(stat.uid != nil) 381 mf.owner = stat.uid; 382 t := now(); 383 mf.atime = t; 384 mf.parent.mtime = t; 385 # not supporting group id at the moment 386 srv.reply(ref Rmsg.Wstat(tm.tag)); 387 Attach => 388 c := srv.newchan(tm.fid); 389 if (c == nil) { 390 srv.reply(ref Rmsg.Error(tm.tag, Styxlib->Einuse)); 391 continue; 392 } 393 c.uname = tm.uname; 394 c.qid = root.qid; 395 srv.reply(ref Rmsg.Attach(tm.tag, c.qid)); 396 } 397 } 398} 399 400writefile(mf: ref Memfile, offset: int, data: array of byte): string 401{ 402 if(mf.perm & Sys->DMAPPEND) 403 offset = mf.length; 404 startblk := offset/blksz; 405 nblks := ((len data + offset) - (startblk * blksz))/blksz; 406 lastblk := startblk + nblks; 407 need := lastblk + 1 - len mf.data; 408 if (need > 0) { 409 if (need > freeblks) 410 return Efull; 411 mf.data = (array [lastblk+1] of array of byte)[:] = mf.data; 412 freeblks -= need; 413 } 414 mf.length = max(mf.length, offset + len data); 415 416 # handle (possibly incomplete first block) separately 417 offset %= blksz; 418 end := min(blksz-offset, len data); 419 if (mf.data[startblk] == nil) 420 mf.data[startblk] = array [blksz] of byte; 421 mf.data[startblk++][offset:] = data[:end]; 422 423 ix := blksz - offset; 424 while (ix < len data) { 425 if (mf.data[startblk] == nil) 426 mf.data[startblk] = array [blksz] of byte; 427 end = min(ix+blksz,len data); 428 mf.data[startblk++][:] = data[ix:end]; 429 ix += blksz; 430 } 431 mf.mtime = now(); 432 return nil; 433} 434 435filedata(mf: ref Memfile, offset, n: int): array of byte 436{ 437 if (offset +n > mf.length) 438 n = mf.length - offset; 439 if (n == 0) 440 return nil; 441 442 data := array [n] of byte; 443 startblk := offset/blksz; 444 offset %= blksz; 445 rn := min(blksz - offset, n); 446 data[:] = mf.data[startblk++][offset:offset+rn]; 447 ix := blksz - offset; 448 while (ix < n) { 449 rn = blksz; 450 if (ix+rn > n) 451 rn = n - ix; 452 data[ix:] = mf.data[startblk++][:rn]; 453 ix += blksz; 454 } 455 return data; 456} 457 458QHSIZE: con 256; 459QHMASK: con QHSIZE-1; 460 461Qidhash.new() : ref Qidhash 462{ 463 qh := ref Qidhash; 464 qh.buckets = array [QHSIZE] of ref Memfile; 465 qh.nextqid = 0; 466 return qh; 467} 468 469Qidhash.add(h : self ref Qidhash, mf : ref Memfile) 470{ 471 path := h.nextqid++; 472 mf.qid = Sys->Qid(big path, 0, Sys->QTFILE); 473 bix := path & QHMASK; 474 mf.hashnext = h.buckets[bix]; 475 h.buckets[bix] = mf; 476} 477 478Qidhash.remove(h : self ref Qidhash, mf : ref Memfile) 479{ 480 481 bix := int mf.qid.path & QHMASK; 482 prev : ref Memfile; 483 for (cur := h.buckets[bix]; cur != nil; cur = cur.hashnext) { 484 if (cur == mf) 485 break; 486 prev = cur; 487 } 488 if (cur != nil) { 489 if (prev != nil) 490 prev.hashnext = cur.hashnext; 491 else 492 h.buckets[bix] = cur.hashnext; 493 cur.hashnext = nil; 494 } 495} 496 497Qidhash.lookup(h : self ref Qidhash, qid : Sys->Qid) : ref Memfile 498{ 499 bix := int qid.path & QHMASK; 500 for (mf := h.buckets[bix]; mf != nil; mf = mf.hashnext) 501 if (mf.qid.path == qid.path) 502 break; 503 return mf; 504} 505 506newmf(qh : ref Qidhash, parent : ref Memfile, name, owner : string, perm : int) : ref Memfile 507{ 508 # qid gets set by Qidhash.add() 509 t := now(); 510 mf := ref Memfile (name, owner, Sys->Qid(big 0,0,Sys->QTFILE), perm, t, t, 0, nil, 0, parent, nil, nil, nil, nil); 511 qh.add(mf); 512 if(perm & Sys->DMDIR) 513 mf.qid.qtype = Sys->QTDIR; 514 return mf; 515} 516 517fidtomf(srv : ref Styxserver, qh : ref Qidhash, fid : int) : (string, ref Styxlib->Chan, ref Memfile) 518{ 519 c := srv.fidtochan(fid); 520 if (c == nil) 521 return (Styxlib->Ebadfid, nil, nil); 522 mf := qh.lookup(c.qid); 523 if (mf == nil) 524 return (Styxlib->Enotfound, c, nil); 525 return (nil, c, mf); 526} 527 528unlink(mf : ref Memfile) 529{ 530 parent := mf.parent; 531 if (parent == nil) 532 return; 533 if (mf.next != nil) 534 mf.next.prev = mf.prev; 535 if (mf.prev != nil) 536 mf.prev.next = mf.next; 537 else 538 mf.parent.kids = mf.next; 539 mf.parent = nil; 540 mf.prev = nil; 541 mf.next = nil; 542} 543 544delfile(qh : ref Qidhash, mf : ref Memfile) : int 545{ 546 if (mf.nopen <= 0 && mf.parent == nil && mf.kids == nil 547 && mf.prev == nil && mf.next == nil) { 548 qh.remove(mf); 549 nblks := len mf.data; 550 mf.data = nil; 551 return nblks; 552 } 553 return 0; 554} 555 556dirlookup(dir : ref Memfile, name : string) : ref Memfile 557{ 558 if (name == ".") 559 return dir; 560 if (name == "..") 561 return dir.parent; 562 for (mf := dir.kids; mf != nil; mf = mf.next) { 563 if (mf.name == name) 564 break; 565 } 566 return mf; 567} 568 569access := array[] of {8r400, 8r200, 8r600, 8r100}; 570modeok(mode, perm : int, user, owner : string) : int 571{ 572 if(mode >= (OTRUNC|ORCLOSE|OREAD|OWRITE)) 573 return 0; 574 575 # not handling groups! 576 if (user != owner) 577 perm <<= 6; 578 579 if ((mode & OTRUNC) && !(perm & 8r200)) 580 return 0; 581 582 a := access[mode &3]; 583 if ((a & perm) != a) 584 return 0; 585 return 1; 586} 587 588dirdata(dir : ref Memfile, start, n : int) : array of byte 589{ 590 data := array[Styx->MAXFDATA] of byte; 591 for (k := dir.kids; start > 0 && k != nil; k = k.next) { 592 a := styx->packdir(fileinfo(k)); 593 start -= len a; 594 } 595 r := 0; 596 for (; r < n && k != nil; k = k.next) { 597 a := styx->packdir(fileinfo(k)); 598 if(r+len a > n) 599 break; 600 data[r:] = a; 601 r += len a; 602 } 603 return data[0:r]; 604} 605 606fileinfo(f : ref Memfile) : Sys->Dir 607{ 608 dir := sys->zerodir; 609 dir.name = f.name; 610 dir.uid = f.owner; 611 dir.gid = "memfs"; 612 dir.qid = f.qid; 613 dir.mode = f.perm; 614 dir.atime = f.atime; 615 dir.mtime = f.mtime; 616 dir.length = big f.length; 617 dir.dtype = 0; 618 dir.dev = 0; 619 return dir; 620} 621 622min(a, b : int) : int 623{ 624 if (a < b) 625 return a; 626 return b; 627} 628 629max(a, b : int) : int 630{ 631 if (a > b) 632 return a; 633 return b; 634} 635 636now(): int 637{ 638 if (timefd == nil) 639 return 0; 640 buf := array[128] of byte; 641 sys->seek(timefd, big 0, 0); 642 n := sys->read(timefd, buf, len buf); 643 if(n < 0) 644 return 0; 645 646 t := (big string buf[0:n]) / big 1000000; 647 return int t; 648} 649