1implement Lockfs; 2include "sys.m"; 3 sys: Sys; 4 stderr: ref Sys->FD; 5include "draw.m"; 6include "styx.m"; 7 styx: Styx; 8 Tmsg, Rmsg: import styx; 9include "styxlib.m"; 10 styxlib: Styxlib; 11 Dirtab, Styxserver, Chan, 12 devdir, 13 Eperm, Ebadfid, Eexists, Enotdir, Enotfound, Einuse: import styxlib; 14include "arg.m"; 15include "keyring.m"; 16 keyring: Keyring; 17include "security.m"; 18 auth: Auth; 19include "dial.m"; 20 dial: Dial; 21 22Lockfs: module { 23 init: fn(nil: ref Draw->Context, argv: list of string); 24 dirgen: fn(srv: ref Styxlib->Styxserver, c: ref Styxlib->Chan, 25 tab: array of Styxlib->Dirtab, i: int): (int, Sys->Dir); 26}; 27 28Elocked: con "file is locked"; 29 30devgen: Dirgenmod; 31 32Openreq: adt { 33 srv: ref Styxserver; 34 tag: int; 35 omode: int; 36 c: ref Chan; 37 uproc: Uproc; 38}; 39 40Lockqueue: adt { 41 h: list of ref Openreq; 42 t: list of ref Openreq; 43 put: fn(q: self ref Lockqueue, s: ref Openreq); 44 get: fn(q: self ref Lockqueue): ref Openreq; 45 peek: fn(q: self ref Lockqueue): ref Openreq; 46 flush: fn(q: self ref Lockqueue, srv: ref Styxserver, tag: int); 47}; 48 49Lockfile: adt { 50 waitq: ref Lockqueue; 51 fd: ref Sys->FD; 52 readers: int; 53 writers: int; 54 d: Sys->Dir; 55}; 56 57Ureq: adt { 58 fname: string; 59 pick { 60 Open => 61 omode: int; 62 Create => 63 omode: int; 64 perm: int; 65 Remove => 66 Wstat => 67 dir: Sys->Dir; 68 } 69}; 70 71Uproc: type chan of (ref Ureq, chan of (ref Sys->FD, string)); 72 73maxqidpath := big 1; 74locks: list of ref Lockfile; 75lockdir: string; 76authinfo: ref Keyring->Authinfo; 77timefd: ref Sys->FD; 78 79MAXCONN: con 20; 80 81verbose := 0; 82 83usage() 84{ 85 sys->fprint(stderr, "usage: lockfs [-A] [-a alg]... [-p addr] dir [mountpoint]\n"); 86 raise "fail:usage"; 87} 88 89badmodule(p: string) 90{ 91 sys->fprint(stderr, "lockfs: cannot load %s: %r\n", p); 92 raise "fail:bad module"; 93} 94 95init(nil: ref Draw->Context, argv: list of string) 96{ 97 sys = load Sys Sys->PATH; 98 stderr = sys->fildes(2); 99 styx = load Styx Styx->PATH; 100 if (styx == nil) 101 badmodule(Styx->PATH); 102 dial = load Dial Dial->PATH; 103 if (dial == nil) 104 badmodule(Dial->PATH); 105 styx->init(); 106 styxlib = load Styxlib Styxlib->PATH; 107 if (styxlib == nil) 108 badmodule(Styxlib->PATH); 109 styxlib->init(styx); 110 devgen = load Dirgenmod "$self"; 111 if (devgen == nil) 112 badmodule("self as Dirgenmod"); 113 timefd = sys->open("/dev/time", sys->OREAD); 114 if (timefd == nil) { 115 sys->fprint(stderr, "lockfs: cannot open /dev/time: %r\n"); 116 raise "fail:no time"; 117 } 118 arg := load Arg Arg->PATH; 119 if (arg == nil) 120 badmodule(Arg->PATH); 121 arg->init(argv); 122 123 addr := ""; 124 doauth := 1; 125 algs: list of string; 126 while ((opt := arg->opt()) != 0) { 127 case opt { 128 'p' => 129 addr = arg->arg(); 130 'a' => 131 alg := arg->arg(); 132 if (alg == nil) 133 usage(); 134 algs = alg :: algs; 135 'A' => 136 doauth = 0; 137 'v' => 138 verbose = 1; 139 * => 140 usage(); 141 } 142 } 143 argv = arg->argv(); 144 if (argv == nil || (addr != nil && tl argv != nil)) 145 usage(); 146 if (addr == nil) 147 doauth = 0; # no authentication necessary for local mount 148 if (doauth) { 149 auth = load Auth Auth->PATH; 150 if (auth == nil) 151 badmodule(Auth->PATH); 152 if ((e := auth->init()) != nil) { 153 sys->fprint(stderr, "lockfs: cannot init auth: %s\n", e); 154 raise "fail:errors"; 155 } 156 keyring = load Keyring Keyring->PATH; 157 if (keyring == nil) 158 badmodule(Keyring->PATH); 159 authinfo = keyring->readauthinfo("/usr/" + user() + "/keyring/default"); 160 } 161 162 mountpoint := lockdir = hd argv; 163 if (tl argv != nil) 164 mountpoint = hd tl argv; 165 if (addr != nil) { 166 if (doauth && algs == nil) 167 algs = "none" :: nil; # XXX is this default a bad idea? 168 srvrq := chan of (ref Sys->FD, string, Uproc); 169 srvsync := chan of (int, string); 170 spawn listener(addr, srvrq, srvsync, algs); 171 (srvpid, err) := <-srvsync; 172 srvsync = nil; 173 if (srvpid == -1) { 174 sys->fprint(stderr, "lockfs: failed to start listener: %s\n", err); 175 raise "fail:errors"; 176 } 177 sync := chan of int; 178 spawn server(srvrq, sync); 179 <-sync; 180 } else { 181 rq := chan of (ref Sys->FD, string, Uproc); 182 fds := array[2] of ref Sys->FD; 183 sys->pipe(fds); 184 sync := chan of int; 185 spawn server(rq, sync); 186 <-sync; 187 rq <-= (fds[0], "lock", nil); 188 rq <-= (nil, nil, nil); 189 if (sys->mount(fds[1], nil, mountpoint, Sys->MREPL | Sys->MCREATE, nil) == -1) { 190 sys->fprint(stderr, "lockfs: cannot mount: %r\n"); 191 raise "fail:cannot mount"; 192 } 193 } 194} 195 196server(srvrq: chan of (ref Sys->FD, string, Uproc), sync: chan of int) 197{ 198 sys->pctl(Sys->FORKNS, nil); 199 sync <-= 1; 200 down := 0; 201 nclient := 0; 202 tchans := array[MAXCONN] of chan of ref Tmsg; 203 srv := array[MAXCONN] of ref Styxserver; 204 uprocs := array[MAXCONN] of Uproc; 205 lockinit(); 206Service: 207 for (;;) alt { 208 (fd, reqstr, uprocch) := <-srvrq => 209 if (fd == nil) { 210 if (verbose && reqstr != nil) 211 sys->print("lockfs: localserver going down (reason: %s)\n", reqstr); 212 down = 1; 213 } else { 214 if (verbose) 215 sys->print("lockfs: got new connection (s == '%s')\n", reqstr); 216 for (i := 0; i < len tchans; i++) 217 if (tchans[i] == nil) { 218 (tchans[i], srv[i]) = Styxserver.new(fd); 219 if(verbose) 220 sys->print("svc started\n"); 221 uprocs[i] = uprocch; 222 break; 223 } 224 if (i == len tchans) { 225 sys->fprint(stderr, "lockfs: too many clients\n"); # XXX expand arrays 226 if (uprocch != nil) 227 uprocch <-= (nil, nil); 228 } else 229 nclient++; 230 } 231 (n, gm) := <-tchans => 232 if (handletmsg(srv[n], gm, uprocs[n]) == -1) { 233 tchans[n] = nil; 234 srv[n] = nil; 235 if (uprocs[n] != nil) { 236 uprocs[n] <-= (nil, nil); 237 uprocs[n] = nil; 238 } 239 if (nclient-- <= 1 && down) 240 break Service; 241 } 242 } 243 if (verbose) 244 sys->print("lockfs: finished\n"); 245} 246 247dirgen(nil: ref Styxserver, nil: ref Styxlib->Chan, 248 nil: array of Dirtab, s: int): (int, Sys->Dir) 249{ 250 d: Sys->Dir; 251 ll := locks; 252 for (i := 0; i < s && ll != nil; i++) 253 ll = tl ll; 254 if (ll == nil) 255 return (-1, d); 256 return (1, (hd ll).d); 257} 258 259handletmsg(srv: ref Styxserver, gm: ref Tmsg, uproc: Uproc): int 260{ 261{ 262 if (gm == nil) 263 gm = ref Tmsg.Readerror(-1, "eof"); 264 if(verbose) 265 sys->print("<- %s\n", gm.text()); 266 pick m := gm { 267 Readerror => 268 # could be more efficient... 269 for (cl := srv.chanlist(); cl != nil; cl = tl cl) { 270 c := hd cl; 271 for (ll := locks; ll != nil; ll = tl ll) { 272 if ((hd ll).d.qid.path == c.qid.path) { 273 l := hd ll; 274 l.waitq.flush(srv, -1); 275 if (c.open) 276 unlocked(l); 277 break; 278 } 279 } 280 } 281 if (m.error != "eof") 282 sys->fprint(stderr, "lockfs: read error: %s\n", m.error); 283 return -1; 284 Version => 285 srv.devversion(m); 286 Auth => 287 srv.devauth(m); 288 Walk => 289 c := fid2chan(srv, m.fid); 290 qids: array of Sys->Qid; 291 cc := ref *c; 292 if (len m.names > 0) { 293 qids = array[1] of Sys->Qid; # it's just one level 294 if ((cc.qid.qtype & Sys->QTDIR) == 0) { 295 srv.reply(ref Rmsg.Error(m.tag, Enotdir)); 296 break; 297 } 298 for (ll := locks; ll != nil; ll = tl ll) 299 if ((hd ll).d.name == m.names[0]) 300 break; 301 if (ll == nil) { 302 srv.reply(ref Rmsg.Error(m.tag, Enotfound)); 303 break; 304 } 305 d := (hd ll).d; 306 cc.qid = d.qid; 307 cc.path = d.name; 308 qids[0] = c.qid; 309 } 310 if(m.newfid != m.fid){ 311 nc := srv.clone(cc, m.newfid); 312 if(nc == nil){ 313 srv.reply(ref Rmsg.Error(m.tag, Einuse)); 314 break; 315 } 316 }else{ 317 c.qid = cc.qid; 318 c.path = cc.path; 319 } 320 srv.reply(ref Rmsg.Walk(m.tag, qids)); 321 Open => 322 c := fid2chan(srv, m.fid); 323 if (c.qid.qtype & Sys->QTDIR) { 324 srv.reply(ref Rmsg.Open(m.tag, c.qid, Styx->MAXFDATA)); 325 break; 326 } 327 for (ll := locks; ll != nil; ll = tl ll) 328 if ((hd ll).d.qid.path == c.qid.path) 329 break; 330 if (ll == nil) { 331 srv.reply(ref Rmsg.Error(m.tag, Enotfound)); 332 break; 333 } 334 l := hd ll; 335 req := ref Openreq(srv, m.tag, m.mode, c, uproc); 336 if (l.fd == nil || (m.mode == Sys->OREAD && l.writers == 0)) { 337 openlockfile(l, req); 338 } else { 339 l.waitq.put(req); 340 } 341 req = nil; 342 Create => 343 c := fid2chan(srv, m.fid); 344 if ((c.qid.qtype & Sys->QTDIR) == 0) { 345 srv.reply(ref Rmsg.Error(m.tag, Enotdir)); 346 break; 347 } 348 if (m.perm & Sys->DMDIR) { 349 srv.reply(ref Rmsg.Error(m.tag, Eperm)); 350 break; 351 } 352 for (ll := locks; ll != nil; ll = tl ll) 353 if ((hd ll).d.name == m.name) 354 break; 355 if (ll != nil) { 356 srv.reply(ref Rmsg.Error(m.tag, Eexists)); 357 break; 358 } 359 (fd, err) := create(uproc, lockdir + "/" + m.name, m.mode, m.perm); 360 if (fd == nil) { 361 srv.reply(ref Rmsg.Error(m.tag, err)); 362 break; 363 } 364 (ok, d) := sys->fstat(fd); 365 if (ok == -1) { 366 srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r"))); 367 break; 368 } 369 l := ref Lockfile(ref Lockqueue, fd, 0, 0, d); 370 l.d.qid = (maxqidpath++, 0, Sys->QTFILE); 371 l.d.mtime = l.d.atime = now(); 372 if (m.mode == Sys->OREAD) 373 l.readers = 1; 374 else 375 l.writers = 1; 376 locks = l :: locks; 377 c.qid.path = (hd locks).d.qid.path; 378 c.open = 1; 379 srv.reply(ref Rmsg.Create(m.tag, c.qid, Styx->MAXFDATA)); 380 Read => 381 c := fid2chan(srv, m.fid); 382 if (c.qid.qtype & Sys->QTDIR) 383 srv.devdirread(m, devgen, nil); 384 else { 385 l := qid2lock(c.qid); 386 if (l == nil) 387 srv.reply(ref Rmsg.Error(m.tag, Enotfound)); 388 else { 389 d := array[m.count] of byte; 390 sys->seek(l.fd, m.offset, Sys->SEEKSTART); 391 n := sys->read(l.fd, d, m.count); 392 if (n == -1) 393 srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r"))); 394 else { 395 srv.reply(ref Rmsg.Read(m.tag, d[0:n])); 396 l.d.atime = now(); 397 } 398 } 399 } 400 Write => 401 c := fid2chan(srv, m.fid); 402 if (c.qid.qtype & Sys->QTDIR) { 403 srv.reply(ref Rmsg.Error(m.tag, Eperm)); 404 break; 405 } 406 l := qid2lock(c.qid); 407 if (l == nil) { 408 srv.reply(ref Rmsg.Error(m.tag, Enotfound)); 409 break; 410 } 411 sys->seek(l.fd, m.offset, Sys->SEEKSTART); 412 n := sys->write(l.fd, m.data, len m.data); 413 if (n == -1) 414 srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r"))); 415 else { 416 srv.reply(ref Rmsg.Write(m.tag, n)); 417 nlength := m.offset + big n; 418 if (nlength > l.d.length) 419 l.d.length = nlength; 420 l.d.mtime = now(); 421 l.d.qid.vers++; 422 } 423 Clunk => 424 c := srv.devclunk(m); 425 if (c != nil && c.open && (l := qid2lock(c.qid)) != nil) 426 unlocked(l); 427 Flush => 428 for (ll := locks; ll != nil; ll = tl ll) 429 (hd ll).waitq.flush(srv, m.tag); 430 srv.reply(ref Rmsg.Flush(m.tag)); 431 Stat => 432 srv.devstat(m, devgen, nil); 433 Remove => 434 c := fid2chan(srv, m.fid); 435 srv.chanfree(c); 436 if (c.qid.qtype & Sys->QTDIR) { 437 srv.reply(ref Rmsg.Error(m.tag, Eperm)); 438 break; 439 } 440 l := qid2lock(c.qid); 441 if (l == nil) { 442 srv.reply(ref Rmsg.Error(m.tag, Enotfound)); 443 break; 444 } 445 if (l.fd != nil) { 446 srv.reply(ref Rmsg.Error(m.tag, Elocked)); 447 break; 448 } 449 if ((err := remove(uproc, lockdir + "/" + l.d.name)) == nil) { 450 srv.reply(ref Rmsg.Error(m.tag, err)); 451 break; 452 } 453 ll: list of ref Lockfile; 454 for (; locks != nil; locks = tl locks) 455 if (hd locks != l) 456 ll = hd locks :: ll; 457 locks = ll; 458 srv.reply(ref Rmsg.Remove(m.tag)); 459 Wstat => 460 c := fid2chan(srv, m.fid); 461 if (c.qid.qtype & Sys->QTDIR) { 462 srv.reply(ref Rmsg.Error(m.tag, Eperm)); 463 break; 464 } 465 l := qid2lock(c.qid); 466 if (l == nil) { 467 srv.reply(ref Rmsg.Error(m.tag, Enotfound)); 468 break; 469 } 470 if ((err := wstat(uproc, lockdir + "/" + l.d.name, m.stat)) != nil) { 471 srv.reply(ref Rmsg.Error(m.tag, err)); 472 break; 473 } 474 (ok, d) := sys->stat(lockdir + "/" + m.stat.name); 475 if (ok == -1) { 476 srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r"))); 477 break; 478 } 479 d.qid = l.d.qid; 480 l.d = d; 481 srv.reply(ref Rmsg.Wstat(m.tag)); 482 Attach => 483 srv.devattach(m); 484 } 485 return 0; 486} 487exception e{ 488 "panic:*" => 489 sys->fprint(stderr, "lockfs: %s\n", e); 490 srv.reply(ref Rmsg.Error(gm.tag, e[len "panic:":])); 491 return 0; 492} 493} 494 495unlocked(l: ref Lockfile) 496{ 497 if (l.readers > 0) 498 l.readers--; 499 else 500 l.writers--; 501 if (l.readers > 0) 502 return; 503 l.fd = nil; 504 505 # unblock all readers at the head of the queue. 506 # XXX should we queuejump other readers? 507 while ((nreq := l.waitq.peek()) != nil && l.writers == 0) { 508 if (nreq.omode != Sys->OREAD && l.readers > 0) 509 break; 510 openlockfile(l, nreq); 511 l.waitq.get(); 512 } 513} 514 515openlockfile(l: ref Lockfile, req: ref Openreq): int 516{ 517 err: string; 518 (l.fd, err) = open(req.uproc, lockdir + "/" + l.d.name, req.omode); 519 if (l.fd == nil) { 520 req.srv.reply(ref Rmsg.Error(req.tag, err)); 521 return -1; 522 } 523 req.c.open = 1; 524 if (req.omode & Sys->OTRUNC) 525 l.d.length = big 0; 526 req.srv.reply(ref Rmsg.Open(req.tag, l.d.qid, Styx->MAXFDATA)); 527 if (req.omode == Sys->OREAD) 528 l.readers++; 529 else 530 l.writers++; 531 return 0; 532} 533 534qid2lock(q: Sys->Qid): ref Lockfile 535{ 536 for (ll := locks; ll != nil; ll = tl ll) 537 if ((hd ll).d.qid.path == q.path) 538 return hd ll; 539 return nil; 540} 541 542lockinit() 543{ 544 fd := sys->open(lockdir, Sys->OREAD); 545 if (fd == nil) 546 return; 547 548 lockl: list of ref Lockfile; 549 # XXX if O(n²) behaviour is a problem, use Readdir module 550 for(;;){ 551 (n, e) := sys->dirread(fd); 552 if(n <= 0) 553 break; 554 for (i := 0; i < n; i++) { 555 for (l := lockl; l != nil; l = tl l) 556 if ((hd l).d.name == e[i].name) 557 break; 558 if (l == nil) { 559 e[i].qid = (maxqidpath++, 0, Sys->QTFILE); 560 lockl = ref Lockfile(ref Lockqueue, nil, 0, 0, e[i]) :: lockl; 561 } 562 } 563 } 564 # remove all directories from list 565 for (locks = nil; lockl != nil; lockl = tl lockl) 566 if (((hd lockl).d.mode & Sys->DMDIR) == 0) 567 locks = hd lockl :: locks; 568} 569 570 571fid2chan(srv: ref Styxserver, fid: int): ref Chan 572{ 573 c := srv.fidtochan(fid); 574 if (c == nil) 575 raise "panic:bad fid"; 576 return c; 577} 578 579Lockqueue.put(q: self ref Lockqueue, s: ref Openreq) 580{ 581 q.t = s :: q.t; 582} 583 584Lockqueue.get(q: self ref Lockqueue): ref Openreq 585{ 586 s: ref Openreq; 587 if(q.h == nil) 588 (q.h, q.t) = (revrqlist(q.t), nil); 589 590 if(q.h != nil) 591 (s, q.h) = (hd q.h, tl q.h); 592 593 return s; 594} 595 596Lockqueue.peek(q: self ref Lockqueue): ref Openreq 597{ 598 s := q.get(); 599 if (s != nil) 600 q.h = s :: q.h; 601 return s; 602} 603 604doflush(l: list of ref Openreq, srv: ref Styxserver, tag: int): list of ref Openreq 605{ 606 oldl := l; 607 nl: list of ref Openreq; 608 doneone := 0; 609 while (l != nil) { 610 oreq := hd l; 611 if (oreq.srv != srv || (tag != -1 && oreq.tag != tag)) 612 nl = oreq :: nl; 613 else 614 doneone = 1; 615 l = tl l; 616 } 617 if (doneone) 618 return revrqlist(nl); 619 else 620 return oldl; 621} 622 623Lockqueue.flush(q: self ref Lockqueue, srv: ref Styxserver, tag: int) 624{ 625 q.h = doflush(q.h, srv, tag); 626 q.t = doflush(q.t, srv, tag); 627} 628 629# or inline 630revrqlist(ls: list of ref Openreq) : list of ref Openreq 631{ 632 rs: list of ref Openreq; 633 while(ls != nil){ 634 rs = hd ls :: rs; 635 ls = tl ls; 636 } 637 return rs; 638} 639 640# addr should be, e.g. tcp!*!2345 641listener(addr: string, ch: chan of (ref Sys->FD, string, Uproc), 642 sync: chan of (int, string), algs: list of string) 643{ 644 addr = dial->netmkaddr(addr, "tcp", "33234"); 645 c := dial->announce(addr); 646 if (c == nil) { 647 sync <-= (-1, sys->sprint("cannot anounce on %s: %r", addr)); 648 return; 649 } 650 sync <-= (sys->pctl(0, nil), nil); 651 for (;;) { 652 nc := dial->listen(c); 653 if (nc == nil) { 654 ch <-= (nil, sys->sprint("listen failed: %r"), nil); 655 return; 656 } 657 dfd := sys->open(nc.dir + "/data", Sys->ORDWR); 658 if (dfd != nil) { 659 if (algs == nil) 660 ch <-= (dfd, nil, nil); 661 else 662 spawn authenticator(dfd, ch, algs); 663 } 664 } 665} 666 667# authenticate a connection, setting the user id appropriately, 668# and then act as a server, performing file operations 669# on behalf of the central process. 670authenticator(dfd: ref Sys->FD, ch: chan of (ref Sys->FD, string, Uproc), algs: list of string) 671{ 672 (fd, err) := auth->server(algs, authinfo, dfd, 1); 673 if (fd == nil) { 674 if (verbose) 675 sys->fprint(stderr, "lockfs: authentication failed: %s\n", err); 676 return; 677 } 678 uproc := chan of (ref Ureq, chan of (ref Sys->FD, string)); 679 ch <-= (fd, err, uproc); 680 for (;;) { 681 (req, reply) := <-uproc; 682 if (req == nil) 683 exit; 684 reply <-= doreq(req); 685 } 686} 687 688create(uproc: Uproc, file: string, omode: int, perm: int): (ref Sys->FD, string) 689{ 690 return proxydoreq(uproc, ref Ureq.Create(file, omode, perm)); 691} 692 693open(uproc: Uproc, file: string, omode: int): (ref Sys->FD, string) 694{ 695 return proxydoreq(uproc, ref Ureq.Open(file, omode)); 696} 697 698remove(uproc: Uproc, file: string): string 699{ 700 return proxydoreq(uproc, ref Ureq.Remove(file)).t1; 701} 702 703wstat(uproc: Uproc, file: string, d: Sys->Dir): string 704{ 705 return proxydoreq(uproc, ref Ureq.Wstat(file, d)).t1; 706} 707 708proxydoreq(uproc: Uproc, req: ref Ureq): (ref Sys->FD, string) 709{ 710 if (uproc == nil) 711 return doreq(req); 712 reply := chan of (ref Sys->FD, string); 713 uproc <-= (req, reply); 714 return <-reply; 715} 716 717doreq(greq: ref Ureq): (ref Sys->FD, string) 718{ 719 fd: ref Sys->FD; 720 err: string; 721 pick req := greq { 722 Open => 723 if ((fd = sys->open(req.fname, req.omode)) == nil) 724 err = sys->sprint("%r"); 725 Create => 726 if ((fd = sys->create(req.fname, req.omode, req.perm)) == nil) 727 err = sys->sprint("%r"); 728 Remove => 729 if (sys->remove(req.fname) == -1) 730 err = sys->sprint("%r"); 731 Wstat => 732 if (sys->wstat(req.fname, req.dir) == -1) 733 err = sys->sprint("%r"); 734 } 735 return (fd, err); 736} 737 738user(): string 739{ 740 fd := sys->open("/dev/user", sys->OREAD); 741 if(fd == nil){ 742 sys->fprint(stderr, "lockfs: can't open /dev/user: %r\n"); 743 raise "fail:no user"; 744 } 745 746 buf := array[Sys->NAMEMAX] of byte; 747 n := sys->read(fd, buf, len buf); 748 if(n < 0) { 749 sys->fprint(stderr, "lockfs: failed to read /dev/user: %r\n"); 750 raise "fail:no user"; 751 } 752 753 return string buf[0:n]; 754} 755 756now(): int 757{ 758 buf := array[128] of byte; 759 sys->seek(timefd, big 0, 0); 760 if ((n := sys->read(timefd, buf, len buf)) < 0) 761 return 0; 762 return int (big string buf[0:n] / big 1000000); 763} 764