lib9p

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

server.go (30110B)


      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)
     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 wait 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 				c.s.Auth(ctx, r)
    282 				// TODO: should move this code to c.s.Auth?
    283 				if rauth, ok := r.ofcall.(*RAuth); ok {
    284 					r.afid.qidpath = rauth.Aqid.Path
    285 				}
    286 			}
    287 			select {
    288 			case c.respChan <- r:
    289 			case <-ctx.Done():
    290 				return
    291 			}
    292 		}
    293 	}
    294 }
    295 
    296 func sAttach(ctx context.Context, c *conn, rc <-chan *request) {
    297 	for {
    298 		select {
    299 		case <-ctx.Done():
    300 			return
    301 		case r, ok := <-rc:
    302 			if !ok {
    303 				return
    304 			}
    305 			var (
    306 				fsys FS
    307 				fi   fs.FileInfo
    308 				err  error
    309 			)
    310 			ifcall := r.ifcall.(*TAttach)
    311 			switch {
    312 			case c.s.Auth == nil && ifcall.Afid == NOFID:
    313 			case c.s.Auth == nil && ifcall.Afid != NOFID:
    314 				r.err = ErrBotch
    315 				goto resp
    316 			case c.s.Auth != nil && ifcall.Afid == NOFID:
    317 				r.err = fmt.Errorf("authentication required")
    318 				goto resp
    319 			case c.s.Auth != nil && ifcall.Afid != NOFID:
    320 				afid, ok := c.fPool.lookup(ifcall.Afid)
    321 				if !ok {
    322 					r.err = ErrUnknownFid
    323 					goto resp
    324 				}
    325 				af, ok := afid.file.(*AuthFile)
    326 				if !ok {
    327 					r.err = fmt.Errorf("not auth file")
    328 					goto resp
    329 				}
    330 				if af.Uname != ifcall.Uname || af.Aname != ifcall.Aname || !af.AuthOK {
    331 					r.err = fmt.Errorf("not authenticated")
    332 					goto resp
    333 				}
    334 			}
    335 			r.fid, err = c.fPool.add(ifcall.Fid)
    336 			if err != nil {
    337 				r.err = ErrDupFid
    338 				goto resp
    339 			}
    340 			r.fid.path = "."
    341 			r.fid.uid = ifcall.Uname
    342 			fsys, ok = c.s.fsmap[ifcall.Aname]
    343 			if !ok {
    344 				r.err = fmt.Errorf("no such file system")
    345 				goto resp
    346 			}
    347 			r.fid.fs = fsys
    348 			fi, err = fs.Stat(ExportFS{c.s.fsmap[ifcall.Aname]}, ".")
    349 			if err != nil {
    350 				r.err = fmt.Errorf("stat root: %v", err)
    351 				goto resp
    352 			}
    353 			r.fid.qidpath = fi.Sys().(*Stat).Qid.Path
    354 			r.ofcall = &RAttach{
    355 				Qid: fi.Sys().(*Stat).Qid,
    356 			}
    357 		resp:
    358 			if r.err != nil {
    359 				if r.fid != nil {
    360 					c.fPool.delete(r.fid.fid)
    361 				}
    362 				setError(r, r.err)
    363 			}
    364 			select {
    365 			case c.respChan <- r:
    366 			case <-ctx.Done():
    367 				return
    368 			}
    369 		}
    370 	}
    371 }
    372 
    373 func sFlush(ctx context.Context, c *conn, rc <-chan *request) {
    374 	for {
    375 		select {
    376 		case <-ctx.Done():
    377 			return
    378 		case r, ok := <-rc:
    379 			if !ok {
    380 				return
    381 			}
    382 			if r.oldreq != nil {
    383 				r.oldreq.flush()
    384 			}
    385 			r.ofcall = &RFlush{}
    386 			select {
    387 			case c.respChan <- r:
    388 			case <-ctx.Done():
    389 				return
    390 			}
    391 		}
    392 	}
    393 }
    394 
    395 // TODO: implement MAXWELEM.
    396 func sWalk(ctx context.Context, c *conn, rc <-chan *request) {
    397 	for {
    398 		select {
    399 		case <-ctx.Done():
    400 			return
    401 		case r, ok := <-rc:
    402 			if !ok {
    403 				return
    404 			}
    405 			var (
    406 				err    error
    407 				oldfi  fs.FileInfo
    408 				newFid *fid
    409 				wqids  []Qid
    410 				cwdp   string
    411 			)
    412 			ifcall := r.ifcall.(*TWalk)
    413 			oldFid, ok := c.fPool.lookup(ifcall.Fid)
    414 			if !ok {
    415 				r.err = ErrUnknownFid
    416 				goto resp
    417 			}
    418 			if oldFid.omode != -1 {
    419 				r.err = fmt.Errorf("cannot clone open fid")
    420 				goto resp
    421 			}
    422 			oldfi, err = fs.Stat(ExportFS{oldFid.fs}, oldFid.path)
    423 			if err != nil {
    424 				r.err = fmt.Errorf("stat: %v", err)
    425 				goto resp
    426 			}
    427 			if len(ifcall.Wnames) > 0 && oldfi.Sys().(*Stat).Qid.Type&QTDIR == 0 {
    428 				r.err = fmt.Errorf("walk on non-dir")
    429 				goto resp
    430 			}
    431 			if ifcall.Fid == ifcall.Newfid {
    432 				newFid = oldFid
    433 			} else {
    434 				newFid, err = c.fPool.add(ifcall.Newfid)
    435 				if err != nil {
    436 					r.err = fmt.Errorf("alloc: %v", err)
    437 					goto resp
    438 				}
    439 			}
    440 			wqids = make([]Qid, 0, len(ifcall.Wnames))
    441 			cwdp = oldFid.path
    442 			for _, name := range ifcall.Wnames {
    443 				cwdp = path.Clean(path.Join(cwdp, name))
    444 				if cwdp == ".." {
    445 					cwdp = "." // parent of the root is itself.
    446 				}
    447 				stat, err := fs.Stat(ExportFS{oldFid.fs}, cwdp)
    448 				if err != nil {
    449 					break
    450 				}
    451 				wqids = append(wqids, stat.Sys().(*Stat).Qid)
    452 			}
    453 			if len(wqids) == 0 {
    454 				newFid.qidpath = oldFid.qidpath
    455 			} else {
    456 				newFid.qidpath = wqids[len(wqids)-1].Path
    457 			}
    458 			newFid.path = cwdp
    459 			newFid.uid = oldFid.uid
    460 			newFid.fs = oldFid.fs
    461 			r.ofcall = &RWalk{
    462 				Qids: wqids,
    463 			}
    464 		resp:
    465 			if r.ofcall == nil {
    466 				setError(r, r.err)
    467 				select {
    468 				case c.respChan <- r:
    469 					continue
    470 				case <-ctx.Done():
    471 					return
    472 				}
    473 			}
    474 			ofcall := r.ofcall.(*RWalk)
    475 			if r.err != nil || len(ofcall.Qids) < len(ifcall.Wnames) {
    476 				if ifcall.Fid != ifcall.Newfid {
    477 					c.fPool.delete(ifcall.Newfid)
    478 				}
    479 				if len(ofcall.Qids) == 0 {
    480 					if r.err == nil && len(ifcall.Wnames) != 0 {
    481 						setError(r, ErrNotFound)
    482 					}
    483 				}
    484 			}
    485 			select {
    486 			case c.respChan <- r:
    487 			case <-ctx.Done():
    488 				return
    489 			}
    490 		}
    491 	}
    492 }
    493 
    494 func sOpen(ctx context.Context, c *conn, rc <-chan *request) {
    495 	for {
    496 		select {
    497 		case <-ctx.Done():
    498 			return
    499 		case r, ok := <-rc:
    500 			if !ok {
    501 				return
    502 			}
    503 			var (
    504 				p   fs.FileMode
    505 				err error
    506 				qid Qid
    507 				fi  fs.FileInfo
    508 			)
    509 			ifcall := r.ifcall.(*TOpen)
    510 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    511 			if !ok {
    512 				r.err = ErrUnknownFid
    513 				goto resp
    514 			}
    515 			if r.fid.omode != -1 {
    516 				r.err = ErrBotch
    517 				goto resp
    518 			}
    519 			if afile, ok := r.fid.file.(*AuthFile); ok {
    520 				// c.s.Auth should set r.fid.file to a valid *AuthFile,
    521 				// so r.fid.file should not be nil.
    522 				fi, err = r.fid.file.Stat()
    523 				if err != nil {
    524 					r.err = fmt.Errorf("stat: %v", err)
    525 					goto resp
    526 				}
    527 				qid = afile.Qid
    528 			} else {
    529 				fi, err = fs.Stat(ExportFS{r.fid.fs}, r.fid.path)
    530 				if err != nil {
    531 					r.err = fmt.Errorf("stat: %v", err)
    532 					goto resp
    533 				}
    534 				qid = fi.Sys().(*Stat).Qid
    535 			}
    536 			// Write attempt to a directory is prohibitted by the protocol.
    537 			// In plan9 implementation, ifcall.Mode is ANDed with ^ORCLOSE,
    538 			// but ORCLOSE is also prohibitted by the protocol...
    539 			if qid.Type == QTDIR && ifcall.Mode != OREAD {
    540 				r.err = fmt.Errorf("is a directory")
    541 				goto resp
    542 			}
    543 			switch ifcall.Mode & 3 {
    544 			case OREAD:
    545 				p = AREAD
    546 			case OWRITE:
    547 				p = AWRITE
    548 			case ORDWR:
    549 				p = AREAD | AWRITE
    550 			case OEXEC:
    551 				p = AEXEC
    552 			}
    553 			if ifcall.Mode&OTRUNC != 0 {
    554 				p |= AWRITE
    555 			}
    556 			if qid.Type&QTDIR != 0 && p != AREAD {
    557 				r.err = ErrPerm
    558 				goto resp
    559 			}
    560 			if !hasPerm(r.fid.fs, fi, r.fid.uid, p) {
    561 				r.err = ErrPerm
    562 				goto resp
    563 			}
    564 			if ifcall.Mode&ORCLOSE != 0 {
    565 				parentPath := path.Dir(r.fid.path)
    566 				fi, err := fs.Stat(ExportFS{r.fid.fs}, parentPath)
    567 				if err != nil {
    568 					r.err = fmt.Errorf("stat parent: %v", err)
    569 					goto resp
    570 				}
    571 				if !hasPerm(r.fid.fs, fi, r.fid.uid, AWRITE) {
    572 					r.err = ErrPerm
    573 					goto resp
    574 				}
    575 			}
    576 			r.ofcall = &ROpen{
    577 				Qid:    qid,
    578 				Iounit: c.mSize() - IOHDRSZ,
    579 			}
    580 		resp:
    581 			if r.err != nil {
    582 				setError(r, r.err)
    583 				goto send
    584 			}
    585 			if _, ok := r.fid.file.(*AuthFile); ok {
    586 				r.fid.omode = ifcall.Mode
    587 				goto send
    588 			}
    589 			r.fid.file, err = r.fid.fs.OpenFile(r.fid.path, ModeToFlag(ifcall.Mode))
    590 			if err != nil {
    591 				setError(r, err)
    592 				goto send
    593 			}
    594 			fi, err = r.fid.file.Stat()
    595 			if err != nil {
    596 				r.fid.file.Close()
    597 				setError(r, err)
    598 				goto send
    599 			}
    600 			if fi.Sys().(*Stat).Qid.Path != r.fid.qidpath {
    601 				log.Println("open:", fi.Sys().(*Stat).Qid.Path, r.fid.qidpath)
    602 				r.fid.file.Close()
    603 				setError(r, fmt.Errorf("qid path mismatch"))
    604 				goto send
    605 			}
    606 			// omode should be set after successfully opening it.
    607 			r.fid.omode = ifcall.Mode
    608 		send:
    609 			select {
    610 			case c.respChan <- r:
    611 			case <-ctx.Done():
    612 				return
    613 			}
    614 		}
    615 	}
    616 }
    617 
    618 func sCreate(ctx context.Context, c *conn, rc <-chan *request) {
    619 	for {
    620 		select {
    621 		case <-ctx.Done():
    622 			return
    623 		case r, ok := <-rc:
    624 			if !ok {
    625 				return
    626 			}
    627 			var (
    628 				fi      fs.FileInfo
    629 				dirfi   fs.FileInfo
    630 				err     error
    631 				cfs     CreatorFS
    632 				cpath   string
    633 				perm    FileMode
    634 				dirperm FileMode
    635 			)
    636 			ifcall := r.ifcall.(*TCreate)
    637 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    638 			if !ok {
    639 				r.err = ErrUnknownFid
    640 				goto resp
    641 			}
    642 			if r.fid.omode != -1 {
    643 				r.err = ErrBotch
    644 				goto resp
    645 			}
    646 			dirfi, err = fs.Stat(ExportFS{r.fid.fs}, r.fid.path)
    647 			if err != nil {
    648 				r.err = fmt.Errorf("stat: %v", err)
    649 				goto resp
    650 			}
    651 			if !dirfi.IsDir() {
    652 				r.err = fmt.Errorf("create in non-dir")
    653 				goto resp
    654 			}
    655 			if !hasPerm(r.fid.fs, dirfi, r.fid.uid, AWRITE) {
    656 				r.err = ErrPerm
    657 				goto resp
    658 			}
    659 			cfs, ok = r.fid.fs.(CreatorFS)
    660 			if !ok {
    661 				r.err = ErrOperation
    662 				goto resp
    663 			}
    664 			perm = ifcall.Perm
    665 			dirperm = dirfi.Mode()
    666 			if perm&fs.ModeDir == 0 {
    667 				perm &= ^FileMode(0666) | (dirperm & FileMode(0666))
    668 			} else {
    669 				perm &= ^FileMode(0777) | (dirperm & FileMode(0777))
    670 			}
    671 			cpath = path.Join(r.fid.path, ifcall.Name)
    672 			r.fid.file, err = cfs.Create(cpath, r.fid.uid, ifcall.Mode, perm)
    673 			if err != nil {
    674 				r.err = fmt.Errorf("create: %v", err)
    675 				goto resp
    676 			}
    677 			r.fid.path = cpath
    678 			r.fid.omode = ifcall.Mode
    679 			fi, err = r.fid.file.Stat()
    680 			if err != nil {
    681 				r.err = fmt.Errorf("stat: %v", err)
    682 				goto resp
    683 			}
    684 			r.fid.qidpath = fi.Sys().(*Stat).Qid.Path
    685 			r.ofcall = &RCreate{
    686 				Qid:    fi.Sys().(*Stat).Qid,
    687 				Iounit: c.mSize() - IOHDRSZ,
    688 			}
    689 		resp:
    690 			if r.err != nil {
    691 				setError(r, r.err)
    692 			}
    693 			select {
    694 			case c.respChan <- r:
    695 			case <-ctx.Done():
    696 				return
    697 			}
    698 		}
    699 	}
    700 }
    701 
    702 // TODO: I think the file should be locked while reading.
    703 // or should the undeterminism left for the client side?
    704 func sRead(ctx context.Context, c *conn, rc <-chan *request) {
    705 	for {
    706 		select {
    707 		case <-ctx.Done():
    708 			return
    709 		case r, ok := <-rc:
    710 			if !ok {
    711 				return
    712 			}
    713 			var (
    714 				fi   fs.FileInfo
    715 				err  error
    716 				data []byte
    717 				done chan struct{}
    718 				n    int
    719 			)
    720 			ifcall := r.ifcall.(*TRead)
    721 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    722 			if !ok {
    723 				r.err = ErrUnknownFid
    724 				goto resp
    725 			}
    726 			if r.fid.omode == -1 {
    727 				r.err = fmt.Errorf("not open")
    728 				goto resp
    729 			}
    730 			if r.fid.omode != OREAD && r.fid.omode != ORDWR && r.fid.omode != OEXEC {
    731 				r.err = ErrPerm
    732 				goto resp
    733 			}
    734 			fi, err = r.fid.file.Stat()
    735 			if err != nil {
    736 				r.err = fmt.Errorf("stat: %v", err)
    737 				goto resp
    738 			}
    739 			if c.mSize()-IOHDRSZ < ifcall.Count {
    740 				ifcall.Count = c.mSize() - IOHDRSZ
    741 			}
    742 			data = make([]byte, ifcall.Count)
    743 			done = make(chan struct{})
    744 			go func() {
    745 				defer close(done)
    746 				if fi.IsDir() {
    747 					if ifcall.Offset != 0 && ifcall.Offset != r.fid.dirOffset {
    748 						err = fmt.Errorf("invalid dir offset")
    749 						return
    750 					}
    751 					if ifcall.Offset == 0 && r.fid.dirIndex != 0 {
    752 						r.fid.dirIndex = 0
    753 						r.fid.dirOffset = 0
    754 						r.fid.dirEnts = nil
    755 						if err = r.fid.file.Close(); err != nil {
    756 							return
    757 						}
    758 						r.fid.file, err = r.fid.fs.OpenFile(r.fid.path, ModeToFlag(r.fid.omode))
    759 						if err != nil {
    760 							return
    761 						}
    762 					}
    763 					if r.fid.dirIndex == 0 {
    764 						r.fid.dirEnts, err = r.fid.file.(ReadDirFile).ReadDir(-1)
    765 						if err != nil {
    766 							return
    767 						}
    768 					}
    769 					k := r.fid.dirIndex
    770 					for ; k < len(r.fid.dirEnts); k++ {
    771 						fi, err := r.fid.dirEnts[k].Info()
    772 						if err != nil {
    773 							log.Println(err)
    774 							continue
    775 						}
    776 						st := fi.Sys().(*Stat)
    777 						buf := st.marshal()
    778 						if n+len(buf) > len(data) {
    779 							break
    780 						}
    781 						for i := 0; i < len(buf); i++ {
    782 							data[n+i] = buf[i]
    783 						}
    784 						n += len(buf)
    785 					}
    786 					r.fid.dirOffset += uint64(n)
    787 					r.fid.dirIndex += k
    788 				} else {
    789 					if reader, ok := r.fid.file.(io.ReaderAt); ok {
    790 						n, err = reader.ReadAt(data, int64(ifcall.Offset))
    791 					} else {
    792 						n, err = r.fid.file.Read(data)
    793 					}
    794 					if err == io.EOF {
    795 						err = nil
    796 					}
    797 				}
    798 			}()
    799 			select {
    800 			case <-done:
    801 				if err != nil {
    802 					r.err = err
    803 					goto resp
    804 				}
    805 			case <-r.done:
    806 				continue
    807 			case <-ctx.Done():
    808 				return
    809 			}
    810 			r.ofcall = &RRead{
    811 				Count: uint32(n),
    812 				Data:  data[:n],
    813 			}
    814 		resp:
    815 			if r.err != nil {
    816 				setError(r, r.err)
    817 			}
    818 			select {
    819 			case c.respChan <- r:
    820 			case <-ctx.Done():
    821 				return
    822 			}
    823 		}
    824 	}
    825 }
    826 
    827 // TODO: I think the file should be locked while writing.
    828 func sWrite(ctx context.Context, c *conn, rc <-chan *request) {
    829 	for {
    830 		select {
    831 		case <-ctx.Done():
    832 			return
    833 		case r, ok := <-rc:
    834 			if !ok {
    835 				return
    836 			}
    837 			var (
    838 				ofcall *RWrite
    839 				done   chan struct{}
    840 				omode  OpenMode
    841 				err    error
    842 			)
    843 			ifcall := r.ifcall.(*TWrite)
    844 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    845 			if !ok {
    846 				r.err = ErrUnknownFid
    847 				goto resp
    848 			}
    849 			if c.mSize()-IOHDRSZ < ifcall.Count {
    850 				ifcall.Count = c.mSize() - IOHDRSZ
    851 			}
    852 			omode = r.fid.omode & 3
    853 			if omode != OWRITE && omode != ORDWR {
    854 				r.err = fmt.Errorf("write on fid with open mode 0x%x", r.fid.omode)
    855 				goto resp
    856 			}
    857 			ofcall = new(RWrite)
    858 			done = make(chan struct{})
    859 			go func() {
    860 				defer close(done)
    861 				var n int
    862 				switch file := r.fid.file.(type) {
    863 				case io.WriterAt:
    864 					n, err = file.WriteAt(ifcall.Data, int64(ifcall.Offset))
    865 					if err != nil {
    866 						return
    867 					}
    868 					ofcall.Count = uint32(n)
    869 				case io.Writer:
    870 					n, err = file.Write(ifcall.Data)
    871 					if err != nil {
    872 						return
    873 					}
    874 					ofcall.Count = uint32(n)
    875 				default:
    876 					err = ErrOperation
    877 					return
    878 				}
    879 			}()
    880 			select {
    881 			case <-done:
    882 				if err != nil {
    883 					r.err = err
    884 					goto resp
    885 				}
    886 			case <-r.done:
    887 				continue
    888 			case <-ctx.Done():
    889 				return
    890 			}
    891 			r.ofcall = ofcall
    892 		resp:
    893 			if r.err != nil {
    894 				setError(r, r.err)
    895 			}
    896 			select {
    897 			case c.respChan <- r:
    898 			case <-ctx.Done():
    899 				return
    900 			}
    901 		}
    902 	}
    903 }
    904 
    905 func sClunk(ctx context.Context, c *conn, rc <-chan *request) {
    906 	for {
    907 		select {
    908 		case <-ctx.Done():
    909 			return
    910 		case r, ok := <-rc:
    911 			if !ok {
    912 				return
    913 			}
    914 			ifcall := r.ifcall.(*TClunk)
    915 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    916 			if !ok {
    917 				r.err = ErrUnknownFid
    918 				goto resp
    919 			}
    920 			c.fPool.delete(ifcall.Fid)
    921 			if r.fid.omode != -1 {
    922 				if err := r.fid.file.Close(); err != nil {
    923 					r.err = fmt.Errorf("close: %v", err)
    924 					goto resp
    925 				}
    926 				if r.fid.omode&ORCLOSE != 0 {
    927 					rfs, ok := r.fid.fs.(RemoverFS)
    928 					if !ok {
    929 						r.err = ErrOperation
    930 						goto resp
    931 					}
    932 					if err := rfs.Remove(r.fid.path); err != nil {
    933 						r.err = err
    934 						goto resp
    935 					}
    936 				}
    937 			}
    938 			r.ofcall = &RClunk{}
    939 		resp:
    940 			if r.err != nil {
    941 				setError(r, r.err)
    942 			}
    943 			select {
    944 			case c.respChan <- r:
    945 			case <-ctx.Done():
    946 				return
    947 			}
    948 		}
    949 	}
    950 }
    951 
    952 // sRemove serves Tremove messages.
    953 // TODO: RemoverFS.Remove function takes as argument the file path to be
    954 // removed. But in the protocol, files are identified by Qid.Path, not by
    955 // path name from the root of the file system.
    956 // But os package also uses the file path to remove an file.
    957 // And the library should not care the inconsistency of Qid.Path?
    958 func sRemove(ctx context.Context, c *conn, rc <-chan *request) {
    959 	for {
    960 		select {
    961 		case <-ctx.Done():
    962 			return
    963 		case r, ok := <-rc:
    964 			if !ok {
    965 				return
    966 			}
    967 			var (
    968 				parentPath string
    969 				pfi        fs.FileInfo
    970 				fi         fs.FileInfo
    971 				err        error
    972 				rfs        RemoverFS
    973 			)
    974 			ifcall := r.ifcall.(*TRemove)
    975 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
    976 			if !ok {
    977 				r.err = ErrUnknownFid
    978 				goto resp
    979 			}
    980 			c.fPool.delete(ifcall.Fid)
    981 			if r.fid.omode != -1 {
    982 				r.fid.file.Close()
    983 			}
    984 			parentPath = path.Dir(r.fid.path)
    985 			pfi, err = fs.Stat(ExportFS{r.fid.fs}, parentPath)
    986 			if err != nil {
    987 				r.err = fmt.Errorf("stat parent: %v", err)
    988 				goto resp
    989 			}
    990 			if !hasPerm(r.fid.fs, pfi, r.fid.uid, AWRITE) {
    991 				r.err = ErrPerm
    992 				goto resp
    993 			}
    994 			// BUG: race. Remove call below uses r.fid.path, so I need to
    995 			// check whether the underlying qid is the same.
    996 			// But other requests can move the same file and then create
    997 			// new one with the same name.
    998 			fi, err = fs.Stat(ExportFS{FS: r.fid.fs}, r.fid.path)
    999 			if err != nil {
   1000 				r.err = err
   1001 				goto resp
   1002 			}
   1003 			if r.fid.qidpath != fi.Sys().(*Stat).Qid.Path {
   1004 				r.err = fmt.Errorf("qid path mismatch")
   1005 				goto resp
   1006 			}
   1007 			rfs, ok = r.fid.fs.(RemoverFS)
   1008 			if !ok {
   1009 				r.err = ErrOperation
   1010 				goto resp
   1011 			}
   1012 			// I think the argument of RemoverFS.Remove should be Qid.Path.
   1013 			if err = rfs.Remove(r.fid.path); err != nil {
   1014 				r.err = fmt.Errorf("remove: %v", err)
   1015 				goto resp
   1016 			}
   1017 			r.ofcall = &RRemove{}
   1018 		resp:
   1019 			if r.err != nil {
   1020 				setError(r, r.err)
   1021 			}
   1022 			select {
   1023 			case c.respChan <- r:
   1024 			case <-ctx.Done():
   1025 				return
   1026 			}
   1027 		}
   1028 	}
   1029 }
   1030 
   1031 func sStat(ctx context.Context, c *conn, rc <-chan *request) {
   1032 	for {
   1033 		select {
   1034 		case <-ctx.Done():
   1035 			return
   1036 		case r, ok := <-rc:
   1037 			if !ok {
   1038 				return
   1039 			}
   1040 			var (
   1041 				fi  fs.FileInfo
   1042 				err error
   1043 			)
   1044 			ifcall := r.ifcall.(*TStat)
   1045 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
   1046 			if !ok {
   1047 				r.err = ErrUnknownFid
   1048 				goto resp
   1049 			}
   1050 			fi, err = fs.Stat(ExportFS{r.fid.fs}, r.fid.path)
   1051 			if err != nil {
   1052 				r.err = fmt.Errorf("stat: %v", err)
   1053 				goto resp
   1054 			}
   1055 			r.ofcall = &RStat{
   1056 				Stat: fi.Sys().(*Stat),
   1057 			}
   1058 		resp:
   1059 			if r.err != nil {
   1060 				setError(r, r.err)
   1061 			}
   1062 			select {
   1063 			case c.respChan <- r:
   1064 			case <-ctx.Done():
   1065 				return
   1066 			}
   1067 		}
   1068 	}
   1069 }
   1070 
   1071 func sWStat(ctx context.Context, c *conn, rc <-chan *request) {
   1072 	for {
   1073 		select {
   1074 		case <-ctx.Done():
   1075 			return
   1076 		case r, ok := <-rc:
   1077 			if !ok {
   1078 				return
   1079 			}
   1080 			var (
   1081 				wsfile  WriterStatFile
   1082 				wstat   *Stat
   1083 				newStat *Stat
   1084 				fi      fs.FileInfo
   1085 				err     error
   1086 			)
   1087 			ifcall := r.ifcall.(*TWstat)
   1088 			r.fid, ok = c.fPool.lookup(ifcall.Fid)
   1089 			if !ok {
   1090 				r.err = ErrUnknownFid
   1091 				goto resp
   1092 			}
   1093 			if r.fid.omode == -1 {
   1094 				var err error
   1095 				r.fid.file, err = r.fid.fs.OpenFile(r.fid.path, O_RDONLY)
   1096 				if err != nil {
   1097 					r.err = fmt.Errorf("open: %v", err)
   1098 					goto resp
   1099 				}
   1100 			}
   1101 			wsfile, ok = r.fid.file.(WriterStatFile)
   1102 			if !ok {
   1103 				r.err = ErrOperation
   1104 				goto resp
   1105 			}
   1106 			wstat = ifcall.Stat
   1107 			fi, err = r.fid.file.Stat()
   1108 			if err != nil {
   1109 				r.err = fmt.Errorf("stat: %v", err)
   1110 				goto resp
   1111 			}
   1112 			newStat = fi.Sys().(*Stat)
   1113 			if r.fid.qidpath != newStat.Qid.Path {
   1114 				r.err = fmt.Errorf("qid mismatch")
   1115 				goto resp
   1116 			}
   1117 			if wstat.Type != ^uint16(0) && wstat.Type != newStat.Type ||
   1118 				wstat.Dev != ^uint32(0) && wstat.Dev != newStat.Dev ||
   1119 				wstat.Qid.Type != QidType(^uint8(0)) && wstat.Qid.Type != newStat.Qid.Type ||
   1120 				wstat.Qid.Vers != ^uint32(0) && wstat.Qid.Vers != newStat.Qid.Vers ||
   1121 				wstat.Qid.Path != ^uint64(0) && wstat.Qid.Path != newStat.Qid.Path ||
   1122 				wstat.Atime != ^uint32(0) && wstat.Atime != newStat.Atime ||
   1123 				wstat.Uid != "" && wstat.Uid != newStat.Uid ||
   1124 				wstat.Muid != "" && wstat.Muid != newStat.Muid {
   1125 				r.err = fmt.Errorf("operation not permitted")
   1126 				goto resp
   1127 			}
   1128 			if wstat.Name != "" && newStat.Name != wstat.Name {
   1129 				parentPath := path.Dir(r.fid.path)
   1130 				pstat, err := fs.Stat(ExportFS{r.fid.fs}, parentPath)
   1131 				if err != nil {
   1132 					r.err = fmt.Errorf("stat parent: %v", err)
   1133 					goto resp
   1134 				}
   1135 				if !hasPerm(r.fid.fs, pstat, r.fid.uid, AWRITE) {
   1136 					r.err = ErrPerm
   1137 					goto resp
   1138 				}
   1139 				// TODO: I think 9P protocol prohibits renaming to existent file.
   1140 				// Wstat(9P) in p9p says:
   1141 				// 	it is an error to change the name to that of
   1142 				//	an existing file.
   1143 				// but 9pfs, 9pfuse does the rename when used with `git init`.
   1144 				/*
   1145 					de, err := fs.ReadDir(ExportFS{FS: r.fid.fs}, parentPath)
   1146 					if err != nil {
   1147 						r.err = fmt.Errorf("readdir: %v", err)
   1148 						goto resp
   1149 					}
   1150 					for _, e := range de {
   1151 						fi, err := e.Info()
   1152 						if err != nil {
   1153 							r.err = fmt.Errorf("stat: %v", err)
   1154 						}
   1155 						if fi.Name() == wstat.Name {
   1156 							r.err = fmt.Errorf("file already exists")
   1157 							goto resp
   1158 						}
   1159 					}
   1160 				*/
   1161 				newStat.Name = wstat.Name
   1162 			}
   1163 			if wstat.Length != ^int64(0) && wstat.Length != newStat.Length {
   1164 				// TODO: deal with wstat which changes directory length to 0
   1165 				if fi.IsDir() || !hasPerm(r.fid.fs, fi, r.fid.uid, AWRITE) {
   1166 					r.err = ErrPerm
   1167 					goto resp
   1168 				}
   1169 				newStat.Length = wstat.Length
   1170 			}
   1171 			if wstat.Mode != FileMode(^uint32(0)) && wstat.Mode != newStat.Mode {
   1172 				// the owner of the file or the group leader of the file'c group.
   1173 				if r.fid.uid != newStat.Uid && !isGroupLeader(r.fid.fs, newStat.Gid, r.fid.uid) {
   1174 					r.err = ErrPerm
   1175 					goto resp
   1176 				}
   1177 				if wstat.Mode&fs.ModeDir != newStat.Mode&fs.ModeDir {
   1178 					r.err = ErrPerm
   1179 					goto resp
   1180 				}
   1181 				newStat.Mode = wstat.Mode
   1182 			}
   1183 			if wstat.Mtime != ^uint32(0) && wstat.Mtime != newStat.Mtime {
   1184 				// the owner of the file or the group leader of the file'c group.
   1185 				if r.fid.uid != newStat.Uid && !isGroupLeader(r.fid.fs, newStat.Gid, r.fid.uid) {
   1186 					r.err = ErrPerm
   1187 					goto resp
   1188 				}
   1189 				newStat.Mtime = wstat.Mtime
   1190 			}
   1191 			if wstat.Gid != "" && wstat.Gid != newStat.Gid {
   1192 				// by the owner if also a member of the new group;
   1193 				// or by the group leader of the file'c current group if
   1194 				// also the leader of the new group.
   1195 				if r.fid.uid == newStat.Uid && isGroupMember(r.fid.fs, wstat.Gid, r.fid.uid) ||
   1196 					isGroupLeader(r.fid.fs, newStat.Gid, r.fid.uid) &&
   1197 						isGroupLeader(r.fid.fs, wstat.Gid, r.fid.uid) {
   1198 					newStat.Gid = wstat.Gid
   1199 				} else {
   1200 					r.err = ErrPerm
   1201 					goto resp
   1202 				}
   1203 			}
   1204 			err = wsfile.WStat(newStat)
   1205 			if err != nil {
   1206 				r.err = fmt.Errorf("wstat: %v", err)
   1207 				goto resp
   1208 			}
   1209 			if path.Base(r.fid.path) != newStat.Name {
   1210 				r.fid.path = path.Join(path.Dir(r.fid.path), newStat.Name)
   1211 			}
   1212 			r.ofcall = &RWstat{}
   1213 		resp:
   1214 			if r.fid.omode == -1 && r.fid.file != nil {
   1215 				r.fid.file.Close()
   1216 			}
   1217 			if r.err != nil {
   1218 				setError(r, r.err)
   1219 			}
   1220 			select {
   1221 			case c.respChan <- r:
   1222 			case <-ctx.Done():
   1223 				return
   1224 			}
   1225 		}
   1226 	}
   1227 }
   1228 
   1229 // Serve serves 9P conversation.
   1230 func (s *Server) Serve(ctx context.Context, r io.Reader, w io.Writer) {
   1231 	ctx, cancel := context.WithCancel(ctx)
   1232 	defer cancel()
   1233 	rp := newReqPool()
   1234 	c := s.newConn(r, w)
   1235 	c.runListener(ctx, rp)
   1236 	c.runResponder(ctx, rp)
   1237 	var (
   1238 		versionChan = make(chan *request)
   1239 		authChan    = make(chan *request)
   1240 		attachChan  = make(chan *request)
   1241 		flushChan   = make(chan *request)
   1242 		walkChan    = make(chan *request)
   1243 		openChan    = make(chan *request)
   1244 		createChan  = make(chan *request)
   1245 		readChan    = make(chan *request)
   1246 		writeChan   = make(chan *request)
   1247 		clunkChan   = make(chan *request)
   1248 		removeChan  = make(chan *request)
   1249 		statChan    = make(chan *request)
   1250 		wstatChan   = make(chan *request)
   1251 	)
   1252 	defer func() { // TODO: unnecessary?
   1253 		close(versionChan)
   1254 		close(authChan)
   1255 		close(attachChan)
   1256 		close(flushChan)
   1257 		close(walkChan)
   1258 		close(openChan)
   1259 		close(createChan)
   1260 		close(readChan)
   1261 		close(writeChan)
   1262 		close(clunkChan)
   1263 		close(removeChan)
   1264 		close(statChan)
   1265 		close(wstatChan)
   1266 	}()
   1267 	go sVersion(ctx, c, versionChan)
   1268 	go sAuth(ctx, c, authChan)
   1269 	go sAttach(ctx, c, attachChan)
   1270 	go sFlush(ctx, c, flushChan)
   1271 	go sWalk(ctx, c, walkChan)
   1272 	go sOpen(ctx, c, openChan)
   1273 	go sCreate(ctx, c, createChan)
   1274 	go sRead(ctx, c, readChan)
   1275 	go sWrite(ctx, c, writeChan)
   1276 	go sClunk(ctx, c, clunkChan)
   1277 	go sRemove(ctx, c, removeChan)
   1278 	go sStat(ctx, c, statChan)
   1279 	go sWStat(ctx, c, wstatChan)
   1280 L:
   1281 	for {
   1282 		select {
   1283 		case <-ctx.Done():
   1284 			break L
   1285 		case r, ok := <-c.listenChan:
   1286 			if !ok {
   1287 				break L
   1288 			}
   1289 			if r.listenErr != nil {
   1290 				if r.listenErr == io.EOF {
   1291 					break L
   1292 				}
   1293 				log.Printf("listen: %v", r.listenErr)
   1294 				continue L
   1295 			}
   1296 			switch r.ifcall.(type) {
   1297 			default:
   1298 				setError(r, fmt.Errorf("unknown message type: %d", r.ifcall.Type()))
   1299 				select {
   1300 				case c.respChan <- r:
   1301 				case <-ctx.Done():
   1302 					return
   1303 				}
   1304 			case *TVersion:
   1305 				versionChan <- r
   1306 			case *TAuth:
   1307 				authChan <- r
   1308 			case *TAttach:
   1309 				attachChan <- r
   1310 			case *TFlush:
   1311 				flushChan <- r
   1312 			case *TWalk:
   1313 				walkChan <- r
   1314 			case *TOpen:
   1315 				openChan <- r
   1316 			case *TCreate:
   1317 				createChan <- r
   1318 			case *TRead:
   1319 				readChan <- r
   1320 			case *TWrite:
   1321 				writeChan <- r
   1322 			case *TClunk:
   1323 				clunkChan <- r
   1324 			case *TRemove:
   1325 				removeChan <- r
   1326 			case *TStat:
   1327 				statChan <- r
   1328 			case *TWstat:
   1329 				wstatChan <- r
   1330 			}
   1331 		}
   1332 	}
   1333 }