9fans.net/go@v0.0.7/cmd/acme/fsys1.go (about) 1 // #include <u.h> 2 // #include <libc.h> 3 // #include <draw.h> 4 // #include <thread.h> 5 // #include <cursor.h> 6 // #include <mouse.h> 7 // #include <keyboard.h> 8 // #include <frame.h> 9 // #include <fcall.h> 10 // #include <plumb.h> 11 // #include <libsec.h> 12 // #include "dat.h" 13 // #include "fns.h" 14 15 package main 16 17 import ( 18 "fmt" 19 "os" 20 "sort" 21 "strconv" 22 "strings" 23 "sync" 24 "time" 25 26 "9fans.net/go/cmd/acme/internal/ui" 27 "9fans.net/go/cmd/acme/internal/util" 28 "9fans.net/go/cmd/acme/internal/wind" 29 "9fans.net/go/cmd/internal/base" 30 "9fans.net/go/plan9" 31 ) 32 33 func QID(w, q int) uint64 { return uint64(w<<8 | q) } 34 func WIN(q plan9.Qid) int { return int(q.Path>>8) & 0xFFFFFF } 35 func FILE(q plan9.Qid) int { return int(q.Path & 0xFF) } 36 37 var sfdR, sfdW *os.File 38 39 const ( 40 Nhash = 16 41 DEBUG = 0 42 ) 43 44 var fids [Nhash]*Fid 45 46 var fcall [plan9.Tmax]func(*Xfid, *Fid) *Xfid 47 48 func initfcall() { 49 fcall[plan9.Tflush] = fsysflush 50 fcall[plan9.Tversion] = fsysversion 51 fcall[plan9.Tauth] = fsysauth 52 fcall[plan9.Tattach] = fsysattach 53 fcall[plan9.Twalk] = fsyswalk 54 fcall[plan9.Topen] = fsysopen 55 fcall[plan9.Tcreate] = fsyscreate 56 fcall[plan9.Tread] = fsysread 57 fcall[plan9.Twrite] = fsyswrite 58 fcall[plan9.Tclunk] = fsysclunk 59 fcall[plan9.Tremove] = fsysremove 60 fcall[plan9.Tstat] = fsysstat 61 fcall[plan9.Twstat] = fsyswstat 62 } 63 64 var Eperm string = "permission denied" 65 var Eexist string = "file does not exist" 66 var Enotdir string = "not a directory" 67 68 var dirtab = [11]Dirtab{ 69 Dirtab{".", plan9.QTDIR, Qdir, 0500 | plan9.DMDIR}, 70 Dirtab{"acme", plan9.QTDIR, Qacme, 0500 | plan9.DMDIR}, 71 Dirtab{"cons", plan9.QTFILE, Qcons, 0600}, 72 Dirtab{"consctl", plan9.QTFILE, Qconsctl, 0000}, 73 Dirtab{"draw", plan9.QTDIR, Qdraw, 0000 | plan9.DMDIR}, // to suppress graphics progs started in acme 74 Dirtab{"editout", plan9.QTFILE, Qeditout, 0200}, 75 Dirtab{"index", plan9.QTFILE, Qindex, 0400}, 76 Dirtab{"label", plan9.QTFILE, Qlabel, 0600}, 77 Dirtab{"log", plan9.QTFILE, Qlog, 0400}, 78 Dirtab{"new", plan9.QTDIR, Qnew, 0500 | plan9.DMDIR}, 79 } 80 81 var dirtabw = [13]Dirtab{ 82 Dirtab{".", plan9.QTDIR, Qdir, 0500 | plan9.DMDIR}, 83 Dirtab{"addr", plan9.QTFILE, QWaddr, 0600}, 84 Dirtab{"body", plan9.QTAPPEND, QWbody, 0600 | plan9.DMAPPEND}, 85 Dirtab{"ctl", plan9.QTFILE, QWctl, 0600}, 86 Dirtab{"data", plan9.QTFILE, QWdata, 0600}, 87 Dirtab{"editout", plan9.QTFILE, QWeditout, 0200}, 88 Dirtab{"errors", plan9.QTFILE, QWerrors, 0200}, 89 Dirtab{"event", plan9.QTFILE, QWevent, 0600}, 90 Dirtab{"rdsel", plan9.QTFILE, QWrdsel, 0400}, 91 Dirtab{"wrsel", plan9.QTFILE, QWwrsel, 0200}, 92 Dirtab{"tag", plan9.QTAPPEND, QWtag, 0600 | plan9.DMAPPEND}, 93 Dirtab{"xdata", plan9.QTFILE, QWxdata, 0600}, 94 } 95 96 type Mnt struct { 97 lk sync.Mutex 98 id int 99 md *base.Mntdir 100 } 101 102 var mnt Mnt 103 104 var user string = "Wile E. Coyote" 105 var closing bool 106 var messagesize int = 8192 + plan9.IOHDRSZ // good start 107 108 func fsysinit() { 109 initfcall() 110 r1, w1, err := os.Pipe() 111 if err != nil { 112 util.Fatal("can't create pipe") 113 } 114 r2, w2, err := os.Pipe() 115 if err != nil { 116 util.Fatal("can't create pipe") 117 } 118 sfdR, sfdW = r1, w2 119 if err := post9pservice(r2, w1, "acme", mtpt); err != nil { 120 util.Fatal("can't post service") 121 } 122 u := os.Getenv("USER") 123 if u != "" { 124 user = u 125 } 126 go fsysproc() 127 } 128 129 func fsysproc() { 130 var x *Xfid 131 for { 132 if x == nil { 133 cxfidalloc <- nil 134 x = <-cxfidalloc 135 } 136 fc, err := plan9.ReadFcall(sfdR) 137 if err != nil { 138 if closing { 139 break 140 } 141 util.Fatal("i/o error on server channel") 142 } 143 if DEBUG != 0 { 144 fmt.Fprintf(os.Stderr, "-> %v\n", fc) 145 } 146 x.fcall = fc 147 if int(x.fcall.Type) >= len(fcall) || fcall[x.fcall.Type] == nil { 148 var t plan9.Fcall 149 x = respond(x, &t, "bad fcall type") 150 } else { 151 var f *Fid 152 var t plan9.Fcall 153 switch x.fcall.Type { 154 case plan9.Tversion, plan9.Tauth, plan9.Tflush: 155 f = nil 156 case plan9.Tattach: 157 f = newfid(int(x.fcall.Fid)) 158 default: 159 f = newfid(int(x.fcall.Fid)) 160 if !f.busy { 161 x.f = f 162 x = respond(x, &t, "fid not in use") 163 continue 164 } 165 } 166 x.f = f 167 x = fcall[x.fcall.Type](x, f) 168 } 169 } 170 } 171 172 func fsysaddid(dir []rune, incl [][]rune) *base.Mntdir { 173 mnt.lk.Lock() 174 defer mnt.lk.Unlock() 175 mnt.id++ 176 mnt.md = &base.Mntdir{ 177 ID: mnt.id, 178 Dir: dir, 179 Ref: 1, // one for Command, one will be incremented in attach 180 Next: mnt.md, 181 Incl: incl, 182 } 183 return mnt.md 184 } 185 186 func fsysincid(m *base.Mntdir) { 187 mnt.lk.Lock() 188 defer mnt.lk.Unlock() 189 m.Ref++ 190 } 191 192 func fsysdelid(idm *base.Mntdir) { 193 if idm == nil { 194 return 195 } 196 mnt.lk.Lock() 197 idm.Ref-- 198 if idm.Ref > 0 { 199 mnt.lk.Unlock() 200 return 201 } 202 var prev *base.Mntdir 203 for m := mnt.md; m != nil; m = m.Next { 204 if m == idm { 205 if prev != nil { 206 prev.Next = m.Next 207 } else { 208 mnt.md = m.Next 209 } 210 mnt.lk.Unlock() 211 return 212 } 213 prev = m 214 } 215 mnt.lk.Unlock() 216 cerr <- []byte(fmt.Sprintf("fsysdelid: can't find id %d\n", idm.ID)) 217 } 218 219 // fsysmount is called only in exec.runproc. 220 // 221 // In the original acme, this would mount the 9p 222 // pipe onto /mnt/acme and bind -b /mnt/acme /dev 223 // to make /dev/cons available to the process running 224 // fsysmount (a subprocess of acme in a separate 225 // fd group) 226 func fsysmount(dir []rune, incl [][]rune) *base.Mntdir { 227 return fsysaddid(dir, incl) 228 } 229 230 func fsysclose() { 231 closing = true 232 /* 233 * apparently this is not kosher on openbsd. 234 * perhaps because fsysproc is reading from sfd right now, 235 * the close hangs indefinitely. 236 close(sfd); 237 */ 238 } 239 240 func respond(x *Xfid, t *plan9.Fcall, err string) *Xfid { 241 if err != "" { 242 t.Type = plan9.Rerror 243 t.Ename = err 244 } else { 245 t.Type = x.fcall.Type + 1 246 } 247 t.Fid = x.fcall.Fid 248 t.Tag = x.fcall.Tag 249 if DEBUG != 0 { 250 fmt.Fprintf(os.Stderr, "<- %v\n", t) 251 } 252 if err := plan9.WriteFcall(sfdW, t); err != nil { 253 util.Fatal("write error in respond") 254 } 255 return x 256 } 257 258 func fsysversion(x *Xfid, f *Fid) *Xfid { 259 var t plan9.Fcall 260 if x.fcall.Msize < 256 { 261 return respond(x, &t, "version: message size too small") 262 } 263 messagesize = int(x.fcall.Msize) 264 t.Msize = uint32(messagesize) 265 if !strings.HasPrefix(x.fcall.Version, "9P2000") { 266 return respond(x, &t, "unrecognized 9P version") 267 } 268 t.Version = "9P2000" 269 return respond(x, &t, "") 270 } 271 272 func fsysauth(x *Xfid, f *Fid) *Xfid { 273 var t plan9.Fcall 274 return respond(x, &t, "acme: authentication not required") 275 } 276 277 func fsysflush(x *Xfid, f *Fid) *Xfid { 278 x.c <- xfidflush 279 return nil 280 } 281 282 func fsysattach(x *Xfid, f *Fid) *Xfid { 283 var t plan9.Fcall 284 if x.fcall.Uname != user { 285 return respond(x, &t, Eperm) 286 } 287 f.busy = true 288 f.open = false 289 f.qid.Path = Qdir 290 f.qid.Type = plan9.QTDIR 291 f.qid.Vers = 0 292 f.dir = dirtab[:] 293 f.rpart = f.rpart[:0] 294 f.w = nil 295 t.Qid = f.qid 296 f.mntdir = nil 297 id, _ := strconv.Atoi(x.fcall.Aname) 298 mnt.lk.Lock() 299 var m *base.Mntdir 300 for m = mnt.md; m != nil; m = m.Next { 301 if m.ID == id { 302 f.mntdir = m 303 m.Ref++ 304 break 305 } 306 } 307 if m == nil && len(x.fcall.Aname) > 0 { 308 cerr <- []byte(fmt.Sprintf("unknown id '%s' in attach", x.fcall.Aname)) 309 } 310 mnt.lk.Unlock() 311 return respond(x, &t, "") 312 } 313 314 func fsyswalk(x *Xfid, f *Fid) *Xfid { 315 var nf *Fid 316 var w *wind.Window 317 var t plan9.Fcall 318 if f.open { 319 return respond(x, &t, "walk of open file") 320 } 321 if x.fcall.Fid != x.fcall.Newfid { 322 nf = newfid(int(x.fcall.Newfid)) 323 if nf.busy { 324 return respond(x, &t, "newfid already in use") 325 } 326 nf.busy = true 327 nf.open = false 328 nf.mntdir = f.mntdir 329 if f.mntdir != nil { 330 f.mntdir.Ref++ 331 } 332 nf.dir = f.dir 333 nf.qid = f.qid 334 nf.w = f.w 335 nf.rpart = nf.rpart[:0] // not open, so must be zero 336 if nf.w != nil { 337 util.Incref(&nf.w.Ref) 338 } 339 f = nf // walk f 340 } 341 342 t.Wqid = nil 343 var err string 344 var dir []Dirtab 345 id := WIN(f.qid) 346 q := f.qid 347 if len(x.fcall.Wname) > 0 { 348 var i int 349 for i = 0; i < len(x.fcall.Wname); i++ { 350 if q.Type&plan9.QTDIR == 0 { 351 err = Enotdir 352 break 353 } 354 var typ uint8 355 var path_ int 356 357 if x.fcall.Wname[i] == ".." { 358 typ = plan9.QTDIR 359 path_ = Qdir 360 id = 0 361 if w != nil { 362 wind.Winclose(w) 363 w = nil 364 } 365 goto Accept 366 } 367 368 // is it a numeric name? 369 for j := 0; j < len(x.fcall.Wname[i]); j++ { 370 c := x.fcall.Wname[i][j] 371 if c < '0' || '9' < c { 372 goto Regular 373 } 374 } 375 // yes: it's a directory 376 if w != nil { // name has form 27/23; get out before losing w 377 break 378 } 379 id, _ = strconv.Atoi(x.fcall.Wname[i]) 380 wind.TheRow.Lk.Lock() 381 w = ui.LookID(id) 382 if w == nil { 383 wind.TheRow.Lk.Unlock() 384 break 385 } 386 util.Incref(&w.Ref) // we'll drop reference at end if there's an error 387 path_ = Qdir 388 typ = plan9.QTDIR 389 wind.TheRow.Lk.Unlock() 390 dir = dirtabw[:] 391 goto Accept 392 393 Regular: 394 if x.fcall.Wname[i] == "new" { 395 if w != nil { 396 util.Fatal("w set in walk to new") 397 } 398 cnewwindow <- nil // signal newwindowthread 399 w = <-cnewwindow // receive new window 400 util.Incref(&w.Ref) 401 typ = plan9.QTDIR 402 path_ = Qdir 403 id = w.ID 404 dir = dirtabw[:] 405 goto Accept 406 } 407 { 408 var d []Dirtab 409 410 if id == 0 { 411 d = dirtab[:] 412 } else { 413 d = dirtabw[:] 414 } 415 d = d[1:] // skip '.' 416 for ; len(d) > 0; d = d[1:] { 417 if x.fcall.Wname[i] == d[0].name { 418 path_ = int(d[0].qid) // TODO(rsc) 419 typ = d[0].typ 420 dir = d 421 goto Accept 422 } 423 } 424 425 break // file not found 426 } 427 428 Accept: 429 if i == plan9.MAXWELEM { 430 err = "name too long" 431 break 432 } 433 q.Type = typ 434 q.Vers = 0 435 q.Path = QID(id, path_) 436 t.Wqid = append(t.Wqid, q) 437 continue 438 } 439 440 if i == 0 && err == "" { 441 err = Eexist 442 } 443 } 444 445 if err != "" || len(t.Wqid) < len(x.fcall.Wname) { 446 if nf != nil { 447 nf.busy = false 448 fsysdelid(nf.mntdir) 449 } 450 } else if len(t.Wqid) == len(x.fcall.Wname) { 451 if w != nil { 452 f.w = w 453 w = nil // don't drop the reference 454 } 455 if dir != nil { 456 f.dir = dir 457 } 458 f.qid = q 459 } 460 461 if w != nil { 462 wind.Winclose(w) 463 } 464 465 return respond(x, &t, err) 466 } 467 468 func fsysopen(x *Xfid, f *Fid) *Xfid { 469 // can't truncate anything, so just disregard 470 x.fcall.Mode &^= plan9.OTRUNC | plan9.OCEXEC 471 // can't execute or remove anything 472 if x.fcall.Mode == plan9.OEXEC || x.fcall.Mode&plan9.ORCLOSE != 0 { 473 goto Deny 474 } 475 { 476 var m int 477 switch x.fcall.Mode { 478 default: 479 goto Deny 480 case plan9.OREAD: 481 m = 0400 482 case plan9.OWRITE: 483 m = 0200 484 case plan9.ORDWR: 485 m = 0600 486 } 487 if (f.dir[0].perm & ^(plan9.DMDIR|plan9.DMAPPEND))&m != m { 488 goto Deny 489 } 490 491 x.c <- xfidopen 492 return nil 493 } 494 495 Deny: 496 var t plan9.Fcall 497 return respond(x, &t, Eperm) 498 } 499 500 func fsyscreate(x *Xfid, f *Fid) *Xfid { 501 var t plan9.Fcall 502 return respond(x, &t, Eperm) 503 } 504 505 func fsysread(x *Xfid, f *Fid) *Xfid { 506 if f.qid.Type&plan9.QTDIR != 0 { 507 var t plan9.Fcall 508 if FILE(f.qid) == Qacme { // empty dir 509 t.Data = nil 510 t.Count = 0 511 respond(x, &t, "") 512 return x 513 } 514 o := int64(x.fcall.Offset) 515 e := int64(x.fcall.Offset) + int64(x.fcall.Count) 516 clock := getclock() 517 b := make([]byte, messagesize) 518 id := WIN(f.qid) 519 n := 0 520 var d []Dirtab 521 if id > 0 { 522 d = dirtabw[:] 523 } else { 524 d = dirtab[:] 525 } 526 d = d[1:] // first entry is '.' 527 var w int 528 var i int 529 for i = 0; len(d) > 0 && int64(i) < e; i += w { 530 buf, err := dostat(WIN(x.f.qid), &d[0], clock) 531 if err != nil { 532 break 533 } 534 if n+len(buf) > int(x.fcall.Count) { 535 break 536 } 537 copy(b[n:], buf) 538 w = len(buf) 539 if w <= 2 { 540 break 541 } 542 if int64(i) >= o { 543 n += w 544 } 545 d = d[1:] 546 } 547 if id == 0 { 548 wind.TheRow.Lk.Lock() 549 nids := 0 550 var ids []int 551 var j int 552 var k int 553 for j = 0; j < len(wind.TheRow.Col); j++ { 554 c := wind.TheRow.Col[j] 555 for k = 0; k < len(c.W); k++ { 556 ids = append(ids, c.W[k].ID) 557 } 558 } 559 wind.TheRow.Lk.Unlock() 560 sort.Ints(ids) 561 j = 0 562 var dt Dirtab 563 var nn int 564 for ; j < nids && int64(i) < e; i += nn { 565 k = ids[j] 566 dt.name = fmt.Sprintf("%d", k) 567 dt.qid = int(QID(k, Qdir)) // TODO(rsc) 568 dt.typ = plan9.QTDIR 569 dt.perm = plan9.DMDIR | 0700 570 buf, err := dostat(k, &dt, clock) 571 if err != nil || len(b) > int(x.fcall.Count)-n { 572 break 573 } 574 copy(b[n:], buf) 575 nn = len(buf) 576 if int64(i) >= o { 577 n += nn 578 } 579 j++ 580 } 581 } 582 t.Data = b[:n] 583 t.Count = uint32(n) 584 respond(x, &t, "") 585 return x 586 } 587 x.c <- xfidread 588 return nil 589 } 590 591 func fsyswrite(x *Xfid, f *Fid) *Xfid { 592 x.c <- xfidwrite 593 return nil 594 } 595 596 func fsysclunk(x *Xfid, f *Fid) *Xfid { 597 fsysdelid(f.mntdir) 598 x.c <- xfidclose 599 return nil 600 } 601 602 func fsysremove(x *Xfid, f *Fid) *Xfid { 603 var t plan9.Fcall 604 return respond(x, &t, Eperm) 605 } 606 607 func fsysstat(x *Xfid, f *Fid) *Xfid { 608 var t plan9.Fcall 609 var err error 610 t.Stat, err = dostat(WIN(x.f.qid), &f.dir[0], getclock()) 611 if err != nil { 612 return respond(x, &t, err.Error()) 613 } 614 if len(t.Stat) > messagesize-plan9.IOHDRSZ { 615 return respond(x, &t, "stat too long") 616 } 617 return respond(x, &t, "") 618 } 619 620 func fsyswstat(x *Xfid, f *Fid) *Xfid { 621 var t plan9.Fcall 622 return respond(x, &t, Eperm) 623 } 624 625 func newfid(fid int) *Fid { 626 var ff *Fid 627 fh := &fids[fid&(Nhash-1)] 628 var f *Fid 629 for f = *fh; f != nil; f = f.next { 630 if f.fid == fid { 631 return f 632 } else if ff == nil && !f.busy { 633 ff = f 634 } 635 } 636 if ff != nil { 637 ff.fid = fid 638 return ff 639 } 640 f = new(Fid) 641 f.fid = fid 642 f.next = *fh 643 *fh = f 644 return f 645 } 646 647 func getclock() int { 648 return int(time.Now().Unix()) 649 } 650 651 func dostat(id int, dir *Dirtab, clock int) ([]byte, error) { 652 var d plan9.Dir 653 d.Qid.Path = QID(id, dir.qid) 654 d.Qid.Vers = 0 655 d.Qid.Type = dir.typ 656 d.Mode = plan9.Perm(dir.perm) 657 d.Length = 0 // would be nice to do better 658 d.Name = dir.name 659 d.Uid = user 660 d.Gid = user 661 d.Muid = user 662 d.Atime = uint32(clock) 663 d.Mtime = uint32(clock) 664 return d.Bytes() 665 }