commit b9e7dc94845876ecafd12a15b1d00c6fa9bded3f
parent 90ec08d4677d4739a49f018cc1947480c46f7000
Author: Matsuda Kenji <info@mtkn.jp>
Date: Wed, 3 Jan 2024 12:31:37 +0900
add TestSWstat
Diffstat:
M | fs_test.go | | | 23 | +++++++++++++++++++++++ |
M | server_test.go | | | 217 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
2 files changed, 219 insertions(+), 21 deletions(-)
diff --git a/fs_test.go b/fs_test.go
@@ -86,6 +86,12 @@ func (f *testFile) WriteAt(p []byte, off int64) (int, error) {
return len(p), nil
}
+// permission check is intentionally skipped.
+func (f *testFile) WStat(stat *Stat) error {
+ f.stat = *stat
+ return nil
+}
+
type testFS struct {
// root is the root testFile of this file system.
root *testFile
@@ -223,6 +229,7 @@ func init() {
{"a", 0644, "glenda", "glenda", "glenda", "a\n"},
{"b", 0600, "ken", "ken", "ken", "b\n"},
{"c", 0644, "glenda", "glenda", "glenda", ""},
+ {"d", fs.ModeDir | 0770, "glenda", "bell", "glenda", ""},
{"dir", fs.ModeDir | 0755, "rob", "rob", "rob", ""},
{"dir/file", 0666, "brian", "brian", "dennis", "unko\n"},
}
@@ -262,6 +269,22 @@ func init() {
}
testfs.waitc = make(chan struct{})
testfs.groups = map[string]group{
+ "glenda": group{
+ leader: "glenda",
+ members: map[string]bool{"glenda": true},
+ },
+ "ken": group{
+ leader: "ken",
+ members: map[string]bool{"ken": true},
+ },
+ "brian": group{
+ leader: "brian",
+ members: map[string]bool{"brian": true},
+ },
+ "rob": group{
+ leader: "rob",
+ members: map[string]bool{"rob": true},
+ },
"bell": group{
leader: "glenda",
members: map[string]bool{
diff --git a/server_test.go b/server_test.go
@@ -5,6 +5,7 @@ import (
"context"
"errors"
"io"
+ "io/fs"
"os"
"path"
"reflect"
@@ -509,7 +510,7 @@ func TestSCreate(t *testing.T) {
t.Errorf("%d: qid mismatch: want: %v, got: %v", i, ff.stat.Qid, qid)
goto remove
}
-remove:
+ remove:
dd, err := testfs.walkPath(test.dir)
if err != nil {
t.Fatal(i, err)
@@ -572,9 +573,9 @@ func testSRead(t *testing.T, pathname string, c *conn, tc, rc chan *request) {
}
func TestSWrite(t *testing.T) {
- tests := []struct{
+ tests := []struct {
pathname string
- data []byte
+ data []byte
}{
{"c", []byte("unko")},
}
@@ -582,7 +583,7 @@ func TestSWrite(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go sWrite(ctx, c, tc)
- for i, test := range tests{
+ for i, test := range tests {
func() {
c.fPool.delete(0)
fid, err := c.fPool.add(0)
@@ -619,10 +620,10 @@ func TestSWrite(t *testing.T) {
}
func TestSClunk(t *testing.T) {
- tests := []struct{
- fid uint32
+ tests := []struct {
+ fid uint32
ifcall *TClunk
- want Msg
+ want Msg
}{
{0, &TClunk{Fid: 0}, &RClunk{}},
{0, &TClunk{Fid: 1}, &RError{}},
@@ -672,15 +673,15 @@ func TestSClunk(t *testing.T) {
}
func TestSRemove(t *testing.T) {
- tests := []struct{
- fid uint32
+ tests := []struct {
+ fid uint32
pathname string
- fuid string // uid of the file to be removed.
- mode OpenMode
- perm FileMode
- ruid string // uid of the user who evoke remove.
- ifcall *TRemove
- want Msg
+ fuid string // uid of the file to be removed.
+ mode OpenMode
+ perm FileMode
+ ruid string // uid of the user who evoke remove.
+ ifcall *TRemove
+ want Msg
}{
// ok.
{0, "unko", "glenda", OREAD, 0777, "glenda", &TRemove{Fid: 0}, &RRemove{}},
@@ -721,7 +722,7 @@ func TestSRemove(t *testing.T) {
if _, ok := test.want.(*RRemove); !ok {
t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v",
i, test.want, ofcall)
- return
+ return
}
if _, err := testfs.OpenFile(test.pathname, OREAD); err == nil {
t.Errorf("%d: file not removed", i)
@@ -733,7 +734,7 @@ func TestSRemove(t *testing.T) {
if _, ok := test.want.(*RError); !ok {
t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v",
i, test.want, ofcall)
- return
+ return
}
default:
t.Fatalf("%d: unexpected message: %v", i, ofcall)
@@ -768,8 +769,8 @@ func testSStat(t *testing.T, pathname string, c *conn, tc, rc chan *request) {
c.fPool.delete(0)
switch ofcall := ofcall.(type) {
case *RStat:
- if !reflect.DeepEqual(st, *ofcall.Stat) {
- t.Errorf("stat doesn't match:\n\twant: %v\n\tgot: %v", st, ofcall.Stat)
+ if !reflect.DeepEqual(&st, ofcall.Stat) {
+ t.Errorf("stat doesn't match:\n\twant: %v\n\tgot: %v", &st, ofcall.Stat)
}
case *RError:
t.Errorf("unexpected message: %v", ofcall)
@@ -780,4 +781,179 @@ func testSStat(t *testing.T, pathname string, c *conn, tc, rc chan *request) {
cpath := path.Join(pathname, child.stat.Name)
testSStat(t, cpath, c, tc, rc)
}
-}
-\ No newline at end of file
+}
+
+func TestSWstat(t *testing.T) {
+ tests := []struct {
+ fid uint32
+ path string
+ uid string
+ stat *Stat
+ ifcall *TWStat
+ want Msg
+ }{
+ // 0 ok: rename by owner of the directory.
+ {0, "./d/unko", "glenda", &Stat{Name: "unko"},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Name: "oppai"},
+ }, &RWStat{}},
+ // 1 ok: rename by group member of the directory.
+ {0, "./d/unko", "ken", &Stat{Name: "unko"},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Name: "oppai"},
+ }, &RWStat{}},
+ // 2 permission denied: rename by other.
+ {0, "./d/unko", "kenji", &Stat{Name: "unko"},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Name: "oppai"},
+ }, &RError{Ename: ErrPerm}},
+ // 3 ok: length change by owner with write permission
+ {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: 0700, Length: 10},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Mode: 0700, Length: 20},
+ }, &RWStat{}},
+ // 4 permission denied: length change by owner without write permission
+ {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: 0000, Length: 10},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Mode: 0700, Length: 20},
+ }, &RError{Ename: ErrPerm}},
+ // 5 operation not permitted: length change of a directory to other than 0
+ {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 10},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 20},
+ }, &RError{}},
+ /*
+ // TODO: change directory length to 0.
+ {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 10},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length:0},
+ }, &RWStat{}},
+ */
+ // 6 ok: change mode by owner
+ {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0755},
+ }, &RWStat{}},
+ // 7 ok: change mode by the leader
+ {0, "unko", "glenda", &Stat{Gid: "bell", Mode: fs.ModeDir | 0700},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Gid: "glenda", Mode: fs.ModeDir | 0755},
+ }, &RWStat{}},
+ // 8 permission denied: change mode by a non-leader member
+ {0, "unko", "ken", &Stat{Gid: "bell", Mode: fs.ModeDir | 0700},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Gid: "glenda", Mode: fs.ModeDir | 0755},
+ }, &RError{}},
+ // 9 ok: change mtime by owner
+ {0, "unko", "glenda", &Stat{Uid: "glenda", Mtime: 1},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Mtime: 0},
+ }, &RWStat{}},
+ // 10 ok: change mtime by the leader
+ {0, "unko", "glenda", &Stat{Gid: "bell", Mtime: 1},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Gid: "bell", Mtime: 0},
+ }, &RWStat{}},
+ // 11 permission denied: change mtime by a non-leader member
+ {0, "unko", "ken", &Stat{Gid: "bell", Mtime: 0},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Gid: "bel", Mtime: 1},
+ }, &RError{}},
+ // 12 operation not permitted: change directory bit.
+ {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda"},
+ }, &RError{}},
+ // 13 ok: change gid by the owner, also a member of new group.
+ {0, "unko", "glenda", &Stat{Uid: "glenda", Gid: "kessoku"},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Gid: "bell"},
+ }, &RWStat{}},
+ // 14 ok: change gid by the leader, also the leader of new group.
+ {0, "unko", "glenda", &Stat{Uid: "ken", Gid: "glenda"},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "ken", Gid: "bell"},
+ }, &RWStat{}},
+ // 15 permission denied: change gid by the leader, non-member of new group.
+ {0, "unko", "ken", &Stat{Uid: "glenda", Gid: "ken"},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Gid: "kessoku"},
+ }, &RError{}},
+ // 16 permission denied: change gid by the owner, non-member of new group.
+ {0, "unko", "ken", &Stat{Uid: "glenda", Gid: "ken"},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "glenda", Gid: "kessoku"},
+ }, &RError{}},
+ // 17 operation not permitted: change uid
+ {0, "unko", "ken", &Stat{Uid: "glenda"},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Uid: "ken"},
+ }, &RError{}},
+ // 18 ok: change mode and gid.
+ {0, "unko", "glenda", &Stat{Gid: "bell", Mode: 0777},
+ &TWStat{
+ Fid: 0,
+ Stat: &Stat{Gid: "glenda", Mode: 0755},
+ }, &RWStat{}},
+ }
+ c, tc, rc := setupConn(testfs)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ go sWStat(ctx, c, tc)
+ for i, test := range tests {
+ func() {
+ fid, err := c.fPool.add(test.fid)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer c.fPool.delete(test.fid)
+ fid.omode = OREAD
+ f := &testFile{stat: *test.stat}
+ fid.file = f
+ fid.uid = test.uid
+ fid.path = test.path
+ tc <- &request{ifcall: test.ifcall}
+ ofcall := (<-rc).ofcall
+ switch ofcall := ofcall.(type) {
+ case *RWStat:
+ if _, ok := test.want.(*RWStat); !ok {
+ t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v",
+ i, test.want, ofcall)
+ return
+ }
+ if !reflect.DeepEqual(&f.stat, test.ifcall.Stat) {
+ t.Errorf("%d: stat doesn't match:\n\twant: %v\n\tgot: %v",
+ i, test.ifcall.Stat, &f.stat)
+ }
+ case *RError:
+ if _, ok := test.want.(*RError); !ok {
+ t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v",
+ i, test.want, ofcall)
+ }
+ default:
+ t.Fatalf("%d: unexpected message: %v", i, ofcall)
+ }
+ }()
+
+ }
+}