lib9p

Go 9P library.
Log | Files | Refs

commit d2c09a602202d600ef96b59287ec0ef7bd824760
parent fe57e966d3d7d1de3a6485a4c9eaf83b6394acbb
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Fri, 22 Sep 2023 09:23:58 +0900

add test

Diffstat:
Mclient_test.go | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mfcall.go | 35+++++++++++++++++++----------------
Mfid.go | 9++++++++-
Mfs_test.go | 26+++++++-------------------
Mserver.go | 4+++-
Mstat.go | 5+++--
Muid.go | 6+++---
7 files changed, 187 insertions(+), 67 deletions(-)

diff --git a/client_test.go b/client_test.go @@ -3,6 +3,7 @@ package lib9p import ( "fmt" "io" + "path/filepath" "testing" ) @@ -11,23 +12,79 @@ var fsys *testFS func init() { fsys = &testFS{ root: &testFile{ - name: "root", - omode: -1, - isDir: true, + stat: Stat{ + Qid: Qid{Path: 0, Type: QTDIR}, + Mode: FileMode(DMDIR | 0755), + Name: "root", + Uid: "glenda", + Gid: "glenda", + Muid: "glenda", + }, children: []*testFile{ &testFile{ - name: "unko", - omode: -1, - isDir: false, - content: []byte("unko\n"), + content: []byte("a\n"), + stat: Stat{ + Qid: Qid{Path: 1, Type: QTFILE}, + Mode: FileMode(0644), + Name: "a", + Uid: "glenda", + Gid: "glenda", + Muid: "glenda", + }, + }, + &testFile{ + content: []byte("b\n"), + stat: Stat{ + Qid: Qid{Path: 2, Type: QTFILE}, + Mode: FileMode(0400), + Name: "b", + Uid: "ken", + Gid: "ken", + Muid: "ken", + }, + }, + &testFile{ + stat: Stat{ + Qid: Qid{Path: 3, Type: QTDIR}, + Mode: FileMode(DMDIR | 0755), + Name: "dir", + Uid: "rob", + Gid: "rob", + Muid: "rob", + }, + children: []*testFile{ + &testFile{ + content: []byte("unko\n"), + stat: Stat{ + Qid: Qid{Path: 4, Type: QTFILE}, + Mode: FileMode(0644), + Name: "file", + Uid: "brian", + Gid: "brian", + Muid: "dennis", + }, + }, + }, }, }, }, } - fsys.root.fsys = fsys - fsys.root.parent = fsys.root - fsys.root.children[0].fsys = fsys - fsys.root.children[0].parent = fsys.root + setFsysAndParent(fsys, 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) + } } func newReq(s *Server, msg Msg) (*Req, error) { @@ -72,24 +129,27 @@ func handleReq(s *Server, r *Req) { } } -func TestWalk(t *testing.T) { - f, err := fsys.Open(".") +// This function does the actual work for TestWalk(). +func testWalk(t *testing.T, fs *testFS, path string, file *testFile) { + t.Logf("walk %s", path) + f, err := fs.walk(split9path(path)) if err != nil { - t.Errorf("open: %v", err) - } else if f != fsys.root { - t.Errorf("open %p != %p", f, fsys.root) + t.Errorf("open %s: %v", path, err) } - f, err = fsys.Open("unko") - if err != nil { - t.Errorf("open: %v", err) - } else if f != fsys.root.children[0] { - t.Errorf("open %p != %p", f, fsys.root.children[0]) + if f != file { + t.Errorf("open %s: wrong file", path) } - f, err = fsys.Open("chinko") - if err == nil { - t.Errorf("open non-existent file") + for _, child := range file.children { + childpath := filepath.Join(path, child.stat.Name) + testWalk(t, fs, childpath, child) } +} +// TestWalk walk through testFS in depth-first fassion, +// 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) } func TestServer(t *testing.T) { @@ -108,6 +168,47 @@ func TestServer(t *testing.T) { uname: "glenda", aname: "", }, + &TStat{ + tag: 0, + fid: 0, + }, + &TWalk{ + tag: 0, + fid: 0, + newFid: 1, + wname: []string{}, + }, + &TOpen{ + tag: 0, + fid: 1, + mode: OREAD, + }, + &TRead{ + tag: 0, + fid: 1, + offset: 0, + count: 1024, + }, + &TClunk{ + fid: 1, + }, + &TWalk{ + tag: 0, + fid: 0, + newFid: 1, + wname: []string{"dir", "file"}, + }, + &TOpen{ + tag: 0, + fid: 1, + mode: OREAD, + }, + &TRead{ + tag: 0, + fid: 1, + offset: 0, + count: 1024, + }, } s := NewServer(fsys, 1024, sr, sw) @@ -119,13 +220,31 @@ func TestServer(t *testing.T) { return } t.Logf("<-- %v\n", r.ifcall) - go handleReq(s, r) buf := make([]byte, 1024) + _, err = cr.Read(buf) if err != nil { t.Fatalf("read: %v", err) } t.Logf("--> %v\n", bufMsg(buf)) + + if bufMsg(buf).Type() == Rread { + rread := newRRead(buf) + data := rread.Data() + fid, ok := s.fPool.lookup(m.(*TRead).Fid()) + if !ok { + t.Fatalf("lookup fid %d", m.(*TRead).Fid()) + } + if fid.Qid.Type&QTDIR != 0 { + for i := 0; i < len(data); { + stat := newStat(data[i:]) + t.Logf("stat: %v", stat) + i += int(stat.size()) + 2 + } + } else { + t.Logf("content: %s", string(data)) + } + } } } diff --git a/fcall.go b/fcall.go @@ -559,7 +559,6 @@ func (msg *RFlush) String() string { } type TWalk struct { - size uint32 tag uint16 fid uint32 newFid uint32 @@ -569,7 +568,7 @@ type TWalk struct { func newTWalk(buf []byte) *TWalk { cur := 0 msg := new(TWalk) - msg.size = gbit32(buf[cur : cur+4]) + msize := gbit32(buf[cur : cur+4]) cur += 4 cur += 1 // msgType msg.tag = gbit16(buf[cur : cur+2]) @@ -587,13 +586,19 @@ func newTWalk(buf []byte) *TWalk { msg.wname[i] = string(buf[cur : cur+size]) cur += size } - if cur != int(msg.size) { - panic("length of buf and cursor position not match") + if cur != int(msize) { + panic(fmt.Errorf("message size %d != cursor position %d", msize, cur)) } return msg } -func (msg *TWalk) Size() uint32 { return msg.size } +func (msg *TWalk) Size() uint32 { + size := uint32(4 + 1 + 2 + 4 + 4 + 2) + for _, wname := range msg.wname { + size += 2 + uint32(len(wname)) + } + return size +} func (msg *TWalk) Type() MsgType { return Twalk } func (msg *TWalk) Tag() uint16 { return msg.tag } func (msg *TWalk) SetTag(t uint16) { msg.tag = t } @@ -916,7 +921,6 @@ func (msg *RCreate) String() string { } type TRead struct { - size uint32 tag uint16 fid uint32 offset uint64 @@ -926,7 +930,7 @@ type TRead struct { func newTRead(buf []byte) *TRead { cur := 0 msg := new(TRead) - msg.size = gbit32(buf[cur : cur+4]) + size := gbit32(buf[cur : cur+4]) cur += 4 cur += 1 // type msg.tag = gbit16(buf[cur : cur+2]) @@ -937,12 +941,12 @@ func newTRead(buf []byte) *TRead { cur += 8 msg.count = gbit32(buf[cur : cur+4]) cur += 4 - if cur != len(buf) { - panic("length of buf and cursor position don't match") + if cur != int(size) { + panic(fmt.Errorf("size %d != cursor position %d", size, cur)) } return msg } -func (msg *TRead) Size() uint32 { return msg.size } +func (msg *TRead) Size() uint32 { return 4 + 1 + 2 + 4 + 8 + 4 } func (msg *TRead) Type() MsgType { return Tread } func (msg *TRead) Tag() uint16 { return msg.tag } func (msg *TRead) SetTag(t uint16) { msg.tag = t } @@ -1259,19 +1263,17 @@ func (msg *RRemove) String() string { } type TStat struct { - size uint32 - tag uint16 - fid uint32 + tag uint16 + fid uint32 } func newTStat(buf []byte) *TStat { msg := new(TStat) - msg.size = gbit32(buf[0:4]) msg.tag = gbit16(buf[5:7]) msg.fid = gbit32(buf[7:11]) return msg } -func (msg *TStat) Size() uint32 { return msg.size } +func (msg *TStat) Size() uint32 { return 4 + 1 + 2 + 4 } func (msg *TStat) Type() MsgType { return Tstat } func (msg *TStat) Tag() uint16 { return msg.tag } func (msg *TStat) SetTag(t uint16) { msg.tag = t } @@ -1296,8 +1298,9 @@ type RStat struct { func newRStat(buf []byte) *RStat { msg := new(RStat) + size := gbit32(buf[:4]) msg.tag = gbit16(buf[5:7]) - msg.stat = newStat(buf[9:]) + msg.stat = newStat(buf[9:size]) return msg } func (msg *RStat) Size() uint32 { diff --git a/fid.go b/fid.go @@ -88,7 +88,14 @@ func (pool *FidPool) String() string { defer pool.lock.Unlock() s := "{" for fnum, fstruct := range pool.m { - st, _ := fstruct.File.Stat() + if fstruct.File == nil { + s += fmt.Sprintf(" [%d]<nil>", fnum) + continue + } + st, err := fstruct.File.Stat() + if err != nil { + panic(err) + } s += fmt.Sprintf(" [%d]%v", fnum, st.Name()) } s += "}" diff --git a/fs_test.go b/fs_test.go @@ -4,17 +4,16 @@ import ( "bytes" "fmt" "strings" + "path/filepath" ) type testFile struct { - name string - omode OpenMode - isDir bool fsys *testFS parent *testFile children []*testFile content []byte reader *bytes.Reader + stat Stat } @@ -27,28 +26,18 @@ func (f *testFile) Child() ([]File, error) { return child, nil } func (f *testFile) Stat() (*FileInfo, error) { return &FileInfo{Stat: f.stat}, nil } -func (f *testFile) Open(omode OpenMode) error { - if f.omode != -1 { - return fmt.Errorf("already open") - } - if omode == -1 { - return fmt.Errorf("invalid mode: %d", omode) - } - f.omode = omode +func (f *testFile) Open() error { + fmt.Println("open") f.reader = bytes.NewReader(f.content) return nil } func (f *testFile) Close() error { - f.omode = -1 f.reader = nil return nil } func (f *testFile) Read(b []byte) (int, error) { - if f.omode != OREAD && f.omode != ORDWR { - return 0, fmt.Errorf("permission denied") - } return f.reader.Read(b) } @@ -67,7 +56,7 @@ func (fs *testFS) walk(wnames []string) (*testFile, error) { L: for i, name := range wnames { for _, child := range cwd.children { - if child.name == name { + if child.stat.Name == name { cwd = child continue L } @@ -78,9 +67,8 @@ L: } func clean9path(path string) string { - if path[len(path)-1] == '/' { - path = path[:len(path)-1] - } + // TODO: eliminate leading ".." + path = filepath.Clean(path) return path } diff --git a/server.go b/server.go @@ -252,18 +252,20 @@ func sWalk(s *Server, r *Req) { for i, name := range ifcall.WName() { child, err := walkfile(cwd, name) if err != nil { + log.Printf("walkfile: %v", err) break } stat, err := child.Stat() if err != nil { + log.Printf("stat: %v", err) break } wqids[i] = stat.Qid() cwd = child n++ } - newFid.File = cwd + log.Printf("fPool: %p %v", s.fPool, s.fPool) newFid.Uid = oldFid.Uid if n == 0 { newFid.Qid = oldFid.Qid diff --git a/stat.go b/stat.go @@ -61,6 +61,7 @@ type Stat struct { func newStat(b []byte) *Stat { stat := new(Stat) + size := gbit16(b[:2]) + 2 cur := 2 // ignore size field stat.Type = gbit16(b[cur : cur+2]) cur += 2 @@ -92,8 +93,8 @@ func newStat(b []byte) *Stat { cur += 2 stat.Muid = string(b[cur : cur+muidSize]) cur += muidSize - if cur != len(b) { - panic("length and cursor position don't match") + if cur != int(size) { + panic(fmt.Errorf("size %d != cursor position %d", size, cur)) } return stat diff --git a/uid.go b/uid.go @@ -14,18 +14,18 @@ func hasPerm(f File, uid string, p fs.FileMode) bool { return false } fp := fi.Mode().Perm() - Stat := fi.Sys().(*Stat) + stat := fi.Sys().(*Stat) m := fp & 7 // other if (p & m) == p { return true } - if Stat.Uid == uid { + if stat.Uid == uid { m |= (fp >> 6) & 7 if (p & m) == p { return true } } - if Stat.Gid == uid { + if stat.Gid == uid { m |= (fp >> 3) & 7 if (p & m) == p { return true