commit d2c09a602202d600ef96b59287ec0ef7bd824760
parent fe57e966d3d7d1de3a6485a4c9eaf83b6394acbb
Author: Matsuda Kenji <info@mtkn.jp>
Date: Fri, 22 Sep 2023 09:23:58 +0900
add test
Diffstat:
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