lib9p

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

commit 598384d1cc8241a26b999fc63e0e0c608eec3171
parent eb6c4499cd33ac8cbb06bc8af2f1467259e13414
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Thu,  9 Nov 2023 08:48:19 +0900

implement *clientFile.ReadDir

Diffstat:
Mclient/file.go | 35+++++++++++++++++++++++++++++++----
Mclient/fs.go | 3+++
Mexport_fs_test.go | 2+-
Mexport_test.go | 5-----
Mfcall.go | 8++++----
Mstat.go | 12++++++------
Mstat_test.go | 2+-
7 files changed, 46 insertions(+), 21 deletions(-)

diff --git a/client/file.go b/client/file.go @@ -2,7 +2,7 @@ package client import ( "context" - "fmt" + "errors" "io" "io/fs" @@ -46,12 +46,12 @@ func (cf *ClientFile) Close() error { } func (cf *ClientFile) Read(b []byte) (int, error) { + if cf.fid.omode == -1 { + return 0, errors.New("not open") + } if len(b) == 0 { return 0, nil } - if cf.fid.omode == -1 { - return 0, fmt.Errorf("not open") - } if cf.fid.omode&3 != lib9p.OREAD && cf.fid.omode&3 != lib9p.ORDWR { return 0, lib9p.ErrPerm } @@ -87,3 +87,30 @@ func (cf *ClientFile) Read(b []byte) (int, error) { return cur, nil } } + +// TODO: support n +func (cf *ClientFile) ReadDir(n int) ([]fs.DirEntry, error) { + if cf.qid.Type&lib9p.QTDIR == 0 { + return nil, errors.New("not a directory") + } + if cf.fid.omode == -1 { + return nil, errors.New("not open") + } + tag, err := cf.fs.tPool.add() + if err != nil { + return nil, err + } + data, err := cf.fs.c.Read(context.TODO(), tag, cf.fid.fid, cf.fid.offset, cf.iounit) + cf.fs.tPool.delete(tag) + if err != nil { + return nil, err + } + cf.fid.offset += uint64(len(data)) + var de []fs.DirEntry + for len(data) > 0 { + st := lib9p.NewStat(data) + data = data[2+st.Size():] + de = append(de, &lib9p.FileInfo{Stat: *st}) + } + return de, nil +} diff --git a/client/fs.go b/client/fs.go @@ -9,6 +9,7 @@ import ( "strings" "git.mtkn.jp/lib9p" + "log" ) // ClientFS represents the file system the client imports. @@ -19,12 +20,14 @@ type ClientFS struct { // Open opens the file named name in fsys. func (fsys *ClientFS) Open(name string) (lib9p.File, error) { + log.Println("open:", name) return fsys.OpenFile(name, lib9p.OREAD, 0) } // OpenFile opens the file named name in fsys with omode. // If the file does not exist, it create it with perm. func (fsys *ClientFS) OpenFile(name string, omode lib9p.OpenMode, perm fs.FileMode) (lib9p.File, error) { + log.Println("openfile:", name) var ( qid lib9p.Qid iounit uint32 diff --git a/export_fs_test.go b/export_fs_test.go @@ -6,5 +6,5 @@ import ( ) func TestInterface(t *testing.T) { - var _ fs.FS = exportFS{} + var _ fs.FS = ExportFS{} } \ No newline at end of file diff --git a/export_test.go b/export_test.go @@ -58,8 +58,6 @@ var ( NewRStat = newRStat NewTWStat = newTWStat NewRWStat = newRWStat - - NewStat = newStat ) type BufMsg = bufMsg @@ -76,5 +74,3 @@ func (s *Server) RunSpeaker(ctx context.Context) { 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/fcall.go b/fcall.go @@ -1260,11 +1260,11 @@ func newRStat(buf []byte) *RStat { msg := new(RStat) size := gbit32(buf[:4]) msg.Tag = gbit16(buf[5:7]) - msg.Stat = newStat(buf[9:size]) + msg.Stat = NewStat(buf[9:size]) return msg } func (msg *RStat) Size() uint32 { - return uint32(4 + 1 + 2 + 2 + 2 + msg.Stat.size()) + return uint32(4 + 1 + 2 + 2 + 2 + msg.Stat.Size()) } func (msg *RStat) Type() MsgType { return Rstat } func (msg *RStat) GetTag() uint16 { return msg.Tag } @@ -1296,11 +1296,11 @@ func newTWStat(buf []byte) *TWStat { msg := new(TWStat) msg.Tag = gbit16(buf[5:7]) msg.Fid = gbit32(buf[7:11]) - msg.Stat = newStat(buf[13:]) + msg.Stat = NewStat(buf[13:]) return msg } func (msg *TWStat) Size() uint32 { - return uint32(4 + 1 + 2 + 4 + 2 + 2 + msg.Stat.size()) + return uint32(4 + 1 + 2 + 4 + 2 + 2 + msg.Stat.Size()) } func (msg *TWStat) Type() MsgType { return Twstat } func (msg *TWStat) GetTag() uint16 { return msg.Tag } diff --git a/stat.go b/stat.go @@ -55,8 +55,8 @@ type Stat struct { Muid string } -// newStat converts a byte array of stat from a 9P message into Stat struct. -func newStat(b []byte) *Stat { +// NewStat converts a byte array of stat from a 9P message into Stat struct. +func NewStat(b []byte) *Stat { stat := new(Stat) size := gbit16(b[:2]) + 2 cur := 2 // ignore size field @@ -93,12 +93,12 @@ func newStat(b []byte) *Stat { if cur != int(size) { panic(fmt.Errorf("size %d != cursor position %d", size, cur)) } - return stat } -// size returns the size of marshaled stat in bytes. -func (s *Stat) size() uint16 { +// size returns the size of marshaled stat in bytes excluding the +// size field itself. +func (s *Stat) Size() uint16 { // type + dev + qid + mode + atime + mtime + length return uint16(2 + 4 + 13 + 4 + 4 + 4 + 8 + 2 + len(s.Name) + @@ -111,7 +111,7 @@ func (s *Stat) size() uint16 { // in 9P conversatino. func (s *Stat) marshal() []byte { cur := 0 - size := s.size() + size := s.Size() msg := make([]byte, 2+size) pbit16(msg[cur:cur+2], size) cur += 2 diff --git a/stat_test.go b/stat_test.go @@ -17,7 +17,7 @@ func TestStatMarshal(t *testing.T) { Uid: "10", Gid: "11", Muid: "12"}}, } for _, test := range tests { - got := newStat(test.stat.marshal()) + got := NewStat(test.stat.marshal()) if !reflect.DeepEqual(got, test.stat) { t.Errorf("%s: got: %v, want: %v", test.name, got, test.stat) }