lib9p

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

commit e3315609106a109686816ddb5772eb7de2807a77
parent 72c9a8f3eefc0ab1eb1509e6acd24a948345cc90
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Sun, 31 Dec 2023 09:18:55 +0900

add conn struct

Diffstat:
Mserver.go | 337++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 171 insertions(+), 166 deletions(-)

diff --git a/server.go b/server.go @@ -37,17 +37,37 @@ type Server struct { // The file system to export via 9P. fs FS + // Auth function is passed an auth request when a TAuth message arrives + // If authentication is desired, Auth should + // set request.Afid.file to an *AuthFile and request.ofcall.Qid, and prepare to + // authenticate via the Read()/Write() calls to request.Afid.file. + // Auth should clean up everything it creates when ctx is canceled. + // If this is nil, no authentication is performed. + Auth func(ctx context.Context, r *request) +} + +// NewServer creates a Server and runs listener and responder goroutines. +// It reads incoming messages from r and writes responses to w. +func NewServer(fsys FS) *Server { + s := &Server{ + fs: fsys, + } + return s +} + +// Chatty enables the server's log messages of 9P messages. +func (s *Server) Chatty() { s.chatty9P = true } + +type conn struct { + s *Server // Maximum length in byte of 9P messages. msize uint32 // Mutex to change msize. mSizeLock *sync.Mutex - // FidPool of the Server. + // FidPool of the connection. fPool *fidPool - // Pending Requests the server is dealing with. - rPool *reqPool - // The channel from which incoming requests are delivered by the // listener goroutine. listenChan <-chan *request @@ -62,59 +82,43 @@ type Server struct { // w is the io.Writer which the responder goroutine writes to. // It should be accessed only by the responder goroutine. w io.Writer - - // Auth function is passed an auth request when a TAuth message arrives - // If authentication is desired, Auth should - // set request.Afid.file to an *AuthFile and request.ofcall.Qid, and prepare to - // authenticate via the Read()/Write() calls to request.Afid.file. - // Auth should clean up everything it creates when ctx is canceled. - // If this is nil, no authentication is performed. - Auth func(ctx context.Context, r *request) } -// NewServer creates a Server and runs listener and responder goroutines. -// It reads incoming messages from r and writes responses to w. -func NewServer(fsys FS, mSize uint32, r io.Reader, w io.Writer) *Server { - s := &Server{ - fs: fsys, - msize: mSize, +func newConn(r io.Reader, w io.Writer) *conn { + return &conn{ + msize: 8*1024, mSizeLock: new(sync.Mutex), - fPool: newFidPool(), - r: r, - w: w, - respChan: make(chan *request), + fPool: newFidPool(), + r: r, + w: w, } - return s } -// Chatty enables the server's log messages of 9P messages. -func (s *Server) Chatty() { s.chatty9P = true } - // mSize reads the maximum message size of the server. -func (s *Server) mSize() uint32 { - s.mSizeLock.Lock() - defer s.mSizeLock.Unlock() - return s.msize +func (c *conn) mSize() uint32 { + c.mSizeLock.Lock() + defer c.mSizeLock.Unlock() + return c.msize } // setMSize changes the server's maximum message size. -func (s *Server) setMSize(mSize uint32) { - s.mSizeLock.Lock() - defer s.mSizeLock.Unlock() - s.msize = mSize +func (c *conn) setMSize(mSize uint32) { + c.mSizeLock.Lock() + defer c.mSizeLock.Unlock() + c.msize = mSize } // runListener runs the listener goroutine. // Listener goroutine reads 9P messages from s.r by calling getReq // and allocats request for each of them, and sends it to the server's listenChan. -func (s *Server) runListener(ctx context.Context, rp *reqPool) { +func (c *conn) runListener(ctx context.Context, rp *reqPool) { rc := make(chan *request) - s.listenChan = rc + c.listenChan = rc go func() { defer close(rc) for { select { - case rc <- getReq(s.r, rp, s.chatty9P): + case rc <- getReq(c.r, rp, c.s.chatty9P): case <-ctx.Done(): return } @@ -124,10 +128,10 @@ func (s *Server) runListener(ctx context.Context, rp *reqPool) { // runResponder runs the responder goroutine. // Responder goroutine wait for reply Requests from the returned channel, -// and marshalls each of them into 9P messages and writes it to s.w. -func (s *Server) runResponder(ctx context.Context, rp *reqPool) { +// and marshalls each of them into 9P messages and writes it to c.w. +func (c *conn) runResponder(ctx context.Context, rp *reqPool) { rc := make(chan *request) - s.respChan = rc + c.respChan = rc go func() { for { select { @@ -138,13 +142,13 @@ func (s *Server) runResponder(ctx context.Context, rp *reqPool) { r.ofcall.SetTag(r.tag) // free tag. rp.delete(r.tag) - _, err := s.w.Write(r.ofcall.marshal()) + _, err := c.w.Write(r.ofcall.marshal()) if err != nil { // TODO: handle error. log.Printf("respond: %v", err) continue } - if s.chatty9P { + if c.s.chatty9P { fmt.Fprintf(os.Stderr, "--> %s\n", r.ofcall) } case <-ctx.Done(): @@ -154,7 +158,7 @@ func (s *Server) runResponder(ctx context.Context, rp *reqPool) { }() } -// GetReq reads 9P message from s.r, allocates request, adds it to s.rPool, +// GetReq reads 9P message from r, allocates request, adds it to s.rPool, // and returns it. // Any error it encountered is embedded into the request struct. // This function is called only by the server's listener goroutine, @@ -199,12 +203,12 @@ func getReq(r io.Reader, rp *reqPool, chatty bool) *request { // server's mSize to the message's one. // TODO: abort all outstanding I/O on the same connection before // serving new Tversion. -func sVersion(ctx context.Context, s *Server, c <-chan *request) { +func sVersion(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -216,16 +220,16 @@ func sVersion(ctx context.Context, s *Server, c <-chan *request) { version = "9P2000" } msize := ifcall.Msize - if msize > s.mSize() { - msize = s.mSize() + if msize > c.mSize() { + msize = c.mSize() } r.ofcall = &RVersion{ Msize: msize, Version: version, } - s.setMSize(r.ofcall.(*RVersion).Msize) + c.setMSize(r.ofcall.(*RVersion).Msize) select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -234,19 +238,19 @@ func sVersion(ctx context.Context, s *Server, c <-chan *request) { } // sAuth serves Tauth message. -func sAuth(ctx context.Context, s *Server, c <-chan *request) { +func sAuth(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } - if s.Auth == nil { + if c.s.Auth == nil { setError(r, fmt.Errorf("authentication not required")) select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -257,34 +261,34 @@ func sAuth(ctx context.Context, s *Server, c <-chan *request) { if ifcall.Afid == NOFID { setError(r, fmt.Errorf("NOFID can't be used for afid")) // TODO: really? select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } continue } - r.afid, err = s.fPool.add(ifcall.Afid) + r.afid, err = c.fPool.add(ifcall.Afid) if err != nil { setError(r, ErrDupFid) select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } continue } - s.Auth(ctx, r) - s.respChan <- r + c.s.Auth(ctx, r) + c.respChan <- r } } } -func sAttach(ctx context.Context, s *Server, c <-chan *request) { +func sAttach(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: var ( st fs.FileInfo err error @@ -293,21 +297,21 @@ func sAttach(ctx context.Context, s *Server, c <-chan *request) { return } ifcall := r.ifcall.(*TAttach) - r.fid, err = s.fPool.add(ifcall.Fid) + r.fid, err = c.fPool.add(ifcall.Fid) if err != nil { r.err = ErrDupFid goto resp } switch { - case s.Auth == nil && ifcall.Afid == NOFID: - case s.Auth == nil && ifcall.Afid != NOFID: + case c.s.Auth == nil && ifcall.Afid == NOFID: + case c.s.Auth == nil && ifcall.Afid != NOFID: r.err = ErrBotch goto resp - case s.Auth != nil && ifcall.Afid == NOFID: + case c.s.Auth != nil && ifcall.Afid == NOFID: r.err = fmt.Errorf("authentication required") goto resp - case s.Auth != nil && ifcall.Afid != NOFID: - afid, ok := s.fPool.lookup(ifcall.Afid) + case c.s.Auth != nil && ifcall.Afid != NOFID: + afid, ok := c.fPool.lookup(ifcall.Afid) if !ok { r.err = ErrUnknownFid goto resp @@ -325,7 +329,7 @@ func sAttach(ctx context.Context, s *Server, c <-chan *request) { r.fid.omode = -1 r.fid.path = "." r.fid.uid = ifcall.Uname - st, err = fs.Stat(ExportFS{s.fs}, ".") + st, err = fs.Stat(ExportFS{c.s.fs}, ".") if err != nil { r.err = fmt.Errorf("stat root: %v", err) goto resp @@ -336,12 +340,12 @@ func sAttach(ctx context.Context, s *Server, c <-chan *request) { resp: if r.err != nil { if r.fid != nil { - s.fPool.delete(r.fid.fid) + c.fPool.delete(r.fid.fid) } setError(r, r.err) } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -349,12 +353,12 @@ func sAttach(ctx context.Context, s *Server, c <-chan *request) { } } -func sFlush(ctx context.Context, s *Server, c <-chan *request) { +func sFlush(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -363,7 +367,7 @@ func sFlush(ctx context.Context, s *Server, c <-chan *request) { } r.ofcall = &RFlush{} select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -371,12 +375,12 @@ func sFlush(ctx context.Context, s *Server, c <-chan *request) { } } -func sWalk(ctx context.Context, s *Server, c <-chan *request) { +func sWalk(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -389,7 +393,7 @@ func sWalk(ctx context.Context, s *Server, c <-chan *request) { n int ) ifcall := r.ifcall.(*TWalk) - oldFid, ok := s.fPool.lookup(ifcall.Fid) + oldFid, ok := c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp @@ -398,7 +402,7 @@ func sWalk(ctx context.Context, s *Server, c <-chan *request) { r.err = fmt.Errorf("cannot clone open fid") goto resp } - oldst, err = fs.Stat(ExportFS{s.fs}, oldFid.path) + oldst, err = fs.Stat(ExportFS{c.s.fs}, oldFid.path) if err != nil { r.err = fmt.Errorf("stat: %v", err) goto resp @@ -411,7 +415,7 @@ func sWalk(ctx context.Context, s *Server, c <-chan *request) { newFid = oldFid } else { var err error - newFid, err = s.fPool.add(ifcall.Newfid) + newFid, err = c.fPool.add(ifcall.Newfid) if err != nil { r.err = fmt.Errorf("alloc: %v", err) goto resp @@ -422,7 +426,7 @@ func sWalk(ctx context.Context, s *Server, c <-chan *request) { // TODO: replace this block with fs.WalkDir. for i, name := range ifcall.Wnames { cwdp = path.Join(cwdp, name) - stat, err := fs.Stat(ExportFS{s.fs}, cwdp) + stat, err := fs.Stat(ExportFS{c.s.fs}, cwdp) if err != nil { break } @@ -441,13 +445,13 @@ func sWalk(ctx context.Context, s *Server, c <-chan *request) { panic("err and r.ofcall are both nil") } setError(r, r.err) - s.respChan <- r + c.respChan <- r continue } ofcall := r.ofcall.(*RWalk) if r.err != nil || len(ofcall.Qids) < len(ifcall.Wnames) { if ifcall.Fid != ifcall.Newfid { - s.fPool.delete(ifcall.Newfid) + c.fPool.delete(ifcall.Newfid) } if len(ofcall.Qids) == 0 { if r.err == nil && len(ifcall.Wnames) != 0 { @@ -456,7 +460,7 @@ func sWalk(ctx context.Context, s *Server, c <-chan *request) { } } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -464,12 +468,12 @@ func sWalk(ctx context.Context, s *Server, c <-chan *request) { } } -func sOpen(ctx context.Context, s *Server, c <-chan *request) { +func sOpen(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -480,7 +484,7 @@ func sOpen(ctx context.Context, s *Server, c <-chan *request) { st fs.FileInfo ) ifcall := r.ifcall.(*TOpen) - r.fid, ok = s.fPool.lookup(ifcall.Fid) + r.fid, ok = c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp @@ -490,7 +494,7 @@ func sOpen(ctx context.Context, s *Server, c <-chan *request) { goto resp } if afile, ok := r.fid.file.(*AuthFile); ok { - // s.Auth should set r.fid.file to a valid *AuthFile, + // c.s.Auth should set r.fid.file to a valid *AuthFile, // so r.fid.file should not be nil. st, err = r.fid.file.Stat() if err != nil { @@ -503,7 +507,7 @@ func sOpen(ctx context.Context, s *Server, c <-chan *request) { // See open(5). // In plan9 implementation, ifcall.Mode() is ANDed with ^ORCLOSE, // but ORCLOSE is also prohibitted by the protocol... - st, err = fs.Stat(ExportFS{s.fs}, r.fid.path) + st, err = fs.Stat(ExportFS{c.s.fs}, r.fid.path) if err != nil { r.err = fmt.Errorf("stat: %v", err) goto resp @@ -533,31 +537,31 @@ func sOpen(ctx context.Context, s *Server, c <-chan *request) { r.err = ErrPerm goto resp } - if !hasPerm(s.fs, st, r.fid.uid, p) { + if !hasPerm(c.s.fs, st, r.fid.uid, p) { r.err = ErrPerm goto resp } if ifcall.Mode&ORCLOSE != 0 { parentPath := path.Dir(r.fid.path) - st, err := fs.Stat(ExportFS{s.fs}, parentPath) + st, err := fs.Stat(ExportFS{c.s.fs}, parentPath) if err != nil { r.err = fmt.Errorf("stat parent: %v", err) goto resp } - if !hasPerm(s.fs, st, r.fid.uid, AWRITE) { + if !hasPerm(c.s.fs, st, r.fid.uid, AWRITE) { r.err = ErrPerm goto resp } } r.ofcall = &ROpen{ Qid: qid, - Iounit: s.mSize() - IOHDRSZ, + Iounit: c.mSize() - IOHDRSZ, } resp: if r.err != nil { setError(r, r.err) select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -566,17 +570,17 @@ func sOpen(ctx context.Context, s *Server, c <-chan *request) { r.fid.omode = r.ifcall.(*TOpen).Mode if _, ok := r.fid.file.(*AuthFile); ok { select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } continue } - f, err := s.fs.OpenFile(r.fid.path, r.fid.omode) + f, err := c.s.fs.OpenFile(r.fid.path, r.fid.omode) if err != nil { setError(r, err) select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -584,7 +588,7 @@ func sOpen(ctx context.Context, s *Server, c <-chan *request) { } r.fid.file = f select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -592,12 +596,12 @@ func sOpen(ctx context.Context, s *Server, c <-chan *request) { } } -func sCreate(ctx context.Context, s *Server, c <-chan *request) { +func sCreate(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -612,12 +616,12 @@ func sCreate(ctx context.Context, s *Server, c <-chan *request) { dirperm FileMode ) ifcall := r.ifcall.(*TCreate) - r.fid, ok = s.fPool.lookup(ifcall.Fid) + r.fid, ok = c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp } - dirstat, err = fs.Stat(ExportFS{s.fs}, r.fid.path) + dirstat, err = fs.Stat(ExportFS{c.s.fs}, r.fid.path) if err != nil { r.err = fmt.Errorf("stat: %v", err) goto resp @@ -626,11 +630,11 @@ func sCreate(ctx context.Context, s *Server, c <-chan *request) { r.err = fmt.Errorf("create in non-dir") goto resp } - if !hasPerm(s.fs, dirstat, r.fid.uid, AWRITE) { + if !hasPerm(c.s.fs, dirstat, r.fid.uid, AWRITE) { r.err = ErrPerm goto resp } - cfs, ok = s.fs.(CreaterFS) + cfs, ok = c.s.fs.(CreaterFS) if !ok { r.err = ErrOperation goto resp @@ -658,14 +662,14 @@ func sCreate(ctx context.Context, s *Server, c <-chan *request) { } r.ofcall = &RCreate{ Qid: st.Sys().(*Stat).Qid, - Iounit: s.mSize() - IOHDRSZ, + Iounit: c.mSize() - IOHDRSZ, } resp: if r.err != nil { setError(r, r.err) } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -675,12 +679,12 @@ func sCreate(ctx context.Context, s *Server, c <-chan *request) { // TODO: I think the file should be locked while reading. // or should the undeterminism left for the client side? -func sRead(ctx context.Context, s *Server, c <-chan *request) { +func sRead(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: var ( fi fs.FileInfo err error @@ -692,7 +696,7 @@ func sRead(ctx context.Context, s *Server, c <-chan *request) { return } ifcall := r.ifcall.(*TRead) - r.fid, ok = s.fPool.lookup(ifcall.Fid) + r.fid, ok = c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp @@ -719,7 +723,7 @@ func sRead(ctx context.Context, s *Server, c <-chan *request) { errc <- fmt.Errorf("invalid dir offset") return } - children, err := fs.Glob(ExportFS{s.fs}, path.Join(r.fid.path, "*")) + children, err := fs.Glob(ExportFS{c.s.fs}, path.Join(r.fid.path, "*")) if err != nil { errc <- fmt.Errorf("glob children: %v", err) return @@ -730,7 +734,7 @@ func sRead(ctx context.Context, s *Server, c <-chan *request) { } k := r.fid.dirIndex for ; k < len(children); k++ { - fi, err := fs.Stat(ExportFS{s.fs}, children[k]) + fi, err := fs.Stat(ExportFS{c.s.fs}, children[k]) if err != nil { log.Printf("stat: %v", err) continue @@ -780,7 +784,7 @@ func sRead(ctx context.Context, s *Server, c <-chan *request) { setError(r, r.err) } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -789,12 +793,12 @@ func sRead(ctx context.Context, s *Server, c <-chan *request) { } // TODO: I think the file should be locked while writing. -func sWrite(ctx context.Context, s *Server, c <-chan *request) { +func sWrite(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -804,13 +808,13 @@ func sWrite(ctx context.Context, s *Server, c <-chan *request) { omode OpenMode ) ifcall := r.ifcall.(*TWrite) - r.fid, ok = s.fPool.lookup(ifcall.Fid) + r.fid, ok = c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp } - if ifcall.Count > s.mSize()-IOHDRSZ { - ifcall.Count = s.mSize() - IOHDRSZ + if ifcall.Count > c.mSize()-IOHDRSZ { + ifcall.Count = c.mSize() - IOHDRSZ } omode = r.fid.omode & 3 if omode != OWRITE && omode != ORDWR { @@ -858,7 +862,7 @@ func sWrite(ctx context.Context, s *Server, c <-chan *request) { setError(r, r.err) } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -866,22 +870,22 @@ func sWrite(ctx context.Context, s *Server, c <-chan *request) { } } -func sClunk(ctx context.Context, s *Server, c <-chan *request) { +func sClunk(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } ifcall := r.ifcall.(*TClunk) - fid, ok := s.fPool.lookup(ifcall.Fid) + fid, ok := c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp } - s.fPool.delete(ifcall.Fid) + c.fPool.delete(ifcall.Fid) if fid.omode != -1 { if err := fid.file.Close(); err != nil { r.err = fmt.Errorf("close: %v", err) @@ -895,7 +899,7 @@ func sClunk(ctx context.Context, s *Server, c <-chan *request) { r.ofcall = &RClunk{} } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -903,12 +907,12 @@ func sClunk(ctx context.Context, s *Server, c <-chan *request) { } } -func sRemove(ctx context.Context, s *Server, c <-chan *request) { +func sRemove(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -919,26 +923,26 @@ func sRemove(ctx context.Context, s *Server, c <-chan *request) { rfs RemoverFS ) ifcall := r.ifcall.(*TRemove) - r.fid, ok = s.fPool.lookup(ifcall.Fid) + r.fid, ok = c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp } - s.fPool.delete(ifcall.Fid) + c.fPool.delete(ifcall.Fid) if r.fid.omode != -1 { r.fid.file.Close() } parentPath = path.Dir(r.fid.path) - pstat, err = fs.Stat(ExportFS{s.fs}, parentPath) + pstat, err = fs.Stat(ExportFS{c.s.fs}, parentPath) if err != nil { r.err = fmt.Errorf("stat parent: %v", err) goto resp } - if !hasPerm(s.fs, pstat, r.fid.uid, AWRITE) { + if !hasPerm(c.s.fs, pstat, r.fid.uid, AWRITE) { r.err = ErrPerm goto resp } - rfs, ok = s.fs.(RemoverFS) + rfs, ok = c.s.fs.(RemoverFS) if !ok { r.err = ErrOperation goto resp @@ -953,7 +957,7 @@ func sRemove(ctx context.Context, s *Server, c <-chan *request) { setError(r, r.err) } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -961,12 +965,12 @@ func sRemove(ctx context.Context, s *Server, c <-chan *request) { } } -func sStat(ctx context.Context, s *Server, c <-chan *request) { +func sStat(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -975,12 +979,12 @@ func sStat(ctx context.Context, s *Server, c <-chan *request) { err error ) ifcall := r.ifcall.(*TStat) - r.fid, ok = s.fPool.lookup(ifcall.Fid) + r.fid, ok = c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp } - fi, err = fs.Stat(ExportFS{s.fs}, r.fid.path) + fi, err = fs.Stat(ExportFS{c.s.fs}, r.fid.path) if err != nil { r.err = fmt.Errorf("stat: %v", err) goto resp @@ -993,7 +997,7 @@ func sStat(ctx context.Context, s *Server, c <-chan *request) { setError(r, r.err) } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -1001,12 +1005,12 @@ func sStat(ctx context.Context, s *Server, c <-chan *request) { } } -func sWStat(ctx context.Context, s *Server, c <-chan *request) { +func sWStat(ctx context.Context, c *conn, rc <-chan *request) { for { select { case <-ctx.Done(): return - case r, ok := <-c: + case r, ok := <-rc: if !ok { return } @@ -1018,14 +1022,14 @@ func sWStat(ctx context.Context, s *Server, c <-chan *request) { err error ) ifcall := r.ifcall.(*TWStat) - r.fid, ok = s.fPool.lookup(ifcall.Fid) + r.fid, ok = c.fPool.lookup(ifcall.Fid) if !ok { r.err = ErrUnknownFid goto resp } if r.fid.omode == -1 { var err error - r.fid.file, err = s.fs.OpenFile(r.fid.path, OREAD) + r.fid.file, err = c.s.fs.OpenFile(r.fid.path, OREAD) if err != nil { r.err = fmt.Errorf("open: %v", err) goto resp @@ -1057,12 +1061,12 @@ func sWStat(ctx context.Context, s *Server, c <-chan *request) { } if wstat.Name != "" && newStat.Name != wstat.Name { parentPath := path.Dir(r.fid.path) - pstat, err := fs.Stat(ExportFS{s.fs}, parentPath) + pstat, err := fs.Stat(ExportFS{c.s.fs}, parentPath) if err != nil { r.err = fmt.Errorf("stat parent: %v", err) goto resp } - if !hasPerm(s.fs, pstat, r.fid.uid, AWRITE) { + if !hasPerm(c.s.fs, pstat, r.fid.uid, AWRITE) { r.err = ErrPerm goto resp } @@ -1072,7 +1076,7 @@ func sWStat(ctx context.Context, s *Server, c <-chan *request) { // an existing file. // but 9pfs, 9pfuse does the rename when used with `git init`. /* - children, err := fs.Glob(ExportFS{s.fs}, path.Join(parentPath, "*")) + children, err := fs.Glob(ExportFS{c.s.fs}, path.Join(parentPath, "*")) if err != nil { r.err = fmt.Errorf("glob children: %v", err) goto resp @@ -1087,14 +1091,14 @@ func sWStat(ctx context.Context, s *Server, c <-chan *request) { newStat.Name = wstat.Name } if wstat.Length != ^int64(0) && wstat.Length != newStat.Length { - if fi.IsDir() || !hasPerm(s.fs, fi, r.fid.uid, AWRITE) { + if fi.IsDir() || !hasPerm(c.s.fs, fi, r.fid.uid, AWRITE) { r.err = ErrPerm goto resp } newStat.Length = wstat.Length } if wstat.Mode != FileMode(^uint32(0)) && wstat.Mode != newStat.Mode { - // the owner of the file or the group leader of the file's group. + // the owner of the file or the group leader of the file'c group. if r.fid.uid != newStat.Uid && r.fid.uid != newStat.Gid { r.err = ErrPerm goto resp @@ -1106,7 +1110,7 @@ func sWStat(ctx context.Context, s *Server, c <-chan *request) { newStat.Mode = wstat.Mode } if wstat.Mtime != ^uint32(0) && wstat.Mtime != newStat.Mtime { - // the owner of the file or the group leader of the file's group. + // the owner of the file or the group leader of the file'c group. if r.fid.uid != newStat.Uid && r.fid.uid != newStat.Gid { r.err = ErrPerm goto resp @@ -1115,11 +1119,11 @@ func sWStat(ctx context.Context, s *Server, c <-chan *request) { } if wstat.Gid != "" && wstat.Gid != newStat.Gid { // by the owner if also a member of the new group; - // or by the group leader of the file's current group if + // or by the group leader of the file'c current group if // also the leader of the new group. - if r.fid.uid == newStat.Uid && s.fs.IsGroupMember(wstat.Gid, r.fid.uid) || - s.fs.IsGroupLeader(newStat.Gid, r.fid.uid) && - s.fs.IsGroupLeader(wstat.Gid, r.fid.uid) { + if r.fid.uid == newStat.Uid && c.s.fs.IsGroupMember(wstat.Gid, r.fid.uid) || + c.s.fs.IsGroupLeader(newStat.Gid, r.fid.uid) && + c.s.fs.IsGroupLeader(wstat.Gid, r.fid.uid) { newStat.Gid = wstat.Gid } else { r.err = ErrPerm @@ -1137,7 +1141,7 @@ func sWStat(ctx context.Context, s *Server, c <-chan *request) { setError(r, r.err) } select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return } @@ -1151,10 +1155,11 @@ func sWStat(ctx context.Context, s *Server, c <-chan *request) { // the same file concurrentry. // So some additional struct to represent each connection is needed. // And Serve method should be attached to that struct. -func (s *Server) Serve(ctx context.Context) { +func (s *Server) Serve(ctx context.Context, r io.Reader, w io.Writer) { rp := newReqPool() - s.runListener(ctx, rp) - s.runResponder(ctx, rp) + c := newConn(r, w) + c.runListener(ctx, rp) + c.runResponder(ctx, rp) var ( versionChan = make(chan *request) authChan = make(chan *request) @@ -1185,25 +1190,25 @@ func (s *Server) Serve(ctx context.Context) { close(statChan) close(wstatChan) }() - go sVersion(ctx, s, versionChan) - go sAuth(ctx, s, authChan) - go sAttach(ctx, s, attachChan) - go sFlush(ctx, s, flushChan) - go sWalk(ctx, s, walkChan) - go sOpen(ctx, s, openChan) - go sCreate(ctx, s, createChan) - go sRead(ctx, s, readChan) - go sWrite(ctx, s, writeChan) - go sClunk(ctx, s, clunkChan) - go sRemove(ctx, s, removeChan) - go sStat(ctx, s, statChan) - go sWStat(ctx, s, wstatChan) + go sVersion(ctx, c, versionChan) + go sAuth(ctx, c, authChan) + go sAttach(ctx, c, attachChan) + go sFlush(ctx, c, flushChan) + go sWalk(ctx, c, walkChan) + go sOpen(ctx, c, openChan) + go sCreate(ctx, c, createChan) + go sRead(ctx, c, readChan) + go sWrite(ctx, c, writeChan) + go sClunk(ctx, c, clunkChan) + go sRemove(ctx, c, removeChan) + go sStat(ctx, c, statChan) + go sWStat(ctx, c, wstatChan) L: for { select { case <-ctx.Done(): break L - case r, ok := <-s.listenChan: + case r, ok := <-c.listenChan: if !ok { break L } @@ -1218,7 +1223,7 @@ L: default: setError(r, fmt.Errorf("unknown message type: %d", r.ifcall.Type())) select { - case s.respChan <- r: + case c.respChan <- r: case <-ctx.Done(): return }