lib9p

Go 9P library.
Log | Files | Refs | LICENSE

server.go (31373B)


      1 package lib9p
      2 
      3 import (
      4 	"context"
      5 	"fmt"
      6 	"io"
      7 	"io/fs"
      8 	"log"
      9 	"os"
     10 	"path"
     11 	"strings"
     12 	"sync"
     13 )
     14 
     15 var (
     16 	ErrBotch      = fmt.Errorf("botch")
     17 	ErrPerm       = fmt.Errorf("permission denied")
     18 	ErrOperation  = fmt.Errorf("operation not supported")
     19 	ErrDupTag     = fmt.Errorf("duplicate tag")
     20 	ErrUnknownFid = fmt.Errorf("unknown fid")
     21 	ErrDupFid     = fmt.Errorf("duplicate fid")
     22 	ErrNotFound   = fmt.Errorf("not found")
     23 )
     24 
     25 func setError(r *request, err error) {
     26 	r.ofcall = &RError{
     27 		Ename: err,
     28 	}
     29 }
     30 
     31 // A Server is a 9P server.
     32 type Server struct {
     33 	// If true, the server prints the transcript of the 9P session
     34 	// to os.Stderr
     35 	chatty9P bool
     36 
     37 	// The file system to export via 9P.
     38 	fsmap map[string]FS
     39 
     40 	// Auth function is passed an auth request when a TAuth message arrives
     41 	// If authentication is desired, Auth should
     42 	// set request.Afid.file to an *AuthFile and request.ofcall.Qid, and prepare to
     43 	// authenticate via the Read()/Write() calls to request.Afid.file.
     44 	// Auth should clean up everything it creates when ctx is canceled.
     45 	// If this is nil, no authentication is performed.
     46 	Auth func(ctx context.Context, r *request) error
     47 }
     48 
     49 // NewServer creates a Server.
     50 // It serves fsys with the aname "".
     51 func NewServer(fsys FS) *Server {
     52 	s := &Server{
     53 		fsmap: map[string]FS{"": fsys},
     54 	}
     55 	return s
     56 }
     57 
     58 // NewServerMap creates a Server.
     59 // Fsmap is the map of aname-FS pair.
     60 func NewServerMap(fsmap map[string]FS) *Server {
     61 	s := &Server{
     62 		fsmap: fsmap,
     63 	}
     64 	return s
     65 }
     66 
     67 // Chatty enables the server's log messages of 9P messages.
     68 func (s *Server) Chatty() { s.chatty9P = true }
     69 
     70 type conn struct {
     71 	s *Server
     72 	// Maximum length in byte of 9P messages.
     73 	msize uint32
     74 	// Mutex to change msize.
     75 	mSizeLock *sync.Mutex
     76 
     77 	// FidPool of the connection.
     78 	fPool *fidPool
     79 
     80 	// The channel from which incoming requests are delivered by the
     81 	// listener goroutine.
     82 	listenChan <-chan *request
     83 	// r is the io.Reader which the listener goroutine reads from.
     84 	// It should be accessed only by the listener goroutine.
     85 	r io.Reader
     86 
     87 	// The channel to which outgoing replies are sent to the responder
     88 	// goroutine.
     89 	respChan chan *request
     90 
     91 	// w is the io.Writer which the responder goroutine writes to.
     92 	// It should be accessed only by the responder goroutine.
     93 	w io.Writer
     94 }
     95 
     96 func (s *Server) newConn(r io.Reader, w io.Writer) *conn {
     97 	return &conn{
     98 		s:         s,
     99 		msize:     8 * 1024,
    100 		mSizeLock: new(sync.Mutex),
    101 		fPool:     newFidPool(),
    102 		r:         r,
    103 		w:         w,
    104 	}
    105 }
    106 
    107 // mSize reads the maximum message size of the server.
    108 func (c *conn) mSize() uint32 {
    109 	c.mSizeLock.Lock()
    110 	defer c.mSizeLock.Unlock()
    111 	return c.msize
    112 }
    113 
    114 // setMSize changes the server's maximum message size.
    115 func (c *conn) setMSize(mSize uint32) {
    116 	c.mSizeLock.Lock()
    117 	defer c.mSizeLock.Unlock()
    118 	c.msize = mSize
    119 }
    120 
    121 // runListener runs the listener goroutine.
    122 // Listener goroutine reads 9P messages from s.r by calling getReq
    123 // and sends them to c.listenChan.
    124 func (c *conn) runListener(ctx context.Context, rp *reqPool) {
    125 	rc := make(chan *request)
    126 	c.listenChan = rc
    127 	go func() {
    128 		defer close(rc)
    129 		for {
    130 			select {
    131 			case rc <- getReq(c.r, rp, c.s.chatty9P):
    132 			case <-ctx.Done():
    133 				return
    134 			}
    135 		}
    136 	}()
    137 }
    138 
    139 // runResponder runs the responder goroutine.
    140 // Responder goroutine waits for reply Requests from c.respChan,
    141 // and marshalls each of them into 9P messages and writes it to c.w.
    142 func (c *conn) runResponder(ctx context.Context, rp *reqPool) {
    143 	rc := make(chan *request)
    144 	c.respChan = rc
    145 	go func() {
    146 		for {
    147 			select {
    148 			case r, ok := <-rc:
    149 				if !ok {
    150 					return
    151 				}
    152 				r.ofcall.SetTag(r.tag)
    153 				rp.delete(r.tag)
    154 				_, err := c.w.Write(r.ofcall.marshal())
    155 				if err != nil {
    156 					// TODO: handle error.
    157 					log.Printf("respond: %v", err)
    158 					continue
    159 				}
    160 				if c.s.chatty9P {
    161 					fmt.Fprintf(os.Stderr, "--> %s\n", r.ofcall)
    162 				}
    163 			case <-ctx.Done():
    164 				return
    165 			}
    166 		}
    167 	}()
    168 }
    169 
    170 // GetReq reads 9P message from r, allocates request, adds it to rp,
    171 // and returns it.
    172 // Any error it encountered is embedded into the request struct.
    173 // This function is called only by the server's listener goroutine,
    174 // and does not need to lock r.
    175 func getReq(r io.Reader, rp *reqPool, chatty bool) *request {
    176 	ifcall, err := RecvMsg(r)
    177 	if err != nil {
    178 		if err == io.EOF {
    179 			return &request{listenErr: err}
    180 		}
    181 		return &request{listenErr: fmt.Errorf("readMsg: %v", err)}
    182 	}
    183 	req, err := rp.add(ifcall.GetTag())
    184 	if err != nil {
    185 		// duplicate tag: cons up a fake request
    186 		req := new(request)
    187 		req.ifcall = ifcall
    188 		req.listenErr = ErrDupTag
    189 		if chatty {
    190 			fmt.Fprintf(os.Stderr, "<-- %v\n", req.ifcall)
    191 		}
    192 		return req
    193 	}
    194 	req.tag = ifcall.GetTag()
    195 	req.ifcall = ifcall
    196 	if ifcall, ok := req.ifcall.(*TFlush); ok {
    197 		req.oldreq, _ = rp.lookup(ifcall.Oldtag)
    198 	}
    199 	if chatty {
    200 		fmt.Fprintf(os.Stderr, "<-- %v\n", req.ifcall)
    201 	}
    202 	return req
    203 }
    204 
    205 // sVersion processes Tversion messages.
    206 // It checks if the version string is "9P2000", and if the version differs from
    207 // that, it replies with version string "unknown".
    208 // It also checks the msize of the message and if it is bigger than that of the
    209 // server, it replies with msize being the server's mSize, otherwise it sets the
    210 // server's mSize to the message's one.
    211 func sVersion(ctx context.Context, c *conn, rc <-chan *request) {
    212 	for {
    213 		select {
    214 		case <-ctx.Done():
    215 			return
    216 		case r, ok := <-rc:
    217 			if !ok {
    218 				return
    219 			}
    220 			ifcall := r.ifcall.(*TVersion)
    221 			version := ifcall.Version
    222 			if strings.HasPrefix(version, "9P2000") {
    223 				version = "9P2000"
    224 			} else {
    225 				version = "unknown"
    226 			}
    227 			msize := ifcall.Msize
    228 			if msize > c.mSize() {
    229 				msize = c.mSize()
    230 			}
    231 			r.ofcall = &RVersion{
    232 				Msize:   msize,
    233 				Version: version,
    234 			}
    235 			c.setMSize(r.ofcall.(*RVersion).Msize)
    236 			select {
    237 			case c.respChan <- r:
    238 			case <-ctx.Done():
    239 				return
    240 			}
    241 		}
    242 	}
    243 }
    244 
    245 // sAuth serves Tauth message.
    246 func sAuth(ctx context.Context, c *conn, rc <-chan *request) {
    247 	for {
    248 		select {
    249 		case <-ctx.Done():
    250 			return
    251 		case r, ok := <-rc:
    252 			if !ok {
    253 				return
    254 			}
    255 			var (
    256 				err    error
    257 				ifcall *TAuth
    258 			)
    259 			if c.s.Auth == nil {
    260 				r.err = fmt.Errorf("authentication not required")
    261 				goto resp
    262 			}
    263 			ifcall = r.ifcall.(*TAuth)
    264 			if _, ok := c.s.fsmap[ifcall.Aname]; !ok {
    265 				r.err = fmt.Errorf("no such file system")
    266 				goto resp
    267 			}
    268 			if ifcall.Afid == NOFID {
    269 				r.err = fmt.Errorf("NOFID can't be used for afid")
    270 				goto resp
    271 			}
    272 			r.afid, err = c.fPool.add(ifcall.Afid)
    273 			if err != nil {
    274 				r.err = ErrDupFid
    275 				goto resp
    276 			}
    277 		resp:
    278 			if r.err != nil {
    279 				setError(r, r.err)
    280 			} else {
    281 				if err := c.s.Auth(ctx, r); err != nil {
    282 					setError(r, fmt.Errorf("auth: %v", err))
    283 				} else {
    284 					// TODO: should move this code to c.s.Auth?
    285 					if rauth, ok := r.ofcall.(*RAuth); ok {
    286 						r.afid.qidpath = rauth.Aqid.Path
    287 					}
    288 				}
    289 			}
    290 			select {
    291 			case c.respChan <- r:
    292 			case <-ctx.Done():
    293 				return
    294 			}
    295 		}
    296 	}
    297 }
    298 
    299 func sAttach(ctx context.Context, c *conn, rc <-chan *request) {
    300 	for {
    301 		select {
    302 		case <-ctx.Done():
    303 			return
    304 		case r, ok := <-rc:
    305 			if !ok {
    306 				return
    307 			}
    308 			var (
    309 				fsys FS
    310 				fi   fs.FileInfo
    311 				err  error
    312 			)
    313 			ifcall := r.ifcall.(*TAttach)
    314 			switch {
    315 			case c.s.Auth == nil && ifcall.Afid == NOFID:
    316 			case c.s.Auth == nil && ifcall.Afid != NOFID:
    317 				r.err = ErrBotch
    318 				goto resp
    319 			case c.s.Auth != nil && ifcall.Afid == NOFID:
    320 				r.err = fmt.Errorf("authentication required")
    321 				goto resp
    322 			case c.s.Auth != nil && ifcall.Afid != NOFID:
    323 				afid, ok := c.fPool.lookup(ifcall.Afid)
    324 				if !ok {
    325 					r.err = ErrUnknownFid
    326 					goto resp
    327 				}
    328 				af, ok := afid.file.(*AuthFile)
    329 				if !ok {
    330 					r.err = fmt.Errorf("not auth file")
    331 					goto resp
    332 				}
    333 				if af.Uname != ifcall.Uname || af.Aname != ifcall.Aname || !af.AuthOK {
    334 					r.err = fmt.Errorf("not authenticated")
    335 					goto resp
    336 				}
    337 			}
    338 			r.fid, err = c.fPool.add(ifcall.Fid)
    339 			if err != nil {
    340 				r.err = ErrDupFid
    341 				goto resp
    342 			}
    343 			r.fid.path = "."
    344 			r.fid.uid = ifcall.Uname
    345 			fsys, ok = c.s.fsmap[ifcall.Aname]
    346 			if !ok {
    347 				r.err = fmt.Errorf("no such file system")
    348 				goto resp
    349 			}
    350 			r.fid.fs = fsys
    351 			fi, err = fs.Stat(ExportFS{c.s.fsmap[ifcall.Aname]}, ".")
    352 			if err != nil {
    353 				r.err = fmt.Errorf("stat root: %v", err)
    354 				goto resp
    355 			}
    356 			r.fid.qidpath = fi.Sys().(*Stat).Qid.Path
    357 			r.ofcall = &RAttach{
    358 				Qid: fi.Sys().(*Stat).Qid,
    359 			}
    360 		resp:
    361 			if r.err != nil {
    362 				if r.fid != nil {
    363 					c.fPool.delete(r.fid.fid)
    364 				}
    365 				setError(r, r.err)
    366 			}
    367 			select {
    368 			case c.respChan <- r:
    369 			case <-ctx.Done():
    370 				return
    371 			}
    372 		}
    373 	}
    374 }
    375 
    376 func sFlush(ctx context.Context, c *conn, rc <-chan *request) {
    377 	for {
    378 		select {
    379 		case <-ctx.Done():
    380 			return
    381 		case r, ok := <-rc:
    382 			if !ok {
    383 				return
    384 			}
    385 			if r.oldreq != nil {
    386 				r.oldreq.flush()
    387 			}
    388 			r.ofcall = &RFlush{}
    389 			select {
    390 			case c.respChan <- r:
    391 			case <-ctx.Done():
    392 				return
    393 			}
    394 		}
    395 	}
    396 }
    397 
    398 // TODO: implement MAXWELEM.
    399 func sWalk(ctx context.Context, c *conn, rc <-chan *request) {
    400 	for {
    401 		select {
    402 		case <-ctx.Done():
    403 			return
    404 		case r, ok := <-rc:
    405 			if !ok {
    406 				return
    407 			}
    408 			var (
    409 				err    error
    410 				oldfi  fs.FileInfo
    411 				newFid *fid
    412 				wqids  []Qid
    413 				cwdp   string
    414 			)
    415 			ifcall := r.ifcall.(*TWalk)
    416 			oldFid, ok := c.fPool.lookup(ifcall.Fid)
    417 			if !ok {
    418 				r.err = ErrUnknownFid
    419 				goto resp
    420 			}
    421 			if oldFid.omode != -1 {
    422 				r.err = fmt.Errorf("cannot clone open fid")
    423 				goto resp
    424 			}
    425 			oldfi, err = fs.Stat(ExportFS{oldFid.fs}, oldFid.path)
    426 			if err != nil {
    427 				r.err = fmt.Errorf("stat: %v", err)
    428 				goto resp
    429 			}
    430 			if len(ifcall.Wnames) > 0 && oldfi.Sys().(*Stat).Qid.Type&QTDIR == 0 {
    431 				r.err = fmt.Errorf("walk on non-dir")
    432 				goto resp
    433 			}
    434 			if ifcall.Fid == ifcall.Newfid {
    435 				newFid = oldFid
    436 			} else {
    437 				newFid, err = c.fPool.add(ifcall.Newfid)
    438 				if err != nil {
    439 					r.err = fmt.Errorf("alloc: %v", err)
    440 					goto resp
    441 				}
    442 			}
    443 			wqids = make([]Qid, 0, len(ifcall.Wnames))
    444 			cwdp = oldFid.path
    445 			for _, name := range ifcall.Wnames {
    446 				cwdp = path.Clean(path.Join(cwdp, name))
    447 				if cwdp == ".." {
    448 					cwdp = "." // parent of the root is itself.
    449 				}
    450 				stat, err := fs.Stat(ExportFS{oldFid.fs}, cwdp)
    451 				if err != nil {
    452 					break
    453 				}
    454 				wqids = append(wqids, stat.Sys().(*Stat).Qid)
    455 			}
    456 			if len(wqids) == 0 {
    457 				newFid.qidpath = oldFid.qidpath
    458 			} else {
    459 				newFid.qidpath = wqids[len(wqids)-1].Path
    460 			}
    461 			newFid.path = cwdp
    462 			newFid.uid = oldFid.uid
    463 			newFid.fs = oldFid.fs
    464 			r.ofcall = &RWalk{
    465 				Qids: wqids,
    466 			}
    467 		resp:
    468 			if r.ofcall == nil {
    469 				setError(r, r.err)
    470 				select {
    471 				case c.respChan <- r:
    472 					continue
    473 				case <-ctx.Done():
    474 					return
    475 				}
    476 			}
    477 			ofcall := r.ofcall.(*RWalk)
    478 			if r.err != nil || len(ofcall.Qids) < len(ifcall.Wnames) {
    479 				if ifcall.Fid != ifcall.Newfid {
    480 					c.fPool.delete(ifcall.Newfid)
    481 				}
    482 				if len(ofcall.Qids) == 0 {
    483 					if r.err == nil && len(ifcall.Wnames) != 0 {
    484 						setError(r, ErrNotFound)
    485 					}
    486 				}
    487 			}
    488 			select {
    489 			case c.respChan <- r:
    490 			case <-ctx.Done():
    491 				return
    492 			}
    493 		}
    494 	}
    495 }
    496 
    497 func sOpen(ctx context.Context, c *conn, rc <-chan *request) {
    498 	for {
    499 		select {
    500 		case <-ctx.Done():
    501 			return
    502 		case r, ok := <-rc:
    503 			if !ok {
    504 				return
    505 			}
    506 			var (
    507 				p   fs.FileMode
    508 				err error
    509 				qid Qid
    510 				fi  fs.FileInfo
    511 			)
    512 			ifcall := r.ifcall.(*TOpen)
    513 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    514 			if !ok {
    515 				r.err = ErrUnknownFid
    516 				goto resp
    517 			}
    518 			if r.fid.omode != -1 {
    519 				r.err = ErrBotch
    520 				goto resp
    521 			}
    522 			if afile, ok := r.fid.file.(*AuthFile); ok {
    523 				// c.s.Auth should set r.fid.file to a valid *AuthFile,
    524 				// so r.fid.file should not be nil.
    525 				fi, err = r.fid.file.Stat()
    526 				if err != nil {
    527 					r.err = fmt.Errorf("stat: %v", err)
    528 					goto resp
    529 				}
    530 				qid = afile.Qid
    531 			} else {
    532 				fi, err = fs.Stat(ExportFS{r.fid.fs}, r.fid.path)
    533 				if err != nil {
    534 					r.err = fmt.Errorf("stat: %v", err)
    535 					goto resp
    536 				}
    537 				qid = fi.Sys().(*Stat).Qid
    538 			}
    539 			// Write attempt to a directory is prohibitted by the protocol.
    540 			// In plan9 implementation, ifcall.Mode is ANDed with ^ORCLOSE,
    541 			// but ORCLOSE is also prohibitted by the protocol...
    542 			if qid.Type == QTDIR && ifcall.Mode != OREAD {
    543 				r.err = fmt.Errorf("is a directory")
    544 				goto resp
    545 			}
    546 			switch ifcall.Mode & 3 {
    547 			case OREAD:
    548 				p = AREAD
    549 			case OWRITE:
    550 				p = AWRITE
    551 			case ORDWR:
    552 				p = AREAD | AWRITE
    553 			case OEXEC:
    554 				p = AEXEC
    555 			}
    556 			if ifcall.Mode&OTRUNC != 0 {
    557 				p |= AWRITE
    558 			}
    559 			if qid.Type&QTDIR != 0 && p != AREAD {
    560 				r.err = ErrPerm
    561 				goto resp
    562 			}
    563 			if !hasPerm(r.fid.fs, fi, r.fid.uid, p) {
    564 				r.err = ErrPerm
    565 				goto resp
    566 			}
    567 			if ifcall.Mode&ORCLOSE != 0 {
    568 				parentPath := path.Dir(r.fid.path)
    569 				fi, err := fs.Stat(ExportFS{r.fid.fs}, parentPath)
    570 				if err != nil {
    571 					r.err = fmt.Errorf("stat parent: %v", err)
    572 					goto resp
    573 				}
    574 				if !hasPerm(r.fid.fs, fi, r.fid.uid, AWRITE) {
    575 					r.err = ErrPerm
    576 					goto resp
    577 				}
    578 			}
    579 			r.ofcall = &ROpen{
    580 				Qid:    qid,
    581 				Iounit: c.mSize() - IOHDRSZ,
    582 			}
    583 		resp:
    584 			if r.err != nil {
    585 				setError(r, r.err)
    586 				goto send
    587 			}
    588 			if _, ok := r.fid.file.(*AuthFile); ok {
    589 				r.fid.omode = ifcall.Mode
    590 				goto send
    591 			}
    592 			r.fid.file, err = r.fid.fs.OpenFile(r.fid.path, ModeToFlag(ifcall.Mode))
    593 			if err != nil {
    594 				setError(r, err)
    595 				goto send
    596 			}
    597 			fi, err = r.fid.file.Stat()
    598 			if err != nil {
    599 				r.fid.file.Close()
    600 				setError(r, err)
    601 				goto send
    602 			}
    603 			if fi.Sys().(*Stat).Qid.Path != r.fid.qidpath {
    604 				log.Println("open:", fi.Sys().(*Stat).Qid.Path, r.fid.qidpath)
    605 				r.fid.file.Close()
    606 				setError(r, fmt.Errorf("qid path mismatch"))
    607 				goto send
    608 			}
    609 			// omode should be set after successfully opening it.
    610 			r.fid.omode = ifcall.Mode
    611 		send:
    612 			select {
    613 			case c.respChan <- r:
    614 			case <-ctx.Done():
    615 				return
    616 			}
    617 		}
    618 	}
    619 }
    620 
    621 func sCreate(ctx context.Context, c *conn, rc <-chan *request) {
    622 	for {
    623 		select {
    624 		case <-ctx.Done():
    625 			return
    626 		case r, ok := <-rc:
    627 			if !ok {
    628 				return
    629 			}
    630 			var (
    631 				fi      fs.FileInfo
    632 				dirfi   fs.FileInfo
    633 				err     error
    634 				cfs     CreatorFS
    635 				cpath   string
    636 				perm    FileMode
    637 				dirperm FileMode
    638 			)
    639 			ifcall := r.ifcall.(*TCreate)
    640 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    641 			if !ok {
    642 				r.err = ErrUnknownFid
    643 				goto resp
    644 			}
    645 			if r.fid.omode != -1 {
    646 				r.err = ErrBotch
    647 				goto resp
    648 			}
    649 			dirfi, err = fs.Stat(ExportFS{r.fid.fs}, r.fid.path)
    650 			if err != nil {
    651 				r.err = fmt.Errorf("stat: %v", err)
    652 				goto resp
    653 			}
    654 			if !dirfi.IsDir() {
    655 				r.err = fmt.Errorf("create in non-dir")
    656 				goto resp
    657 			}
    658 			if !hasPerm(r.fid.fs, dirfi, r.fid.uid, AWRITE) {
    659 				r.err = ErrPerm
    660 				goto resp
    661 			}
    662 			cfs, ok = r.fid.fs.(CreatorFS)
    663 			if !ok {
    664 				r.err = ErrOperation
    665 				goto resp
    666 			}
    667 			perm = ifcall.Perm
    668 			dirperm = dirfi.Mode()
    669 			if perm&fs.ModeDir == 0 {
    670 				perm &= ^FileMode(0666) | (dirperm & FileMode(0666))
    671 			} else {
    672 				perm &= ^FileMode(0777) | (dirperm & FileMode(0777))
    673 			}
    674 			cpath = path.Join(r.fid.path, ifcall.Name)
    675 			r.fid.file, err = cfs.Create(cpath, r.fid.uid, ifcall.Mode, perm)
    676 			if err != nil {
    677 				r.err = fmt.Errorf("create: %v", err)
    678 				goto resp
    679 			}
    680 			r.fid.path = cpath
    681 			r.fid.omode = ifcall.Mode
    682 			fi, err = r.fid.file.Stat()
    683 			if err != nil {
    684 				r.err = fmt.Errorf("stat: %v", err)
    685 				goto resp
    686 			}
    687 			r.fid.qidpath = fi.Sys().(*Stat).Qid.Path
    688 			r.ofcall = &RCreate{
    689 				Qid:    fi.Sys().(*Stat).Qid,
    690 				Iounit: c.mSize() - IOHDRSZ,
    691 			}
    692 		resp:
    693 			if r.err != nil {
    694 				setError(r, r.err)
    695 			}
    696 			select {
    697 			case c.respChan <- r:
    698 			case <-ctx.Done():
    699 				return
    700 			}
    701 		}
    702 	}
    703 }
    704 
    705 // TODO: I think the file should be locked while reading.
    706 // or should the undeterminism left for the client side?
    707 func sRead(ctx context.Context, c *conn, rc <-chan *request) {
    708 	for {
    709 		select {
    710 		case <-ctx.Done():
    711 			return
    712 		case r, ok := <-rc:
    713 			if !ok {
    714 				return
    715 			}
    716 			var (
    717 				fi   fs.FileInfo
    718 				err  error
    719 				data []byte
    720 				done chan struct{}
    721 				n    int
    722 			)
    723 			ifcall := r.ifcall.(*TRead)
    724 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    725 			if !ok {
    726 				r.err = ErrUnknownFid
    727 				goto resp
    728 			}
    729 			if r.fid.omode == -1 {
    730 				r.err = fmt.Errorf("not open")
    731 				goto resp
    732 			}
    733 			if r.fid.omode != OREAD && r.fid.omode != ORDWR && r.fid.omode != OEXEC {
    734 				r.err = ErrPerm
    735 				goto resp
    736 			}
    737 			fi, err = r.fid.file.Stat()
    738 			if err != nil {
    739 				r.err = fmt.Errorf("stat: %v", err)
    740 				goto resp
    741 			}
    742 			if c.mSize()-IOHDRSZ < ifcall.Count {
    743 				ifcall.Count = c.mSize() - IOHDRSZ
    744 			}
    745 			data = make([]byte, ifcall.Count)
    746 			done = make(chan struct{})
    747 			go func() {
    748 				defer close(done)
    749 				if fi.IsDir() {
    750 					if ifcall.Offset != 0 && ifcall.Offset != r.fid.dirOffset {
    751 						err = fmt.Errorf("invalid dir offset")
    752 						return
    753 					}
    754 					if ifcall.Offset == 0 && r.fid.dirIndex != 0 {
    755 						r.fid.dirIndex = 0
    756 						r.fid.dirOffset = 0
    757 						r.fid.dirEnts = nil
    758 						if err = r.fid.file.Close(); err != nil {
    759 							return
    760 						}
    761 						r.fid.file, err = r.fid.fs.OpenFile(r.fid.path, ModeToFlag(r.fid.omode))
    762 						if err != nil {
    763 							return
    764 						}
    765 					}
    766 					if r.fid.dirIndex == 0 {
    767 						r.fid.dirEnts, err = r.fid.file.(ReadDirFile).ReadDir(-1)
    768 						if err != nil {
    769 							return
    770 						}
    771 					}
    772 					k := r.fid.dirIndex
    773 					for ; k < len(r.fid.dirEnts); k++ {
    774 						fi, err := r.fid.dirEnts[k].Info()
    775 						if err != nil {
    776 							log.Println(err)
    777 							continue
    778 						}
    779 						st := fi.Sys().(*Stat)
    780 						buf := st.marshal()
    781 						if n+len(buf) > len(data) {
    782 							break
    783 						}
    784 						for i := 0; i < len(buf); i++ {
    785 							data[n+i] = buf[i]
    786 						}
    787 						n += len(buf)
    788 					}
    789 					r.fid.dirOffset += uint64(n)
    790 					r.fid.dirIndex += k
    791 				} else {
    792 					if reader, ok := r.fid.file.(io.ReaderAt); ok {
    793 						n, err = reader.ReadAt(data, int64(ifcall.Offset))
    794 					} else {
    795 						n, err = r.fid.file.Read(data)
    796 					}
    797 					if err == io.EOF {
    798 						err = nil
    799 					}
    800 				}
    801 			}()
    802 			select {
    803 			case <-done:
    804 				if err != nil {
    805 					r.err = err
    806 					goto resp
    807 				}
    808 			case <-r.done:
    809 				continue
    810 			case <-ctx.Done():
    811 				return
    812 			}
    813 			r.ofcall = &RRead{
    814 				Count: uint32(n),
    815 				Data:  data[:n],
    816 			}
    817 		resp:
    818 			if r.err != nil {
    819 				setError(r, r.err)
    820 			}
    821 			select {
    822 			case c.respChan <- r:
    823 			case <-ctx.Done():
    824 				return
    825 			}
    826 		}
    827 	}
    828 }
    829 
    830 // TODO: I think the file should be locked while writing.
    831 func sWrite(ctx context.Context, c *conn, rc <-chan *request) {
    832 	for {
    833 		select {
    834 		case <-ctx.Done():
    835 			return
    836 		case r, ok := <-rc:
    837 			if !ok {
    838 				return
    839 			}
    840 			var (
    841 				ofcall *RWrite
    842 				done   chan struct{}
    843 				omode  OpenMode
    844 				err    error
    845 			)
    846 			ifcall := r.ifcall.(*TWrite)
    847 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    848 			if !ok {
    849 				r.err = ErrUnknownFid
    850 				goto resp
    851 			}
    852 			if c.mSize()-IOHDRSZ < ifcall.Count {
    853 				ifcall.Count = c.mSize() - IOHDRSZ
    854 			}
    855 			omode = r.fid.omode & 3
    856 			if omode != OWRITE && omode != ORDWR {
    857 				r.err = fmt.Errorf("write on fid with open mode 0x%x", r.fid.omode)
    858 				goto resp
    859 			}
    860 			ofcall = new(RWrite)
    861 			done = make(chan struct{})
    862 			go func() {
    863 				defer close(done)
    864 				var n int
    865 				switch file := r.fid.file.(type) {
    866 				case io.WriterAt:
    867 					n, err = file.WriteAt(ifcall.Data, int64(ifcall.Offset))
    868 					if err != nil {
    869 						return
    870 					}
    871 					ofcall.Count = uint32(n)
    872 				case io.Writer:
    873 					n, err = file.Write(ifcall.Data)
    874 					if err != nil {
    875 						return
    876 					}
    877 					ofcall.Count = uint32(n)
    878 				default:
    879 					err = ErrOperation
    880 					return
    881 				}
    882 			}()
    883 			select {
    884 			case <-done:
    885 				if err != nil {
    886 					r.err = err
    887 					goto resp
    888 				}
    889 			case <-r.done:
    890 				continue
    891 			case <-ctx.Done():
    892 				return
    893 			}
    894 			r.ofcall = ofcall
    895 		resp:
    896 			if r.err != nil {
    897 				setError(r, r.err)
    898 			}
    899 			select {
    900 			case c.respChan <- r:
    901 			case <-ctx.Done():
    902 				return
    903 			}
    904 		}
    905 	}
    906 }
    907 
    908 func sClunk(ctx context.Context, c *conn, rc <-chan *request) {
    909 	for {
    910 		select {
    911 		case <-ctx.Done():
    912 			return
    913 		case r, ok := <-rc:
    914 			if !ok {
    915 				return
    916 			}
    917 			ifcall := r.ifcall.(*TClunk)
    918 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    919 			if !ok {
    920 				r.err = ErrUnknownFid
    921 				goto resp
    922 			}
    923 			c.fPool.delete(ifcall.Fid)
    924 			if r.fid.omode != -1 {
    925 				if err := r.fid.file.Close(); err != nil {
    926 					r.err = fmt.Errorf("close: %v", err)
    927 					goto resp
    928 				}
    929 				if r.fid.omode&ORCLOSE != 0 {
    930 					rfs, ok := r.fid.fs.(RemoverFS)
    931 					if !ok {
    932 						r.err = ErrOperation
    933 						goto resp
    934 					}
    935 					if err := rfs.Remove(r.fid.path); err != nil {
    936 						r.err = err
    937 						goto resp
    938 					}
    939 				}
    940 			}
    941 			r.ofcall = &RClunk{}
    942 		resp:
    943 			if r.err != nil {
    944 				setError(r, r.err)
    945 			}
    946 			select {
    947 			case c.respChan <- r:
    948 			case <-ctx.Done():
    949 				return
    950 			}
    951 		}
    952 	}
    953 }
    954 
    955 // sRemove serves Tremove messages.
    956 // TODO: RemoverFS.Remove function takes as argument the file path to be
    957 // removed. But in the protocol, files are identified by Qid.Path, not by
    958 // path name from the root of the file system.
    959 // But os package also uses the file path to remove an file.
    960 // And the library should not care the inconsistency of Qid.Path?
    961 func sRemove(ctx context.Context, c *conn, rc <-chan *request) {
    962 	for {
    963 		select {
    964 		case <-ctx.Done():
    965 			return
    966 		case r, ok := <-rc:
    967 			if !ok {
    968 				return
    969 			}
    970 			var (
    971 				parentPath string
    972 				pfi        fs.FileInfo
    973 				fi         fs.FileInfo
    974 				err        error
    975 				rfs        RemoverFS
    976 			)
    977 			ifcall := r.ifcall.(*TRemove)
    978 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    979 			if !ok {
    980 				r.err = ErrUnknownFid
    981 				goto resp
    982 			}
    983 			c.fPool.delete(ifcall.Fid)
    984 			if r.fid.omode != -1 {
    985 				r.fid.file.Close()
    986 			}
    987 			parentPath = path.Dir(r.fid.path)
    988 			pfi, err = fs.Stat(ExportFS{r.fid.fs}, parentPath)
    989 			if err != nil {
    990 				r.err = fmt.Errorf("stat parent: %v", err)
    991 				goto resp
    992 			}
    993 			if !hasPerm(r.fid.fs, pfi, r.fid.uid, AWRITE) {
    994 				r.err = ErrPerm
    995 				goto resp
    996 			}
    997 			// BUG: race. Remove call below uses r.fid.path, so I need to
    998 			// check whether the underlying qid is the same.
    999 			// But other requests can move the same file and then create
   1000 			// new one with the same name.
   1001 			fi, err = fs.Stat(ExportFS{FS: r.fid.fs}, r.fid.path)
   1002 			if err != nil {
   1003 				r.err = err
   1004 				goto resp
   1005 			}
   1006 			if r.fid.qidpath != fi.Sys().(*Stat).Qid.Path {
   1007 				r.err = fmt.Errorf("qid path mismatch")
   1008 				goto resp
   1009 			}
   1010 			rfs, ok = r.fid.fs.(RemoverFS)
   1011 			if !ok {
   1012 				r.err = ErrOperation
   1013 				goto resp
   1014 			}
   1015 			// I think the argument of RemoverFS.Remove should be Qid.Path.
   1016 			if err = rfs.Remove(r.fid.path); err != nil {
   1017 				r.err = fmt.Errorf("remove: %v", err)
   1018 				goto resp
   1019 			}
   1020 			r.ofcall = &RRemove{}
   1021 		resp:
   1022 			if r.err != nil {
   1023 				setError(r, r.err)
   1024 			}
   1025 			select {
   1026 			case c.respChan <- r:
   1027 			case <-ctx.Done():
   1028 				return
   1029 			}
   1030 		}
   1031 	}
   1032 }
   1033 
   1034 func sStat(ctx context.Context, c *conn, rc <-chan *request) {
   1035 	for {
   1036 		select {
   1037 		case <-ctx.Done():
   1038 			return
   1039 		case r, ok := <-rc:
   1040 			if !ok {
   1041 				return
   1042 			}
   1043 			var (
   1044 				fi  fs.FileInfo
   1045 				err error
   1046 			)
   1047 			ifcall := r.ifcall.(*TStat)
   1048 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
   1049 			if !ok {
   1050 				r.err = ErrUnknownFid
   1051 				goto resp
   1052 			}
   1053 			fi, err = fs.Stat(ExportFS{r.fid.fs}, r.fid.path)
   1054 			if err != nil {
   1055 				r.err = fmt.Errorf("stat: %v", err)
   1056 				goto resp
   1057 			}
   1058 			r.ofcall = &RStat{
   1059 				Stat: fi.Sys().(*Stat),
   1060 			}
   1061 		resp:
   1062 			if r.err != nil {
   1063 				setError(r, r.err)
   1064 			}
   1065 			select {
   1066 			case c.respChan <- r:
   1067 			case <-ctx.Done():
   1068 				return
   1069 			}
   1070 		}
   1071 	}
   1072 }
   1073 
   1074 func sWStat(ctx context.Context, c *conn, rc <-chan *request) {
   1075 	for {
   1076 		select {
   1077 		case <-ctx.Done():
   1078 			return
   1079 		case r, ok := <-rc:
   1080 			if !ok {
   1081 				return
   1082 			}
   1083 			var (
   1084 				wsfile  WriterStatFile
   1085 				wstat   *Stat
   1086 				newStat *Stat
   1087 				fi      fs.FileInfo
   1088 				err     error
   1089 			)
   1090 			ifcall := r.ifcall.(*TWstat)
   1091 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
   1092 			if !ok {
   1093 				r.err = ErrUnknownFid
   1094 				goto resp
   1095 			}
   1096 			if r.fid.omode == -1 {
   1097 				var err error
   1098 				r.fid.file, err = r.fid.fs.OpenFile(r.fid.path, O_RDONLY)
   1099 				if err != nil {
   1100 					r.err = fmt.Errorf("open: %v", err)
   1101 					goto resp
   1102 				}
   1103 			}
   1104 			wsfile, ok = r.fid.file.(WriterStatFile)
   1105 			if !ok {
   1106 				r.err = ErrOperation
   1107 				goto resp
   1108 			}
   1109 			wstat = ifcall.Stat
   1110 			fi, err = r.fid.file.Stat()
   1111 			if err != nil {
   1112 				r.err = fmt.Errorf("stat: %v", err)
   1113 				goto resp
   1114 			}
   1115 			newStat = fi.Sys().(*Stat)
   1116 			if r.fid.qidpath != newStat.Qid.Path {
   1117 				r.err = fmt.Errorf("qid mismatch")
   1118 				goto resp
   1119 			}
   1120 			if wstat.Type != ^uint16(0) && wstat.Type != newStat.Type {
   1121 				r.err = fmt.Errorf("changing type is not permitted")
   1122 				goto resp
   1123 			}
   1124 			if wstat.Dev != ^uint32(0) && wstat.Dev != newStat.Dev {
   1125 				r.err = fmt.Errorf("changing dev is not permitted")
   1126 				goto resp
   1127 			}
   1128 			if wstat.Qid.Type != QidType(^uint8(0)) && wstat.Qid.Type != newStat.Qid.Type {
   1129 				r.err = fmt.Errorf("changing qid type is not permitted")
   1130 				goto resp
   1131 			}
   1132 			if wstat.Qid.Vers != ^uint32(0) && wstat.Qid.Vers != newStat.Qid.Vers {
   1133 				r.err = fmt.Errorf("changing qid vers is not permitted")
   1134 				goto resp
   1135 			}
   1136 			if wstat.Qid.Path != ^uint64(0) && wstat.Qid.Path != newStat.Qid.Path {
   1137 				r.err = fmt.Errorf("changing qid path is not permitted")
   1138 				goto resp
   1139 			}
   1140 			// TODO: some unix utilities (e.g. touch, mv, git) tries to change atime.
   1141 			// But changing atime is prohibited by the protocol.
   1142 			if wstat.Atime != ^uint32(0) && wstat.Atime != newStat.Atime {
   1143 				if r.fid.uid != newStat.Uid && !isGroupLeader(r.fid.fs, newStat.Gid, r.fid.uid) {
   1144 					r.err = ErrPerm
   1145 					goto resp
   1146 				}
   1147 				newStat.Atime = wstat.Atime
   1148 				//r.err = fmt.Errorf("changing atime is not permitted")
   1149 				//goto resp
   1150 			}
   1151 			if wstat.Uid != "" && wstat.Uid != newStat.Uid {
   1152 				r.err = fmt.Errorf("changing uid is not permitted")
   1153 				goto resp
   1154 			}
   1155 			if wstat.Muid != "" && wstat.Muid != newStat.Muid {
   1156 				r.err = fmt.Errorf("changing muid is not permitted")
   1157 				goto resp
   1158 			}
   1159 			if wstat.Name != "" && newStat.Name != wstat.Name {
   1160 				parentPath := path.Dir(r.fid.path)
   1161 				pstat, err := fs.Stat(ExportFS{r.fid.fs}, parentPath)
   1162 				if err != nil {
   1163 					r.err = fmt.Errorf("stat parent: %v", err)
   1164 					goto resp
   1165 				}
   1166 				if !hasPerm(r.fid.fs, pstat, r.fid.uid, AWRITE) {
   1167 					r.err = ErrPerm
   1168 					goto resp
   1169 				}
   1170 				// TODO: I think 9P protocol prohibits renaming to existent file.
   1171 				// Wstat(9P) in p9p says:
   1172 				// 	it is an error to change the name to that of
   1173 				//	an existing file.
   1174 				// but 9pfs, 9pfuse does the rename when used with `git init`.
   1175 				/*
   1176 					de, err := fs.ReadDir(ExportFS{FS: r.fid.fs}, parentPath)
   1177 					if err != nil {
   1178 						r.err = fmt.Errorf("readdir: %v", err)
   1179 						goto resp
   1180 					}
   1181 					for _, e := range de {
   1182 						fi, err := e.Info()
   1183 						if err != nil {
   1184 							r.err = fmt.Errorf("stat: %v", err)
   1185 						}
   1186 						if fi.Name() == wstat.Name {
   1187 							r.err = fmt.Errorf("file already exists")
   1188 							goto resp
   1189 						}
   1190 					}
   1191 				*/
   1192 				newStat.Name = wstat.Name
   1193 			}
   1194 			if wstat.Length != ^int64(0) && wstat.Length != newStat.Length {
   1195 				// TODO: deal with wstat which changes directory length to 0
   1196 				if fi.IsDir() || !hasPerm(r.fid.fs, fi, r.fid.uid, AWRITE) {
   1197 					r.err = ErrPerm
   1198 					goto resp
   1199 				}
   1200 				newStat.Length = wstat.Length
   1201 			}
   1202 			if wstat.Mode != FileMode(^uint32(0)) && wstat.Mode != newStat.Mode {
   1203 				// the owner of the file or the group leader of the file'c group.
   1204 				if r.fid.uid != newStat.Uid && !isGroupLeader(r.fid.fs, newStat.Gid, r.fid.uid) {
   1205 					r.err = ErrPerm
   1206 					goto resp
   1207 				}
   1208 				if wstat.Mode&fs.ModeDir != newStat.Mode&fs.ModeDir {
   1209 					r.err = ErrPerm
   1210 					goto resp
   1211 				}
   1212 				newStat.Mode = wstat.Mode
   1213 			}
   1214 			if wstat.Mtime != ^uint32(0) && wstat.Mtime != newStat.Mtime {
   1215 				// the owner of the file or the group leader of the file'c group.
   1216 				if r.fid.uid != newStat.Uid && !isGroupLeader(r.fid.fs, newStat.Gid, r.fid.uid) {
   1217 					r.err = ErrPerm
   1218 					goto resp
   1219 				}
   1220 				newStat.Mtime = wstat.Mtime
   1221 			}
   1222 			if wstat.Gid != "" && wstat.Gid != newStat.Gid {
   1223 				// by the owner if also a member of the new group;
   1224 				// or by the group leader of the file'c current group if
   1225 				// also the leader of the new group.
   1226 				if r.fid.uid == newStat.Uid && isGroupMember(r.fid.fs, wstat.Gid, r.fid.uid) ||
   1227 					isGroupLeader(r.fid.fs, newStat.Gid, r.fid.uid) &&
   1228 						isGroupLeader(r.fid.fs, wstat.Gid, r.fid.uid) {
   1229 					newStat.Gid = wstat.Gid
   1230 				} else {
   1231 					r.err = ErrPerm
   1232 					goto resp
   1233 				}
   1234 			}
   1235 			err = wsfile.WStat(newStat)
   1236 			if err != nil {
   1237 				r.err = fmt.Errorf("wstat: %v", err)
   1238 				goto resp
   1239 			}
   1240 			if path.Base(r.fid.path) != newStat.Name {
   1241 				oldPath := r.fid.path
   1242 				r.fid.path = path.Join(path.Dir(oldPath), newStat.Name)
   1243 				// TODO: I think map[fid.path][]*fid is better.
   1244 				// I think there is a race condition.
   1245 				// e.g. create a file with the old name?
   1246 				c.fPool.Lock()
   1247 				for _, fid := range c.fPool.m {
   1248 					if fid.path == oldPath {
   1249 						fid.path = r.fid.path
   1250 					}
   1251 				}
   1252 				c.fPool.Unlock()
   1253 			}
   1254 			r.ofcall = &RWstat{}
   1255 		resp:
   1256 			if r.fid.omode == -1 && r.fid.file != nil {
   1257 				r.fid.file.Close()
   1258 			}
   1259 			if r.err != nil {
   1260 				setError(r, r.err)
   1261 			}
   1262 			select {
   1263 			case c.respChan <- r:
   1264 			case <-ctx.Done():
   1265 				return
   1266 			}
   1267 		}
   1268 	}
   1269 }
   1270 
   1271 // Serve serves 9P conversation.
   1272 func (s *Server) Serve(ctx context.Context, r io.Reader, w io.Writer) {
   1273 	ctx, cancel := context.WithCancel(ctx)
   1274 	defer cancel()
   1275 	rp := newReqPool()
   1276 	c := s.newConn(r, w)
   1277 	c.runListener(ctx, rp)
   1278 	c.runResponder(ctx, rp)
   1279 	var (
   1280 		versionChan = make(chan *request)
   1281 		authChan    = make(chan *request)
   1282 		attachChan  = make(chan *request)
   1283 		flushChan   = make(chan *request)
   1284 		walkChan    = make(chan *request)
   1285 		openChan    = make(chan *request)
   1286 		createChan  = make(chan *request)
   1287 		readChan    = make(chan *request)
   1288 		writeChan   = make(chan *request)
   1289 		clunkChan   = make(chan *request)
   1290 		removeChan  = make(chan *request)
   1291 		statChan    = make(chan *request)
   1292 		wstatChan   = make(chan *request)
   1293 	)
   1294 	defer func() { // TODO: unnecessary?
   1295 		close(versionChan)
   1296 		close(authChan)
   1297 		close(attachChan)
   1298 		close(flushChan)
   1299 		close(walkChan)
   1300 		close(openChan)
   1301 		close(createChan)
   1302 		close(readChan)
   1303 		close(writeChan)
   1304 		close(clunkChan)
   1305 		close(removeChan)
   1306 		close(statChan)
   1307 		close(wstatChan)
   1308 	}()
   1309 	go sVersion(ctx, c, versionChan)
   1310 	go sAuth(ctx, c, authChan)
   1311 	go sAttach(ctx, c, attachChan)
   1312 	go sFlush(ctx, c, flushChan)
   1313 	go sWalk(ctx, c, walkChan)
   1314 	go sOpen(ctx, c, openChan)
   1315 	go sCreate(ctx, c, createChan)
   1316 	go sRead(ctx, c, readChan)
   1317 	go sWrite(ctx, c, writeChan)
   1318 	go sClunk(ctx, c, clunkChan)
   1319 	go sRemove(ctx, c, removeChan)
   1320 	go sStat(ctx, c, statChan)
   1321 	go sWStat(ctx, c, wstatChan)
   1322 L:
   1323 	for {
   1324 		select {
   1325 		case <-ctx.Done():
   1326 			break L
   1327 		case r, ok := <-c.listenChan:
   1328 			if !ok {
   1329 				break L
   1330 			}
   1331 			if r.listenErr != nil {
   1332 				if r.listenErr == io.EOF {
   1333 					break L
   1334 				}
   1335 				log.Printf("listen: %v", r.listenErr)
   1336 				continue L
   1337 			}
   1338 			switch r.ifcall.(type) {
   1339 			default:
   1340 				setError(r, fmt.Errorf("unknown message type: %d", r.ifcall.Type()))
   1341 				select {
   1342 				case c.respChan <- r:
   1343 				case <-ctx.Done():
   1344 					return
   1345 				}
   1346 			case *TVersion:
   1347 				versionChan <- r
   1348 			case *TAuth:
   1349 				authChan <- r
   1350 			case *TAttach:
   1351 				attachChan <- r
   1352 			case *TFlush:
   1353 				flushChan <- r
   1354 			case *TWalk:
   1355 				walkChan <- r
   1356 			case *TOpen:
   1357 				openChan <- r
   1358 			case *TCreate:
   1359 				createChan <- r
   1360 			case *TRead:
   1361 				readChan <- r
   1362 			case *TWrite:
   1363 				writeChan <- r
   1364 			case *TClunk:
   1365 				clunkChan <- r
   1366 			case *TRemove:
   1367 				removeChan <- r
   1368 			case *TStat:
   1369 				statChan <- r
   1370 			case *TWstat:
   1371 				wstatChan <- r
   1372 			}
   1373 		}
   1374 	}
   1375 }