lib9p

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

commit 029882122750da85f908f6159c8bbf7e27f60e6c
parent b1dc634e18ea7d53f3aa47c15ce925a4464296c3
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Sat, 28 Oct 2023 08:45:00 +0900

change test

Diffstat:
Aauth_test.go | 13+++++++++++++
Aexport_test.go | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mserver.go | 162++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mserver_test.go | 127++++++++++++++++++++++++++++++++++++++++---------------------------------------
Atestfs/fs.go | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 418 insertions(+), 144 deletions(-)

diff --git a/auth_test.go b/auth_test.go @@ -0,0 +1,12 @@ +package lib9p_test +/* +import ( + "testing" + + "git.mtkn.jp/lib9p" + "git.mtkn.jp/lib9p/client" +) + +func TestAuth(t *testing.T) { + s := lib9p.NewServer() +}*/ +\ No newline at end of file diff --git a/export_test.go b/export_test.go @@ -0,0 +1,72 @@ +package lib9p + +var ( + SrvVersion = sVersion + RepVersion = rVersion + SrvAuth = sAuth + RepAuth = rAuth + SrvAttach = sAttach + RepAttach = rAttach + SrvFlush = sFlush + RepFlush = rFlush + SrvWalk = sWalk + RepWalk = rWalk + SrvOpen = sOpen + RepOpen = rOpen + SrvCreate = sCreate + RepCreate = rCreate + SrvRead = sRead + RepRead = rRead + SrvWrite = sWrite + RepWrite = rWrite + SrvClunk = sClunk + RepClunk = rClunk + SrvRemove = sRemove + RepRemove = rRemove + SrvStat = sStat + RepStat = rStat + SrvWStat = sWStat + RepWStat = rWStat +) + +var ( + NewTVersion = newTVersion + NewRVersion = newRVersion + NewTAuth = newTAuth + NewRAuth = newRAuth + NewTAttach = newTAttach + NewRAttach = newRAttach + NewTFlush = newTFlush + NewRFlush = newRFlush + NewTWalk = newTWalk + NewRWalk = newRWalk + NewTOpen = newTOpen + NewROpen = newROpen + NewTCreate = newTCreate + NewRCreate = newRCreate + NewTRead = newTRead + NewRRead = newRRead + NewTWrite = newTWrite + NewRWrite = newRWrite + NewTClunk = newTClunk + NewRClunk = newRClunk + NewTRemove = newTRemove + NewRRemove = newRRemove + NewTStat = newTStat + NewRStat = newRStat + NewTWStat = newTWStat + NewRWStat = newRWStat + + NewStat = newStat +) + +type BufMsg = bufMsg + +func (s *Server) RPool() *ReqPool { return s.rPool } +func (s *Server) FPool() *FidPool { return s.fPool } + +func (rp *ReqPool) Add(tag uint16) (*Req, error) { return rp.add(tag) } + +func (fp *FidPool) Lookup(fid uint32) (*Fid, bool) { return fp.lookup(fid) } + +func (st *Stat) Size() uint16 { return st.size() } +\ No newline at end of file diff --git a/server.go b/server.go @@ -204,7 +204,7 @@ func sVersion(ctx context.Context, s *Server, r *Req) { Msize: msize, Version: version, } - respond(ctx, r, nil) + Respond(ctx, r, nil) } // rVersion confirms that err is nil, and sets the server's msize to the @@ -222,12 +222,12 @@ func sAuth(ctx context.Context, s *Server, r *Req) { var err error r.Afid, err = s.fPool.add(ifcall.Afid) if err != nil { - respond(ctx, r, ErrDupFid) + Respond(ctx, r, ErrDupFid) } if s.Auth != nil { s.Auth(ctx, r) } else { - respond(ctx, r, fmt.Errorf("authentication not required")) + Respond(ctx, r, fmt.Errorf("authentication not required")) return } } @@ -245,49 +245,49 @@ func sAttach(ctx context.Context, s *Server, r *Req) { ifcall := r.Ifcall.(*TAttach) fid, err := s.fPool.add(ifcall.Fid) if err != nil { - respond(ctx, r, ErrDupFid) + Respond(ctx, r, ErrDupFid) return } switch { case s.Auth == nil && ifcall.Afid == NOFID: case s.Auth == nil && ifcall.Afid != NOFID: - respond(ctx, r, ErrBotch) + Respond(ctx, r, ErrBotch) return case s.Auth != nil && ifcall.Afid == NOFID: - respond(ctx, r, fmt.Errorf("authentication required")) + Respond(ctx, r, fmt.Errorf("authentication required")) return case s.Auth != nil && ifcall.Afid != NOFID: afid, ok := s.fPool.lookup(ifcall.Afid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } af, ok := afid.File.(*AuthFile) if !ok { - respond(ctx, r, fmt.Errorf("not auth file")) + Respond(ctx, r, fmt.Errorf("not auth file")) return } if !af.AuthOK { - respond(ctx, r, fmt.Errorf("not authenticated")) + Respond(ctx, r, fmt.Errorf("not authenticated")) return } } fid.File, err = s.fs.OpenFile(".", OREAD, 0) if err != nil { - respond(ctx, r, fmt.Errorf("open root: %v", err)) + Respond(ctx, r, fmt.Errorf("open root: %v", err)) return } fid.Uid = ifcall.Uname fid.OMode = -1 // TODO: right? st, err := fid.File.Stat() if err != nil { - respond(ctx, r, fmt.Errorf("stat root: %v", err)) + Respond(ctx, r, fmt.Errorf("stat root: %v", err)) return } r.Ofcall = &RAttach{ Qid: st.Sys().(*Stat).Qid, } - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rAttach(r *Req, err error) { @@ -298,7 +298,7 @@ func rAttach(r *Req, err error) { } func sFlush(ctx context.Context, s *Server, r *Req) { - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rFlush(r *Req, err error) { @@ -315,20 +315,20 @@ func sWalk(ctx context.Context, s *Server, r *Req) { ifcall := r.Ifcall.(*TWalk) oldFid, ok := s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } if oldFid.OMode != -1 { - respond(ctx, r, fmt.Errorf("cannot clone open fid")) + Respond(ctx, r, fmt.Errorf("cannot clone open fid")) return } oldst, err := oldFid.File.Stat() if err != nil { - respond(ctx, r, fmt.Errorf("stat: %v", err)) + Respond(ctx, r, fmt.Errorf("stat: %v", err)) return } if len(ifcall.Wnames) > 0 && oldst.Sys().(*Stat).Qid.Type&QTDIR == 0 { - respond(ctx, r, fmt.Errorf("walk on non-dir")) + Respond(ctx, r, fmt.Errorf("walk on non-dir")) return } var newFid *Fid @@ -339,7 +339,7 @@ func sWalk(ctx context.Context, s *Server, r *Req) { newFid, err = s.fPool.add(ifcall.Newfid) if err != nil { log.Printf("alloc: %v", err) - respond(ctx, r, fmt.Errorf("internal error")) + Respond(ctx, r, fmt.Errorf("internal error")) return } } @@ -369,7 +369,7 @@ func sWalk(ctx context.Context, s *Server, r *Req) { r.Ofcall = &RWalk{ Qids: wqids[:n], } - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rWalk(r *Req, err error) { @@ -393,11 +393,11 @@ func sOpen(ctx context.Context, s *Server, r *Req) { var ok bool r.Fid, ok = s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } if r.Fid.OMode != -1 { - respond(ctx, r, ErrBotch) + Respond(ctx, r, ErrBotch) return } // Write attempt to a directory is prohibitted by the protocol. @@ -406,12 +406,12 @@ func sOpen(ctx context.Context, s *Server, r *Req) { // but ORCLOSE is also prohibitted by the protocol... st, err := r.Fid.File.Stat() if err != nil { - respond(ctx, r, fmt.Errorf("stat: %v", err)) + Respond(ctx, r, fmt.Errorf("stat: %v", err)) return } qid := st.Sys().(*Stat).Qid if qid.Type == QTDIR && ifcall.Mode != OREAD { - respond(ctx, r, fmt.Errorf("is a directory")) + Respond(ctx, r, fmt.Errorf("is a directory")) return } var p fs.FileMode @@ -431,11 +431,11 @@ func sOpen(ctx context.Context, s *Server, r *Req) { p |= AWRITE } if qid.Type&QTDIR != 0 && p != AREAD { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } if !hasPerm(r.Fid.File, r.Fid.Uid, p) { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } if ifcall.Mode&ORCLOSE != 0 { @@ -443,11 +443,11 @@ func sOpen(ctx context.Context, s *Server, r *Req) { parent, err := s.fs.OpenFile(parentPath, OREAD, 0) defer parent.Close() if err != nil { - respond(ctx, r, fmt.Errorf("open parent")) + Respond(ctx, r, fmt.Errorf("open parent")) return } if !hasPerm(parent, r.Fid.Uid, AWRITE) { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } } @@ -455,7 +455,7 @@ func sOpen(ctx context.Context, s *Server, r *Req) { Qid: qid, Iounit: s.mSize() - IOHDRSZ, } - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rOpen(r *Req, err error) { @@ -477,26 +477,26 @@ func sCreate(ctx context.Context, s *Server, r *Req) { var ok bool r.Fid, ok = s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } dir := r.Fid.File dirstat, err := dir.Stat() if err != nil { - respond(ctx, r, fmt.Errorf("stat: %v", err)) + Respond(ctx, r, fmt.Errorf("stat: %v", err)) return } if !dirstat.IsDir() { - respond(ctx, r, fmt.Errorf("create in non-dir")) + Respond(ctx, r, fmt.Errorf("create in non-dir")) return } if !hasPerm(dir, r.Fid.Uid, AWRITE) { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } cfdir, ok := r.Fid.File.(CreaterFile) if !ok { - respond(ctx, r, ErrOperation) + Respond(ctx, r, ErrOperation) return } perm := ifcall.Perm @@ -508,25 +508,25 @@ func sCreate(ctx context.Context, s *Server, r *Req) { } file, err := cfdir.Create(ifcall.Name, r.Fid.Uid, ifcall.Mode, perm) if err != nil { - respond(ctx, r, fmt.Errorf("create: %v", err)) + Respond(ctx, r, fmt.Errorf("create: %v", err)) return } if err := r.Fid.File.Close(); err != nil { - respond(ctx, r, fmt.Errorf("close: %v", err)) + Respond(ctx, r, fmt.Errorf("close: %v", err)) return } r.Fid.File = file r.Fid.path = path.Join(r.Fid.path, ifcall.Name) st, err := r.Fid.File.Stat() if err != nil { - respond(ctx, r, fmt.Errorf("stat: %v", err)) + Respond(ctx, r, fmt.Errorf("stat: %v", err)) return } r.Ofcall = &RCreate{ Qid: st.Sys().(*Stat).Qid, Iounit: s.mSize() - IOHDRSZ, } - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rCreate(r *Req, err error) { if err != nil { @@ -548,15 +548,15 @@ func sRead(ctx context.Context, s *Server, r *Req) { var ok bool r.Fid, ok = s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } if r.Fid.OMode == -1 { - respond(ctx, r, fmt.Errorf("not open")) + 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) + Respond(ctx, r, ErrPerm) return } data := make([]byte, ifcall.Count) @@ -569,7 +569,7 @@ func sRead(ctx context.Context, s *Server, r *Req) { fi, err := r.Fid.File.Stat() if err != nil { log.Printf("Stat: %v", err) - respond(ctx, r, fmt.Errorf("internal error")) + Respond(ctx, r, fmt.Errorf("internal error")) return } wg.Add(1) @@ -586,7 +586,7 @@ func sRead(ctx context.Context, s *Server, r *Req) { } if ifcall.Offset != 0 && ifcall.Offset != r.Fid.dirOffset { - respond(ctx, r, fmt.Errorf("invalid dir offset")) + Respond(ctx, r, fmt.Errorf("invalid dir offset")) return } if ifcall.Offset == 0 { @@ -623,7 +623,7 @@ func sRead(ctx context.Context, s *Server, r *Req) { } if err != io.EOF && err != nil { log.Printf("sRead: %v\n", err) - respond(ctx, r, err) + Respond(ctx, r, err) return } } @@ -636,7 +636,7 @@ func sRead(ctx context.Context, s *Server, r *Req) { Count: uint32(n), Data: data[:n], } - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rRead(r *Req, err error) { if err != nil { @@ -649,19 +649,19 @@ func sWrite(ctx context.Context, s *Server, r *Req) { var ok bool r.Fid, ok = s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } if ifcall.Count > s.mSize()-IOHDRSZ { ifcall.Count = s.mSize() - IOHDRSZ } if !hasPerm(r.Fid.File, r.Fid.Uid, AWRITE) { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } 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)) + Respond(ctx, r, fmt.Errorf("write on fid with open mode 0x%x", r.Fid.OMode)) return } ofcall := new(RWrite) @@ -677,19 +677,19 @@ func sWrite(ctx context.Context, s *Server, r *Req) { case io.WriterAt: n, err := file.WriteAt(ifcall.Data, int64(ifcall.Offset)) if err != nil { - respond(ctx, r, fmt.Errorf("write: %v", err)) + Respond(ctx, r, fmt.Errorf("write: %v", err)) return } ofcall.Count = uint32(n) case io.Writer: n, err := file.Write(ifcall.Data) if err != nil { - respond(ctx, r, fmt.Errorf("write: %v", err)) + Respond(ctx, r, fmt.Errorf("write: %v", err)) return } ofcall.Count = uint32(n) default: - respond(ctx, r, ErrOperation) + Respond(ctx, r, ErrOperation) return } wg.Done() @@ -699,7 +699,7 @@ func sWrite(ctx context.Context, s *Server, r *Req) { case <-ctx.Done(): } r.Ofcall = ofcall - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rWrite(r *Req, err error) { @@ -714,12 +714,12 @@ func sClunk(ctx context.Context, s *Server, r *Req) { ifcall := r.Ifcall.(*TClunk) _, ok := s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } s.fPool.delete(ifcall.Fid) r.Ofcall = &RClunk{} - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rClunk(r *Req, err error) { @@ -733,32 +733,32 @@ func sRemove(ctx context.Context, s *Server, r *Req) { var ok bool r.Fid, ok = s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } defer s.fPool.delete(ifcall.Fid) parentPath := path.Dir(r.Fid.path) parent, err := s.fs.OpenFile(parentPath, OREAD, 0) if err != nil { - respond(ctx, r, fmt.Errorf("open parent: %v", err)) + Respond(ctx, r, fmt.Errorf("open parent: %v", err)) return } defer parent.Close() if !hasPerm(parent, r.Fid.Uid, AWRITE) { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } rfile, ok := r.Fid.File.(RemoverFile) if !ok { - respond(ctx, r, ErrOperation) + Respond(ctx, r, ErrOperation) return } if err := rfile.Remove(); err != nil { - respond(ctx, r, fmt.Errorf("remove: %v", err)) + Respond(ctx, r, fmt.Errorf("remove: %v", err)) return } r.Ofcall = &RRemove{} - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rRemove(r *Req, err error) { if err != nil { @@ -771,19 +771,19 @@ func sStat(ctx context.Context, s *Server, r *Req) { var ok bool r.Fid, ok = s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } fileInfo, err := r.Fid.File.Stat() if err != nil { log.Printf("stat %v: %v", r.Fid.File, err) - respond(ctx, r, fmt.Errorf("internal error")) + Respond(ctx, r, fmt.Errorf("internal error")) return } r.Ofcall = &RStat{ Stat: fileInfo.Sys().(*Stat), } - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rStat(r *Req, err error) { @@ -797,18 +797,18 @@ func sWStat(ctx context.Context, s *Server, r *Req) { var ok bool r.Fid, ok = s.fPool.lookup(ifcall.Fid) if !ok { - respond(ctx, r, ErrUnknownFid) + Respond(ctx, r, ErrUnknownFid) return } wsfile, ok := r.Fid.File.(WriterStatFile) if !ok { - respond(ctx, r, ErrOperation) + 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)) + Respond(ctx, r, fmt.Errorf("stat: %v", err)) return } newStat := fi.Sys().(*Stat) @@ -816,33 +816,33 @@ func sWStat(ctx context.Context, s *Server, r *Req) { wstat.Qid.Type != QidType(^uint8(0)) || wstat.Qid.Vers != ^uint32(0) || wstat.Qid.Path != ^uint64(0) || wstat.Atime != ^uint32(0) || wstat.Uid != "" || wstat.Muid != "" { - respond(ctx, r, fmt.Errorf("operation not permitted")) + Respond(ctx, r, fmt.Errorf("operation not permitted")) return } if wstat.Name != "" { parentPath := path.Dir(r.Fid.path) parent, err := s.fs.OpenFile(parentPath, OREAD, 0) if err != nil { - respond(ctx, r, fmt.Errorf("get parent: %v", err)) + Respond(ctx, r, fmt.Errorf("get parent: %v", err)) return } if !hasPerm(parent, r.Fid.Uid, AWRITE) { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } children, err := getChildren(s.fs, parentPath) if err != nil { - respond(ctx, r, fmt.Errorf("get children: %v", err)) + Respond(ctx, r, fmt.Errorf("get children: %v", err)) return } for _, f := range children { s, err := f.Stat() if err != nil { - respond(ctx, r, fmt.Errorf("stat: %v", err)) + Respond(ctx, r, fmt.Errorf("stat: %v", err)) return } if s.Name() == wstat.Name { - respond(ctx, r, fmt.Errorf("file already exists")) + Respond(ctx, r, fmt.Errorf("file already exists")) return } } @@ -850,7 +850,7 @@ func sWStat(ctx context.Context, s *Server, r *Req) { } if wstat.Length != ^int64(0) { if fi.IsDir() || !hasPerm(r.Fid.File, r.Fid.Uid, AWRITE) { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } newStat.Length = wstat.Length @@ -858,11 +858,11 @@ func sWStat(ctx context.Context, s *Server, r *Req) { if wstat.Mode != FileMode(^uint32(0)) { // 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) + Respond(ctx, r, ErrPerm) return } if wstat.Mode&fs.ModeDir != newStat.Mode&fs.ModeDir { - respond(ctx, r, ErrPerm) + Respond(ctx, r, ErrPerm) return } newStat.Mode = wstat.Mode @@ -870,23 +870,23 @@ func sWStat(ctx context.Context, s *Server, r *Req) { if wstat.Mtime != ^uint32(0) { // 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) + Respond(ctx, r, ErrPerm) return } newStat.Mtime = wstat.Mtime } if wstat.Gid != "" { // TODO implement - respond(ctx, r, fmt.Errorf("not implemented")) + Respond(ctx, r, fmt.Errorf("not implemented")) return } err = wsfile.WStat(newStat) if err != nil { - respond(ctx, r, fmt.Errorf("wstat: %v", err)) + Respond(ctx, r, fmt.Errorf("wstat: %v", err)) return } r.Ofcall = &RWStat{} - respond(ctx, r, nil) + Respond(ctx, r, nil) } func rWStat(r *Req, err error) { @@ -916,7 +916,7 @@ L: go func() { switch r.Ifcall.(type) { default: - respond(ctx1, r, fmt.Errorf("unknown message type: %d", r.Ifcall.Type())) + Respond(ctx1, r, fmt.Errorf("unknown message type: %d", r.Ifcall.Type())) case *TVersion: sVersion(ctx1, s, r) case *TAuth: @@ -952,10 +952,10 @@ L: } } -// Respond responds to the request r with the message r.Ofcall if err is nil, +// 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(ctx context.Context, r *Req, err error) { switch r.Ifcall.(type) { default: panic(fmt.Errorf("bug: r.Ifcall: %v", r.Ifcall)) diff --git a/server_test.go b/server_test.go @@ -4,7 +4,8 @@ import ( "context" "fmt" "io" - "path/filepath" + "path" + "strings" "testing" "git.mtkn.jp/lib9p" @@ -16,55 +17,55 @@ func newReq(s *lib9p.Server, msg lib9p.Msg) (*lib9p.Req, error) { if err != nil { return nil, fmt.Errorf("lib9p.ReqPool.add(%d): %w", msg.GetTag(), err) } - r.srv = s - r.tag = msg.GetTag() - r.ifcall = msg + r.Srv = s + r.Tag = msg.GetTag() + r.Ifcall = msg return r, nil } func handleReq(ctx context.Context, s *lib9p.Server, r *lib9p.Req) { - switch r.ifcall.(type) { + switch r.Ifcall.(type) { default: - respond(ctx, r, fmt.Errorf("unknown message type: %d", r.ifcall.Type())) - case *TVersion: - sVersion(ctx, s, r) - case *TAuth: - sAuth(ctx, s, r) - case *TAttach: - sAttach(ctx, s, r) - case *TWalk: - sWalk(ctx, s, r) - case *TOpen: - sOpen(ctx, s, r) - case *TCreate: - sCreate(ctx, s, r) - case *TRead: - sRead(ctx, s, r) - case *TWrite: - sWrite(ctx, s, r) - case *TClunk: - sClunk(ctx, s, r) - case *TRemove: - sRemove(ctx, s, r) - case *TStat: - sStat(ctx, s, r) - case *TWStat: - sWStat(ctx, s, r) + lib9p.Respond(ctx, r, fmt.Errorf("unknown message type: %d", r.Ifcall.Type())) + case *lib9p.TVersion: + lib9p.SrvVersion(ctx, s, r) + case *lib9p.TAuth: + lib9p.SrvAuth(ctx, s, r) + case *lib9p.TAttach: + lib9p.SrvAttach(ctx, s, r) + case *lib9p.TWalk: + lib9p.SrvWalk(ctx, s, r) + case *lib9p.TOpen: + lib9p.SrvOpen(ctx, s, r) + case *lib9p.TCreate: + lib9p.SrvCreate(ctx, s, r) + case *lib9p.TRead: + lib9p.SrvRead(ctx, s, r) + case *lib9p.TWrite: + lib9p.SrvWrite(ctx, s, r) + case *lib9p.TClunk: + lib9p.SrvClunk(ctx, s, r) + case *lib9p.TRemove: + lib9p.SrvRemove(ctx, s, r) + case *lib9p.TStat: + lib9p.SrvStat(ctx, s, r) + case *lib9p.TWStat: + lib9p.SrvWStat(ctx, s, r) } } // This function does the actual work for TestWalk(). -func testWalk(t *testing.T, fs *testfs.TestFS, path string, file *testfs.TestFile) { - t.Logf("walk %s", path) - f, err := fs.walk(split9path(path)) +func testWalk(t *testing.T, fs *testfs.TestFS, pathname string, file *testfs.TestFile) { + t.Logf("walk %s", pathname) + f, err := fs.Walk(strings.Split(pathname, "/")) if err != nil { - t.Errorf("open %s: %v", path, err) + t.Errorf("open %s: %v", pathname, err) } if f != file { - t.Errorf("open %s: wrong file", path) + t.Errorf("open %s: wrong file", pathname) } for _, child := range file.Children { - childpath := filepath.Join(path, child.St.Name) + childpath := path.Join(pathname, child.St.Name) testWalk(t, fs, childpath, child) } } @@ -73,7 +74,7 @@ func testWalk(t *testing.T, fs *testfs.TestFS, path string, file *testfs.TestFil // checks if all files can be opened without error and if // the opened file is the same as the file accessed via testFS.child func TestWalk(t *testing.T) { - testWalk(t, fsys, ".", fsys.Root) + testWalk(t, testfs.FS, ".", testfs.FS.Root) } func TestServer(t *testing.T) { @@ -84,81 +85,81 @@ func TestServer(t *testing.T) { defer cr.Close() defer sw.Close() msg := []lib9p.Msg{ - &TVersion{ - Tag: NOTAG, + &lib9p.TVersion{ + Tag: lib9p.NOTAG, Msize: 1024, Version: "9P2000", }, - &TAttach{ + &lib9p.TAttach{ Tag: 0, Fid: 0, - Afid: NOFID, + Afid: lib9p.NOFID, Uname: "glenda", Aname: "", }, - &TStat{ + &lib9p.TStat{ Tag: 0, Fid: 0, }, - &TWalk{ + &lib9p.TWalk{ Tag: 0, Fid: 0, Newfid: 1, Wnames: []string{}, }, - &TOpen{ + &lib9p.TOpen{ Tag: 0, Fid: 1, - Mode: OREAD, + Mode: lib9p.OREAD, }, - &TRead{ + &lib9p.TRead{ Tag: 0, Fid: 1, Offset: 0, Count: 1024, }, - &TClunk{ + &lib9p.TClunk{ Fid: 1, }, - &TWalk{ + &lib9p.TWalk{ Tag: 0, Fid: 0, Newfid: 1, Wnames: []string{"dir", "file"}, }, - &TOpen{ + &lib9p.TOpen{ Tag: 0, Fid: 1, - Mode: ORDWR, + Mode: lib9p.ORDWR, }, - &TRead{ + &lib9p.TRead{ Tag: 0, Fid: 1, Offset: 0, Count: 1024, }, - &TWrite{ + &lib9p.TWrite{ Tag: 0, Fid: 1, Offset: 2, Count: 4, Data: []byte("a"), }, - &TRead{ + &lib9p.TRead{ Tag: 0, Fid: 1, Offset: 0, Count: 1024, }, } - s := NewServer(fsys, 1024, sr, sw) + s := lib9p.NewServer(testfs.FS, 1024, sr, sw) for _, m := range msg { r, err := newReq(s, m) if err != nil { t.Fatalf("newReq: %v", err) return } - t.Logf("<-- %v\n", r.ifcall) + t.Logf("<-- %v\n", r.Ifcall) go handleReq(context.Background(), s, r) buf := make([]byte, 1024) @@ -166,25 +167,25 @@ func TestServer(t *testing.T) { if err != nil { t.Fatalf("read: %v", err) } - t.Logf("--> %v\n", buflib9p.Msg(buf)) + t.Logf("--> %v\n", lib9p.BufMsg(buf)) - if buflib9p.Msg(buf).Type() == Rread { - rread := newRRead(buf) + if lib9p.BufMsg(buf).Type() == lib9p.Rread { + rread := lib9p.NewRRead(buf) data := rread.Data - fid, ok := s.fPool.lookup(m.(*TRead).Fid) + fid, ok := s.FPool().Lookup(m.(*lib9p.TRead).Fid) if !ok { - t.Fatalf("lookup fid %d", m.(*TRead).Fid) + t.Fatalf("lookup fid %d", m.(*lib9p.TRead).Fid) } st, err := fid.File.Stat() if err != nil { t.Errorf("stat: %v", err) continue } - if st.Sys().(*Stat).Qid.Type&QTDIR != 0 { + if st.Sys().(*lib9p.Stat).Qid.Type&lib9p.QTDIR != 0 { for i := 0; i < len(data); { - stat := newStat(data[i:]) + stat := lib9p.NewStat(data[i:]) t.Logf("stat: %v", stat) - i += int(stat.size()) + 2 + i += int(stat.Size()) + 2 } } else { t.Logf("content: %s", string(data)) diff --git a/testfs/fs.go b/testfs/fs.go @@ -0,0 +1,187 @@ +package testfs + +import ( + "bytes" + "fmt" + "io/fs" + "strings" + "time" + + "git.mtkn.jp/lib9p" +) + +const sleepTime = 1 * time.Second + +type TestFile struct { + Fsys *TestFS + Parent *TestFile + Children []*TestFile + Content []byte + Reader *bytes.Reader + + St lib9p.Stat +} + +func (f *TestFile) Stat() (*lib9p.FileInfo, error) { + return &lib9p.FileInfo{Stat: f.St}, nil +} +func (f *TestFile) Close() error { + f.Reader = nil + return nil +} + +func (f *TestFile) Read(b []byte) (int, error) { + if f.Fsys.Slow { + time.Sleep(sleepTime) + } + return f.Reader.Read(b) +} + +func (f *TestFile) ReadAt(b []byte, off int64) (n int, err error) { + if f.Fsys.Slow { + time.Sleep(sleepTime) + } + return f.Reader.ReadAt(b, off) +} + +func (f *TestFile) ReadDir(n int) ([]*lib9p.DirEntry, error) { + de := make([]*lib9p.DirEntry, len(f.Children)) + for i, c := range f.Children { + de[i], _ = c.Stat() + } + return de, nil +} + +func (f *TestFile) WriteAt(p []byte, off int64) (int, error) { + if f.Fsys.Slow { + time.Sleep(sleepTime) + } + if f.Reader == nil { + return 0, fmt.Errorf("not open") + } + if off < 0 || off > int64(len(f.Content)) { + return 0, fmt.Errorf("bad offset") + } + + if off+int64(len(p)) > int64(len(f.Content)) { + newcon := make([]byte, off+int64(len(p))) + copy(newcon, f.Content) + f.Content = newcon + } + copy(f.Content[off:], p) + f.Reader.Reset(f.Content) + return len(p), nil +} + +type TestFS struct { + Root *TestFile + Slow bool +} + +func (fs *TestFS) OpenFile(path string, omode lib9p.OpenMode, perm fs.FileMode) (lib9p.File, error) { + wnames := strings.Split(path, "/") + f, err := fs.Walk(wnames) + if err != nil { + return nil, fmt.Errorf("walk: %v", err) + } + f.Reader = bytes.NewReader(f.Content) + return f, nil +} + +func (fs *TestFS) Walk(wnames []string) (*TestFile, error) { + if len(wnames) == 1 && (wnames[0] == "." || wnames[0] == "") { + return fs.Root, nil + } + cwd := fs.Root +L: + for i, name := range wnames { + for _, child := range cwd.Children { + if child.St.Name == name { + cwd = child + continue L + } + } + return nil, fmt.Errorf("%s not found", strings.Join(wnames[:i+1], "/")) + } + return cwd, nil +} + +var FS *TestFS + +func init() { + FS = &TestFS{ + Root: &TestFile{ + St: lib9p.Stat{ + Qid: lib9p.Qid{Path: 0, Type: lib9p.QTDIR}, + Mode: lib9p.FileMode(fs.ModeDir | 0755), + Name: "root", + Uid: "glenda", + Gid: "glenda", + Muid: "glenda", + }, + Children: []*TestFile{ + &TestFile{ + Content: []byte("a\n"), + St: lib9p.Stat{ + Qid: lib9p.Qid{Path: 1, Type: lib9p.QTFILE}, + Mode: lib9p.FileMode(0644), + Name: "a", + Uid: "glenda", + Gid: "glenda", + Muid: "glenda", + }, + }, + &TestFile{ + Content: []byte("b\n"), + St: lib9p.Stat{ + Qid: lib9p.Qid{Path: 2, Type: lib9p.QTFILE}, + Mode: lib9p.FileMode(0400), + Name: "b", + Uid: "ken", + Gid: "ken", + Muid: "ken", + }, + }, + &TestFile{ + St: lib9p.Stat{ + Qid: lib9p.Qid{Path: 3, Type: lib9p.QTDIR}, + Mode: lib9p.FileMode(fs.ModeDir | 0755), + Name: "dir", + Uid: "rob", + Gid: "rob", + Muid: "rob", + }, + Children: []*TestFile{ + &TestFile{ + Content: []byte("unko\n"), + St: lib9p.Stat{ + Qid: lib9p.Qid{Path: 4, Type: lib9p.QTFILE}, + Mode: lib9p.FileMode(0666), + Name: "file", + Uid: "brian", + Gid: "brian", + Muid: "dennis", + }, + }, + }, + }, + }, + }, + } + SetFsysAndParent(FS, nil) +} + +// SetFsysAndParent sets file.fsys and file.parent for every file in the fsys. +// Pass nil as file to setup entire file system. +func SetFsysAndParent(fsys *TestFS, file *TestFile) { + if file == nil { + file = fsys.Root + file.Parent = fsys.Root + file.Fsys = fsys + } + for _, child := range file.Children { + child.Parent = file + child.Fsys = fsys + SetFsysAndParent(fsys, child) + } +}