commit bc92ce34656ccfb2ae4ca7a2b1c440f690578268
parent e7f453a45c687591ceb0b4a6f975966d074cd297
Author: Matsuda Kenji <info@mtkn.jp>
Date: Fri, 22 Dec 2023 11:56:46 +0900
all pipeline
Diffstat:
| M | server.go | | | 1264 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
1 file changed, 758 insertions(+), 506 deletions(-)
diff --git a/server.go b/server.go
@@ -459,7 +459,7 @@ func sWalk(ctx context.Context, s *Server, c <-chan *Req) {
var err error
newFid, err = s.fPool.add(ifcall.Newfid)
if err != nil {
- r.err = fmt.Errorf("alloc: $v", err)
+ r.err = fmt.Errorf("alloc: %v", err)
rc <- r
continue
}
@@ -522,542 +522,782 @@ func rWalk(ctx context.Context, c <-chan *Req) {
}
}
-func sOpen(ctx context.Context, s *Server, r *Req) {
- ifcall := r.Ifcall.(*TOpen)
- var ok bool
- r.Fid, ok = s.fPool.lookup(ifcall.Fid)
- if !ok {
- Respond(ctx, r, ErrUnknownFid)
- return
- }
- if r.Fid.OMode != -1 {
- Respond(ctx, r, ErrBotch)
- return
- }
- var (
- err error
- qid Qid
- st fs.FileInfo
- )
- if afile, ok := r.Fid.File.(*AuthFile); ok {
- // 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 {
- Respond(ctx, r, fmt.Errorf("stat: %v", err))
+func sOpen(ctx context.Context, s *Server, c <-chan *Req) {
+ rc := make(chan *Req)
+ defer close(rc)
+ go rOpen(ctx, rc)
+ for {
+ select {
+ case <-ctx.Done():
return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ ifcall := r.Ifcall.(*TOpen)
+ var ok bool
+ r.Fid, ok = s.fPool.lookup(ifcall.Fid)
+ if !ok {
+ r.err = ErrUnknownFid
+ rc <- r
+ continue
+ }
+ if r.Fid.OMode != -1 {
+ r.err = ErrBotch
+ rc <- r
+ continue
+ }
+ var (
+ err error
+ qid Qid
+ st fs.FileInfo
+ )
+ if afile, ok := r.Fid.File.(*AuthFile); ok {
+ // 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 {
+ r.err = fmt.Errorf("stat: %v", err)
+ rc <- r
+ continue
+ }
+ qid = afile.Qid
+ } else {
+ // Write attempt to a directory is prohibitted by the protocol.
+ // 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)
+ if err != nil {
+ r.err = fmt.Errorf("stat: %v", err)
+ rc <- r
+ continue
+ }
+ qid = st.Sys().(*Stat).Qid
+ }
+ if qid.Type == QTDIR && ifcall.Mode != OREAD {
+ r.err = fmt.Errorf("is a directory")
+ rc <- r
+ continue
+ }
+ var p fs.FileMode
+ switch ifcall.Mode & 3 {
+ default:
+ panic("invalid mode")
+ case OREAD:
+ p = AREAD
+ case OWRITE:
+ p = AWRITE
+ case ORDWR:
+ p = AREAD | AWRITE
+ case OEXEC:
+ p = AEXEC
+ }
+ if ifcall.Mode&OTRUNC != 0 {
+ p |= AWRITE
+ }
+ if qid.Type&QTDIR != 0 && p != AREAD {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ if !hasPerm(s.fs, st, r.Fid.Uid, p) {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ if ifcall.Mode&ORCLOSE != 0 {
+ parentPath := path.Dir(r.Fid.path)
+ st, err := fs.Stat(ExportFS{s.fs}, parentPath)
+ if err != nil {
+ r.err = fmt.Errorf("stat parent: %v", err)
+ rc <- r
+ continue
+ }
+ if !hasPerm(s.fs, st, r.Fid.Uid, AWRITE) {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ }
+ r.Ofcall = &ROpen{
+ Qid: qid,
+ Iounit: s.mSize() - IOHDRSZ,
+ }
+ rc <- r
}
- qid = afile.Qid
- } else {
- // Write attempt to a directory is prohibitted by the protocol.
- // 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)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("stat: %v", err))
+ }
+}
+
+func rOpen(ctx context.Context, c <-chan *Req) {
+ for {
+ select {
+ case <-ctx.Done():
return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ if r.err != nil {
+ setError(r, r.err)
+ r.Srv.respChan <- r
+ continue
+ }
+ r.Fid.OMode = r.Ifcall.(*TOpen).Mode
+ if _, ok := r.Fid.File.(*AuthFile); ok {
+ r.Srv.respChan <- r
+ continue
+ }
+ f, err := r.Srv.fs.OpenFile(r.Fid.path, r.Fid.OMode)
+ if err != nil {
+ setError(r, err)
+ r.Srv.respChan <- r
+ continue
+ }
+ r.Fid.File = f
+ r.Srv.respChan <- r
}
- qid = st.Sys().(*Stat).Qid
- }
- if qid.Type == QTDIR && ifcall.Mode != OREAD {
- Respond(ctx, r, fmt.Errorf("is a directory"))
- return
- }
- var p fs.FileMode
- switch ifcall.Mode & 3 {
- default:
- panic("invalid mode")
- case OREAD:
- p = AREAD
- case OWRITE:
- p = AWRITE
- case ORDWR:
- p = AREAD | AWRITE
- case OEXEC:
- p = AEXEC
- }
- if ifcall.Mode&OTRUNC != 0 {
- p |= AWRITE
}
- if qid.Type&QTDIR != 0 && p != AREAD {
- Respond(ctx, r, ErrPerm)
- return
- }
- if !hasPerm(s.fs, st, r.Fid.Uid, p) {
- Respond(ctx, r, ErrPerm)
- return
- }
- if ifcall.Mode&ORCLOSE != 0 {
- parentPath := path.Dir(r.Fid.path)
- st, err := fs.Stat(ExportFS{s.fs}, parentPath)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("stat parent: %v", err))
+}
+
+func sCreate(ctx context.Context, s *Server, c <-chan *Req) {
+ rc := make(chan *Req)
+ defer close(rc)
+ go rCreate(ctx, rc)
+ for {
+ select {
+ case <-ctx.Done():
return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ ifcall := r.Ifcall.(*TCreate)
+ var ok bool
+ r.Fid, ok = s.fPool.lookup(ifcall.Fid)
+ if !ok {
+ r.err = ErrUnknownFid
+ rc <- r
+ continue
+ }
+ dirstat, err := fs.Stat(ExportFS{s.fs}, r.Fid.path)
+ if err != nil {
+ r.err = fmt.Errorf("stat: %v", err)
+ rc <- r
+ continue
+ }
+ if !dirstat.IsDir() {
+ r.err = fmt.Errorf("create in non-dir")
+ rc <- r
+ continue
+ }
+ if !hasPerm(s.fs, dirstat, r.Fid.Uid, AWRITE) {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ cfs, ok := s.fs.(CreaterFS)
+ if !ok {
+ r.err = ErrOperation
+ rc <- r
+ continue
+ }
+ perm := ifcall.Perm
+ dirperm := dirstat.Mode()
+ if perm&fs.ModeDir == 0 {
+ perm &= ^FileMode(0666) | (dirperm & FileMode(0666))
+ } else {
+ perm &= ^FileMode(0777) | (dirperm & FileMode(0777))
+ }
+ cpath := path.Join(r.Fid.path, ifcall.Name)
+ file, err := cfs.Create(cpath, r.Fid.Uid, ifcall.Mode, perm)
+ if err != nil {
+ r.err = fmt.Errorf("create: %v", err)
+ rc <- r
+ continue
+ }
+ r.Fid.File = file
+ r.Fid.path = cpath
+ r.Fid.OMode = ifcall.Mode
+ st, err := r.Fid.File.Stat()
+ if err != nil {
+ r.err = fmt.Errorf("stat: %v", err)
+ rc <- r
+ continue
+ }
+ r.Ofcall = &RCreate{
+ Qid: st.Sys().(*Stat).Qid,
+ Iounit: s.mSize() - IOHDRSZ,
+ }
+ rc <- r
}
- if !hasPerm(s.fs, st, r.Fid.Uid, AWRITE) {
- Respond(ctx, r, ErrPerm)
+ }
+}
+
+func rCreate(ctx context.Context, c <-chan *Req) {
+ for {
+ select {
+ case <-ctx.Done():
return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ if r.err != nil {
+ setError(r, r.err)
+ }
+ r.Srv.respChan <- r
}
}
- r.Ofcall = &ROpen{
- Qid: qid,
- Iounit: s.mSize() - IOHDRSZ,
- }
- Respond(ctx, r, nil)
}
-func rOpen(r *Req, err error) {
- if err != nil {
- setError(r, err)
- return
- }
- r.Fid.OMode = r.Ifcall.(*TOpen).Mode
- if _, ok := r.Fid.File.(*AuthFile); ok {
- return
- }
- f, err := r.Srv.fs.OpenFile(r.Fid.path, r.Fid.OMode)
- if err != nil {
- setError(r, err)
- return
+// TODO: I think the file should be locked while reading.
+func sRead(ctx context.Context, s *Server, c <-chan *Req) {
+ rc := make(chan *Req)
+ defer close(rc)
+ go rRead(ctx, rc)
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ ifcall := r.Ifcall.(*TRead)
+ var ok bool
+ r.Fid, ok = s.fPool.lookup(ifcall.Fid)
+ if !ok {
+ r.err = ErrUnknownFid
+ rc <- r
+ continue
+ }
+ if r.Fid.OMode == -1 {
+ r.err = fmt.Errorf("not open")
+ rc <- r
+ continue
+ }
+ if r.Fid.OMode != OREAD && r.Fid.OMode != ORDWR && r.Fid.OMode != OEXEC {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ fi, err := r.Fid.File.Stat()
+ if err != nil {
+ r.err = fmt.Errorf("stat: %v", err)
+ rc <- r
+ continue
+ }
+ data := make([]byte, ifcall.Count)
+ errc := make(chan error)
+ var n int
+ go func() {
+ defer close(errc)
+ if fi.IsDir() {
+ if ifcall.Offset != 0 && ifcall.Offset != r.Fid.dirOffset {
+ errc <- fmt.Errorf("invalid dir offset")
+ return
+ }
+ children, err := fs.Glob(ExportFS{s.fs}, path.Join(r.Fid.path, "*"))
+ if err != nil {
+ errc <- fmt.Errorf("glob children: %v", err)
+ return
+ }
+ if ifcall.Offset == 0 {
+ r.Fid.dirIndex = 0
+ r.Fid.dirOffset = 0
+ }
+ k := r.Fid.dirIndex
+ for ; k < len(children); k++ {
+ fi, err := fs.Stat(ExportFS{s.fs}, children[k])
+ if err != nil {
+ log.Printf("stat: %v", err)
+ continue
+ }
+ st := fi.Sys().(*Stat)
+ buf := st.marshal()
+ if n+len(buf) > len(data) {
+ break
+ }
+ for i := 0; i < len(buf); i++ {
+ data[n+i] = buf[i]
+ }
+ n += len(buf)
+ }
+ r.Fid.dirOffset += uint64(n)
+ r.Fid.dirIndex = k
+ } else {
+ var err error
+ if reader, ok := r.Fid.File.(io.ReaderAt); ok {
+ n, err = reader.ReadAt(data, int64(ifcall.Offset))
+ } else {
+ n, err = r.Fid.File.Read(data)
+ }
+ if err != io.EOF && err != nil {
+ errc <- err
+ return
+ }
+ }
+ }()
+ select {
+ case err := <-errc:
+ if err != nil {
+ r.err = err
+ rc <- r
+ continue
+ }
+ case <-ctx.Done():
+ return
+ }
+ r.Ofcall = &RRead{
+ Count: uint32(n),
+ Data: data[:n],
+ }
+ rc <- r
+ }
}
- r.Fid.File = f
}
-func sCreate(ctx context.Context, s *Server, r *Req) {
- ifcall := r.Ifcall.(*TCreate)
- var ok bool
- r.Fid, ok = s.fPool.lookup(ifcall.Fid)
- if !ok {
- Respond(ctx, r, ErrUnknownFid)
- return
- }
- dirstat, err := fs.Stat(ExportFS{s.fs}, r.Fid.path)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("stat: %v", err))
- return
- }
- if !dirstat.IsDir() {
- Respond(ctx, r, fmt.Errorf("create in non-dir"))
- return
- }
- if !hasPerm(s.fs, dirstat, r.Fid.Uid, AWRITE) {
- Respond(ctx, r, ErrPerm)
- return
- }
- cfs, ok := s.fs.(CreaterFS)
- if !ok {
- Respond(ctx, r, ErrOperation)
- return
- }
- perm := ifcall.Perm
- dirperm := dirstat.Mode()
- if perm&fs.ModeDir == 0 {
- perm &= ^FileMode(0666) | (dirperm & FileMode(0666))
- } else {
- perm &= ^FileMode(0777) | (dirperm & FileMode(0777))
+func rRead(ctx context.Context, c <-chan *Req) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ if r.err != nil {
+ setError(r, r.err)
+ }
+ r.Srv.respChan <- r
+ }
}
- cpath := path.Join(r.Fid.path, ifcall.Name)
- file, err := cfs.Create(cpath, r.Fid.Uid, ifcall.Mode, perm)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("create: %v", err))
- return
+}
+
+// TODO: I think the file should be locked while reading.
+func sWrite(ctx context.Context, s *Server, c <-chan *Req) {
+ rc := make(chan *Req)
+ defer close(rc)
+ go rWrite(ctx, rc)
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ ifcall := r.Ifcall.(*TWrite)
+ var ok bool
+ r.Fid, ok = s.fPool.lookup(ifcall.Fid)
+ if !ok {
+ r.err = ErrUnknownFid
+ rc <- r
+ continue
+ }
+ if ifcall.Count > s.mSize()-IOHDRSZ {
+ ifcall.Count = s.mSize() - IOHDRSZ
+ }
+ omode := r.Fid.OMode & 3
+ if omode != OWRITE && omode != ORDWR {
+ r.err = fmt.Errorf("write on fid with open mode 0x%x", r.Fid.OMode)
+ rc <- r
+ continue
+ }
+ ofcall := new(RWrite)
+ errc := make(chan error)
+ go func() {
+ defer close(errc)
+ switch file := r.Fid.File.(type) {
+ case io.WriterAt:
+ n, err := file.WriteAt(ifcall.Data, int64(ifcall.Offset))
+ if err != nil {
+ errc <- fmt.Errorf("write: %v", err)
+ return
+ }
+ ofcall.Count = uint32(n)
+ case io.Writer:
+ n, err := file.Write(ifcall.Data)
+ if err != nil {
+ errc <- fmt.Errorf("write: %v", err)
+ return
+ }
+ ofcall.Count = uint32(n)
+ default:
+ errc <- ErrOperation
+ return
+ }
+ }()
+ select {
+ case err := <-errc:
+ if err != nil {
+ r.err = err
+ rc <- r
+ continue
+ }
+ case <-ctx.Done():
+ return
+ }
+ r.Ofcall = ofcall
+ rc <- r
+ }
}
- r.Fid.File = file
- r.Fid.path = cpath
- r.Fid.OMode = ifcall.Mode
- st, err := r.Fid.File.Stat()
- if err != nil {
- Respond(ctx, r, fmt.Errorf("stat: %v", err))
- return
+}
+
+func rWrite(ctx context.Context, c <-chan *Req) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ if r.err != nil {
+ setError(r, r.err)
+ }
+ r.Srv.respChan <- r
+ }
}
- r.Ofcall = &RCreate{
- Qid: st.Sys().(*Stat).Qid,
- Iounit: s.mSize() - IOHDRSZ,
+}
+
+func sClunk(ctx context.Context, s *Server, c <-chan *Req) {
+ rc := make(chan *Req)
+ defer close(rc)
+ go rClunk(ctx, rc)
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ ifcall := r.Ifcall.(*TClunk)
+ fid, ok := s.fPool.lookup(ifcall.Fid)
+ if !ok {
+ r.err = ErrUnknownFid
+ rc <- r
+ continue
+ }
+ s.fPool.delete(ifcall.Fid)
+ if fid.OMode != -1 {
+ if err := fid.File.Close(); err != nil {
+ r.err = fmt.Errorf("close: %v", err)
+ rc <- r
+ continue
+ }
+ }
+ r.Ofcall = &RClunk{}
+ rc <- r
+ }
}
- Respond(ctx, r, nil)
}
-func rCreate(r *Req, err error) {
- if err != nil {
- setError(r, err)
- return
+func rClunk(ctx context.Context, c <-chan *Req) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ if r.err != nil {
+ log.Printf("clunk: %v", r.err)
+ r.Ofcall = &RClunk{}
+ }
+ r.Srv.respChan <- r
+ }
}
}
-// TODO: I think the file should be locked while reading.
-func sRead(ctx context.Context, s *Server, r *Req) {
- ifcall := r.Ifcall.(*TRead)
- var ok bool
- r.Fid, ok = s.fPool.lookup(ifcall.Fid)
- if !ok {
- Respond(ctx, r, ErrUnknownFid)
- return
- }
- if r.Fid.OMode == -1 {
- Respond(ctx, r, fmt.Errorf("not open"))
- return
- }
- if r.Fid.OMode != OREAD && r.Fid.OMode != ORDWR && r.Fid.OMode != OEXEC {
- Respond(ctx, r, ErrPerm)
- return
- }
- fi, err := r.Fid.File.Stat()
- if err != nil {
- log.Printf("Stat: %v", err)
- Respond(ctx, r, fmt.Errorf("internal error"))
- return
- }
- data := make([]byte, ifcall.Count)
- errc := make(chan error)
- var n int
- go func() {
- defer close(errc)
- if fi.IsDir() {
- if ifcall.Offset != 0 && ifcall.Offset != r.Fid.dirOffset {
- errc <- fmt.Errorf("invalid dir offset")
+func sRemove(ctx context.Context, s *Server, c <-chan *Req) {
+ rc := make(chan *Req)
+ defer close(rc)
+ go rRemove(ctx, rc)
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
return
}
- children, err := fs.Glob(ExportFS{s.fs}, path.Join(r.Fid.path, "*"))
- if err != nil {
- errc <- fmt.Errorf("glob children: %v", err)
- return
+ ifcall := r.Ifcall.(*TRemove)
+ var ok bool
+ r.Fid, ok = s.fPool.lookup(ifcall.Fid)
+ if !ok {
+ r.err = ErrUnknownFid
+ rc <- r
+ continue
}
- if ifcall.Offset == 0 {
- r.Fid.dirIndex = 0
- r.Fid.dirOffset = 0
+ s.fPool.delete(ifcall.Fid)
+ if r.Fid.OMode != -1 {
+ r.Fid.File.Close()
}
- k := r.Fid.dirIndex
- for ; k < len(children); k++ {
- fi, err := fs.Stat(ExportFS{s.fs}, children[k])
- if err != nil {
- log.Printf("stat: %v", err)
- continue
- }
- st := fi.Sys().(*Stat)
- buf := st.marshal()
- if n+len(buf) > len(data) {
- break
- }
- for i := 0; i < len(buf); i++ {
- data[n+i] = buf[i]
- }
- n += len(buf)
+ parentPath := path.Dir(r.Fid.path)
+ pstat, err := fs.Stat(ExportFS{s.fs}, parentPath)
+ if err != nil {
+ r.err = fmt.Errorf("stat parent: %v", err)
+ rc <- r
+ continue
}
- r.Fid.dirOffset += uint64(n)
- r.Fid.dirIndex = k
- } else {
- var err error
- if reader, ok := r.Fid.File.(io.ReaderAt); ok {
- n, err = reader.ReadAt(data, int64(ifcall.Offset))
- } else {
- n, err = r.Fid.File.Read(data)
+ if !hasPerm(s.fs, pstat, r.Fid.Uid, AWRITE) {
+ r.err = ErrPerm
+ rc <- r
+ continue
}
- if err != io.EOF && err != nil {
- errc <- err
- return
+ rfs, ok := s.fs.(RemoverFS)
+ if !ok {
+ r.err = ErrOperation
+ rc <- r
+ continue
}
+ if err := rfs.Remove(r.Fid.path); err != nil {
+ r.err = fmt.Errorf("remove: %v", err)
+ rc <- r
+ continue
+ }
+ r.Ofcall = &RRemove{}
+ rc <- r
}
- }()
- select {
- case err := <-errc:
- if err != nil {
- Respond(ctx, r, err)
- return
- }
- case <-ctx.Done():
- return
- }
- r.Ofcall = &RRead{
- Count: uint32(n),
- Data: data[:n],
}
- Respond(ctx, r, nil)
}
-func rRead(r *Req, err error) {
- if err != nil {
- setError(r, err)
+func rRemove(ctx context.Context, c <-chan *Req) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ if r.err != nil {
+ setError(r, r.err)
+ }
+ r.Srv.respChan <- r
+ }
}
}
-// TODO: I think the file should be locked while reading.
-func sWrite(ctx context.Context, s *Server, r *Req) {
- ifcall := r.Ifcall.(*TWrite)
- var ok bool
- r.Fid, ok = s.fPool.lookup(ifcall.Fid)
- if !ok {
- Respond(ctx, r, ErrUnknownFid)
- return
- }
- if ifcall.Count > s.mSize()-IOHDRSZ {
- ifcall.Count = s.mSize() - IOHDRSZ
- }
- omode := r.Fid.OMode & 3
- if omode != OWRITE && omode != ORDWR {
- Respond(ctx, r, fmt.Errorf("write on fid with open mode 0x%x", r.Fid.OMode))
- return
- }
- ofcall := new(RWrite)
- errc := make(chan error)
- go func() {
- defer close(errc)
- switch file := r.Fid.File.(type) {
- case io.WriterAt:
- n, err := file.WriteAt(ifcall.Data, int64(ifcall.Offset))
- if err != nil {
- errc <- fmt.Errorf("write: %v", err)
+func sStat(ctx context.Context, s *Server, c <-chan *Req) {
+ rc := make(chan *Req)
+ defer close(rc)
+ go rStat(ctx, rc)
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
return
}
- ofcall.Count = uint32(n)
- case io.Writer:
- n, err := file.Write(ifcall.Data)
+ ifcall := r.Ifcall.(*TStat)
+ var ok bool
+ r.Fid, ok = s.fPool.lookup(ifcall.Fid)
+ if !ok {
+ r.err = ErrUnknownFid
+ rc <- r
+ continue
+ }
+ fi, err := fs.Stat(ExportFS{s.fs}, r.Fid.path)
if err != nil {
- errc <- fmt.Errorf("write: %v", err)
- return
+ r.err = fmt.Errorf("stat: %v", err)
+ rc <- r
+ continue
}
- ofcall.Count = uint32(n)
- default:
- errc <- ErrOperation
- return
- }
- }()
- select {
- case err := <-errc:
- if err != nil {
- Respond(ctx, r, err)
- return
+ r.Ofcall = &RStat{
+ Stat: fi.Sys().(*Stat),
+ }
+ rc <- r
}
- case <-ctx.Done():
- return
}
- r.Ofcall = ofcall
- Respond(ctx, r, nil)
}
-func rWrite(r *Req, err error) {
- if err != nil {
- setError(r, err)
- return
- }
-}
-
-func sClunk(ctx context.Context, s *Server, r *Req) {
- ifcall := r.Ifcall.(*TClunk)
- fid, ok := s.fPool.lookup(ifcall.Fid)
- if !ok {
- Respond(ctx, r, ErrUnknownFid)
- return
- }
- s.fPool.delete(ifcall.Fid)
- if fid.OMode != -1 {
- if err := fid.File.Close(); err != nil {
- Respond(ctx, r, fmt.Errorf("close: %v", err))
+func rStat(ctx context.Context, c <-chan *Req) {
+ for {
+ select {
+ case <-ctx.Done():
return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ if r.err != nil {
+ setError(r, r.err)
+ }
+ r.Srv.respChan <- r
}
}
- r.Ofcall = &RClunk{}
- Respond(ctx, r, nil)
-}
-
-func rClunk(r *Req, err error) {
- if err != nil {
- log.Printf("clunk: %v", err)
- r.Ofcall = &RClunk{}
- }
-}
-
-func sRemove(ctx context.Context, s *Server, r *Req) {
- ifcall := r.Ifcall.(*TRemove)
- var ok bool
- r.Fid, ok = s.fPool.lookup(ifcall.Fid)
- if !ok {
- Respond(ctx, r, ErrUnknownFid)
- return
- }
- s.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)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("stat parent: %v", err))
- return
- }
- if !hasPerm(s.fs, pstat, r.Fid.Uid, AWRITE) {
- Respond(ctx, r, ErrPerm)
- return
- }
- rfs, ok := s.fs.(RemoverFS)
- if !ok {
- Respond(ctx, r, ErrOperation)
- return
- }
- if err := rfs.Remove(r.Fid.path); err != nil {
- Respond(ctx, r, fmt.Errorf("remove: %v", err))
- return
- }
- r.Ofcall = &RRemove{}
- Respond(ctx, r, nil)
-}
-
-func rRemove(r *Req, err error) {
- if err != nil {
- setError(r, err)
- }
-}
-
-func sStat(ctx context.Context, s *Server, r *Req) {
- ifcall := r.Ifcall.(*TStat)
- var ok bool
- r.Fid, ok = s.fPool.lookup(ifcall.Fid)
- if !ok {
- Respond(ctx, r, ErrUnknownFid)
- return
- }
- fi, err := fs.Stat(ExportFS{s.fs}, r.Fid.path)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("stat: %v", err))
- return
- }
- r.Ofcall = &RStat{
- Stat: fi.Sys().(*Stat),
- }
- Respond(ctx, r, nil)
-}
-
-func rStat(r *Req, err error) {
- if err != nil {
- setError(r, err)
- }
}
-func sWStat(ctx context.Context, s *Server, r *Req) {
- ifcall := r.Ifcall.(*TWStat)
- var ok bool
- r.Fid, ok = s.fPool.lookup(ifcall.Fid)
- if !ok {
- Respond(ctx, r, ErrUnknownFid)
- return
- }
- if r.Fid.OMode == -1 {
- var err error
- r.Fid.File, err = s.fs.OpenFile(r.Fid.path, OREAD)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("open: %v", err))
- return
- }
- defer r.Fid.File.Close()
- }
- wsfile, ok := r.Fid.File.(WriterStatFile)
- if !ok {
- Respond(ctx, r, ErrOperation)
- return
- }
- wstat := ifcall.Stat
- fi, err := r.Fid.File.Stat()
- if err != nil {
- Respond(ctx, r, fmt.Errorf("stat: %v", err))
- return
- }
- newStat := fi.Sys().(*Stat)
- if wstat.Type != ^uint16(0) && wstat.Type != newStat.Type ||
- wstat.Dev != ^uint32(0) && wstat.Dev != newStat.Dev ||
- wstat.Qid.Type != QidType(^uint8(0)) && wstat.Qid.Type != newStat.Qid.Type ||
- wstat.Qid.Vers != ^uint32(0) && wstat.Qid.Vers != newStat.Qid.Vers ||
- wstat.Qid.Path != ^uint64(0) && wstat.Qid.Path != newStat.Qid.Path ||
- wstat.Atime != ^uint32(0) && wstat.Atime != newStat.Atime ||
- wstat.Uid != "" && wstat.Uid != newStat.Uid ||
- wstat.Muid != "" && wstat.Muid != newStat.Muid {
- Respond(ctx, r, fmt.Errorf("operation not permitted"))
- return
- }
- if wstat.Name != "" && newStat.Name != wstat.Name {
- parentPath := path.Dir(r.Fid.path)
- pstat, err := fs.Stat(ExportFS{s.fs}, parentPath)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("stat parent: %v", err))
- return
- }
- if !hasPerm(s.fs, pstat, r.Fid.Uid, AWRITE) {
- Respond(ctx, r, ErrPerm)
+func sWStat(ctx context.Context, s *Server, c <-chan *Req) {
+ rc := make(chan *Req)
+ defer close(rc)
+ go rWStat(ctx, rc)
+ for {
+ select {
+ case <-ctx.Done():
return
- }
- // TODO: I think 9P protocol prohibits renaming to existent file.
- // Wstat(9P) in p9p says:
- // it is an error to change the name to that of
- // an existing file.
- // but 9pfs, 9pfuse does the rename when used with `git init`.
- /*
- children, err := fs.Glob(ExportFS{s.fs}, path.Join(parentPath, "*"))
- if err != nil {
- Respond(ctx, r, fmt.Errorf("glob children: %v", err))
+ case r := <-c:
+ if r == nil {
return
}
- for _, f := range children {
- if path.Base(f) == wstat.Name {
- Respond(ctx, r, fmt.Errorf("file already exists"))
- return
+ ifcall := r.Ifcall.(*TWStat)
+ var ok bool
+ r.Fid, ok = s.fPool.lookup(ifcall.Fid)
+ if !ok {
+ r.err = ErrUnknownFid
+ rc <- r
+ continue
+ }
+ if r.Fid.OMode == -1 {
+ var err error
+ r.Fid.File, err = s.fs.OpenFile(r.Fid.path, OREAD)
+ if err != nil {
+ r.err = fmt.Errorf("open: %v", err)
+ rc <- r
+ continue
}
+ defer r.Fid.File.Close()
}
- */
- newStat.Name = wstat.Name
- }
- if wstat.Length != ^int64(0) && wstat.Length != newStat.Length {
- if fi.IsDir() || !hasPerm(s.fs, fi, r.Fid.Uid, AWRITE) {
- Respond(ctx, r, ErrPerm)
- return
- }
- 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.
- if r.Fid.Uid != newStat.Uid && r.Fid.Uid != newStat.Gid {
- Respond(ctx, r, ErrPerm)
- return
- }
- if wstat.Mode&fs.ModeDir != newStat.Mode&fs.ModeDir {
- Respond(ctx, r, ErrPerm)
- return
- }
- 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.
- if r.Fid.Uid != newStat.Uid && r.Fid.Uid != newStat.Gid {
- Respond(ctx, r, ErrPerm)
- return
- }
- newStat.Mtime = wstat.Mtime
- }
- // TODO: check group membership
- // for now, group member == group leader == gid
- 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
- // 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) {
- newStat.Gid = wstat.Gid
- } else {
- Respond(ctx, r, ErrPerm)
- return
+ wsfile, ok := r.Fid.File.(WriterStatFile)
+ if !ok {
+ r.err = ErrOperation
+ rc <- r
+ continue
+ }
+ wstat := ifcall.Stat
+ fi, err := r.Fid.File.Stat()
+ if err != nil {
+ r.err = fmt.Errorf("stat: %v", err)
+ rc <- r
+ continue
+ }
+ newStat := fi.Sys().(*Stat)
+ if wstat.Type != ^uint16(0) && wstat.Type != newStat.Type ||
+ wstat.Dev != ^uint32(0) && wstat.Dev != newStat.Dev ||
+ wstat.Qid.Type != QidType(^uint8(0)) && wstat.Qid.Type != newStat.Qid.Type ||
+ wstat.Qid.Vers != ^uint32(0) && wstat.Qid.Vers != newStat.Qid.Vers ||
+ wstat.Qid.Path != ^uint64(0) && wstat.Qid.Path != newStat.Qid.Path ||
+ wstat.Atime != ^uint32(0) && wstat.Atime != newStat.Atime ||
+ wstat.Uid != "" && wstat.Uid != newStat.Uid ||
+ wstat.Muid != "" && wstat.Muid != newStat.Muid {
+ r.err = fmt.Errorf("operation not permitted")
+ rc <- r
+ continue
+ }
+ if wstat.Name != "" && newStat.Name != wstat.Name {
+ parentPath := path.Dir(r.Fid.path)
+ pstat, err := fs.Stat(ExportFS{s.fs}, parentPath)
+ if err != nil {
+ r.err = fmt.Errorf("stat parent: %v", err)
+ rc <- r
+ continue
+ }
+ if !hasPerm(s.fs, pstat, r.Fid.Uid, AWRITE) {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ // TODO: I think 9P protocol prohibits renaming to existent file.
+ // Wstat(9P) in p9p says:
+ // it is an error to change the name to that of
+ // an existing file.
+ // but 9pfs, 9pfuse does the rename when used with `git init`.
+ /*
+ children, err := fs.Glob(ExportFS{s.fs}, path.Join(parentPath, "*"))
+ if err != nil {
+ r.err = fmt.Errorf("glob children: %v", err)
+ rc <- r
+ continue
+ }
+ for _, f := range children {
+ if path.Base(f) == wstat.Name {
+ r.err = fmt.Errorf("file already exists")
+ rc <- r
+ continue
+ }
+ }
+ */
+ newStat.Name = wstat.Name
+ }
+ if wstat.Length != ^int64(0) && wstat.Length != newStat.Length {
+ if fi.IsDir() || !hasPerm(s.fs, fi, r.Fid.Uid, AWRITE) {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ 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.
+ if r.Fid.Uid != newStat.Uid && r.Fid.Uid != newStat.Gid {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ if wstat.Mode&fs.ModeDir != newStat.Mode&fs.ModeDir {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ 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.
+ if r.Fid.Uid != newStat.Uid && r.Fid.Uid != newStat.Gid {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ newStat.Mtime = wstat.Mtime
+ }
+ // TODO: check group membership
+ // for now, group member == group leader == gid
+ 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
+ // 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) {
+ newStat.Gid = wstat.Gid
+ } else {
+ r.err = ErrPerm
+ rc <- r
+ continue
+ }
+ }
+ err = wsfile.WStat(newStat)
+ if err != nil {
+ r.err = fmt.Errorf("wstat: %v", err)
+ rc <- r
+ continue
+ }
+ r.Ofcall = &RWStat{}
+ rc <- r
}
}
- err = wsfile.WStat(newStat)
- if err != nil {
- Respond(ctx, r, fmt.Errorf("wstat: %v", err))
- return
- }
- r.Ofcall = &RWStat{}
- Respond(ctx, r, nil)
}
-func rWStat(r *Req, err error) {
- if err != nil {
- setError(r, err)
+func rWStat(ctx context.Context, c <-chan *Req) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case r := <-c:
+ if r == nil {
+ return
+ }
+ if r.err != nil {
+ setError(r, r.err)
+ }
+ r.Srv.respChan <- r
+ }
}
}
@@ -1073,19 +1313,43 @@ func (s *Server) Serve(ctx context.Context) {
attachChan = make(chan *Req)
flushChan = make(chan *Req)
walkChan = make(chan *Req)
- )
+ openChan = make(chan *Req)
+ createChan = make(chan *Req)
+ readChan = make(chan *Req)
+ writeChan = make(chan *Req)
+ clunkChan = make(chan *Req)
+ removeChan = make(chan *Req)
+ statChan = make(chan *Req)
+ wstatChan = make(chan *Req)
+ )
defer func() {
close(versionChan)
close(authChan)
close(attachChan)
close(flushChan)
close(walkChan)
+ close(openChan)
+ close(createChan)
+ close(readChan)
+ close(writeChan)
+ close(clunkChan)
+ close(removeChan)
+ 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 Respond2(ctx, s)
L:
for {
@@ -1098,12 +1362,14 @@ L:
}
continue L
}
- ctx1, cancel := context.WithCancel(ctx)
- r.Cancel = cancel
+ //ctx1, cancel := context.WithCancel(ctx)
+ //r.Cancel = cancel
go func() {
switch r.Ifcall.(type) {
default:
- Respond(ctx1, r, fmt.Errorf("unknown message type: %d", r.Ifcall.Type()))
+ r.err = fmt.Errorf("unknown message type: %d", r.Ifcall.Type())
+ s.respChan <- r
+ return
case *TVersion:
versionChan <- r
case *TAuth:
@@ -1115,21 +1381,21 @@ L:
case *TWalk:
walkChan <- r
case *TOpen:
- sOpen(ctx1, s, r)
+ openChan <- r
case *TCreate:
- sCreate(ctx1, s, r)
+ createChan <- r
case *TRead:
- sRead(ctx1, s, r)
+ readChan <- r
case *TWrite:
- sWrite(ctx1, s, r)
+ writeChan <- r
case *TClunk:
- sClunk(ctx1, s, r)
+ clunkChan <- r
case *TRemove:
- sRemove(ctx1, s, r)
+ removeChan <- r
case *TStat:
- sStat(ctx1, s, r)
+ statChan <- r
case *TWStat:
- sWStat(ctx1, s, r)
+ wstatChan <- r
}
}()
case <-ctx.Done():
@@ -1139,29 +1405,14 @@ L:
}
}
+/*
// Respond Responds to the request r with the message r.Ofcall if err is nil,
// or if err is not nil, with the Rerror with the error message.
// If r is nil, or both r.Ofcall and err are nil it panics.
-func Respond(ctx context.Context, r *Req, err error) {
+func Respond() {
switch r.Ifcall.(type) {
default:
panic(fmt.Errorf("bug: r.Ifcall: %v", r.Ifcall))
- case *TOpen:
- rOpen(r, err)
- case *TCreate:
- rCreate(r, err)
- case *TRead:
- rRead(r, err)
- case *TWrite:
- rWrite(r, err)
- case *TClunk:
- rClunk(r, err)
- case *TRemove:
- rRemove(r, err)
- case *TStat:
- rStat(r, err)
- case *TWStat:
- rWStat(r, err)
}
r.Ofcall.SetTag(r.Tag)
// free tag.
@@ -1189,6 +1440,7 @@ func Respond(ctx context.Context, r *Req, err error) {
//log.Printf("respond: %v", ctx.Err())
}
}
+*/
func Respond2(ctx context.Context, s *Server) {
for {