server_test.go (27815B)
1 package lib9p 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "io" 8 "io/fs" 9 "os" 10 "path" 11 "reflect" 12 "testing" 13 ) 14 15 // TestRunListener tests if the listener goroutine receives 9P messages 16 // and send them through the server's listenChan channel. 17 func TestRunListener(t *testing.T) { 18 tFile, err := os.Open("testdata/test_Tmsg.dat") 19 if err != nil { 20 t.Fatalf("open file: %v", err) 21 } 22 defer tFile.Close() 23 tFile2, err := os.Open("testdata/test_Tmsg.dat") 24 if err != nil { 25 t.Fatalf("open file: %v", err) 26 } 27 defer tFile2.Close() 28 c := &conn{ 29 s: &Server{chatty9P: false}, 30 r: tFile, 31 } 32 oldReqPoolAdd := reqPoolAdd 33 defer func() { reqPoolAdd = oldReqPoolAdd }() 34 reqPoolAdd = func(*reqPool, uint16) (*request, error) { return &request{}, nil } 35 ctx, cancel := context.WithCancel(context.Background()) 36 defer cancel() 37 c.runListener(ctx, newReqPool()) 38 for { 39 want, err := RecvMsg(tFile2) 40 if err == io.EOF { 41 break 42 } else if err != nil { 43 t.Fatalf("recvmsg: %v", err) 44 } 45 r := <-c.listenChan 46 if r.listenErr != nil { 47 t.Fatalf("listenErr: %v", r.listenErr) 48 } 49 got := r.ifcall 50 if !reflect.DeepEqual(want, got) { 51 t.Errorf("listener modified message:\n\twant: %v\n\tgot: %v", 52 want, got) 53 } 54 } 55 } 56 57 // TestRunSpeaker tests if the speaker goroutine receives 9P messages 58 // and send them through the server's speakChan channel. 59 func TestRunResponder(t *testing.T) { 60 rFile, err := os.Open("testdata/test_Rmsg.dat") 61 if err != nil { 62 t.Fatalf("open file: %v", err) 63 } 64 defer rFile.Close() 65 r, w := io.Pipe() 66 c := &conn{s: &Server{chatty9P: false}, w: w} 67 rp := newReqPool() 68 ctx, cancel := context.WithCancel(context.Background()) 69 defer cancel() 70 c.runResponder(ctx, rp) 71 for { 72 want, err := readMsg(rFile) 73 if err == io.EOF { 74 break 75 } else if err != nil { 76 t.Fatalf("readmsg: %v", err) 77 } 78 msg, err := unmarshal(want) 79 if err != nil { 80 t.Fatalf("unmarshal %v", err) 81 } 82 c.respChan <- &request{ 83 tag: msg.GetTag(), 84 ofcall: msg, 85 } 86 got := make([]byte, len(want)) 87 _, err = io.ReadFull(r, got) 88 if err != nil { 89 t.Fatalf("readfull: %v", err) 90 } 91 if !bytes.Equal(want, got) { 92 t.Errorf("responder modified message:\n\twant: %v\n\tgot: %v", 93 want, got) 94 } 95 } 96 } 97 98 // TestGetReq tests if getReq returns the same Msg as RecvMsg and 99 // registers the Msg to the ReqPool. 100 func TestGetReq(t *testing.T) { 101 tFile, err := os.Open("testdata/test_Tmsg.dat") 102 if err != nil { 103 t.Fatalf("open file: %v", err) 104 } 105 defer tFile.Close() 106 tFile2, err := os.Open("testdata/test_Tmsg.dat") 107 if err != nil { 108 t.Fatalf("open file: %v", err) 109 } 110 defer tFile2.Close() 111 rp := newReqPool() 112 for { 113 got := getReq(tFile, rp, false) 114 if got.listenErr == io.EOF { 115 break 116 } else if got.listenErr != nil { 117 t.Fatalf("getReq: %v", got.listenErr) 118 } 119 wantMsg, err := RecvMsg(tFile2) 120 if err != nil { 121 t.Fatalf("recvmsg: %v", err) 122 } 123 if got.tag != wantMsg.GetTag() { 124 t.Errorf("r.tag: want: %v, got: %v", wantMsg.GetTag(), got.tag) 125 } 126 if !reflect.DeepEqual(got.ifcall, wantMsg) { 127 t.Errorf("r.ifcall:\n\twant: %v,\n\tgot: %v", wantMsg, got.ifcall) 128 } 129 got2, ok := rp.lookup(wantMsg.GetTag()) 130 if !ok { 131 t.Errorf("request not registered to the pool") 132 } 133 if got != got2 { 134 t.Errorf("wrong message in pool:\n\twant: %p,\n\tgot: %p", got, got2) 135 } 136 rp.delete(wantMsg.GetTag()) 137 } 138 } 139 140 func TestSVersion(t *testing.T) { 141 tests := []struct { 142 input *TVersion 143 want Msg 144 }{ 145 {&TVersion{Msize: 1024, Version: "9P2000"}, 146 &RVersion{Msize: 1024, Version: "9P2000"}}, 147 {&TVersion{Msize: 564, Version: "9P2000"}, 148 &RVersion{Msize: 564, Version: "9P2000"}}, 149 {&TVersion{Msize: 8 * 1024, Version: "9P2000"}, 150 &RVersion{Msize: 1024, Version: "9P2000"}}, 151 {&TVersion{Msize: 1024, Version: "hoge"}, 152 &RVersion{Msize: 1024, Version: "unknown"}}, 153 } 154 c, rc := setupConn(nil) 155 tc := make(chan *request) 156 ctx, cancel := context.WithCancel(context.Background()) 157 defer cancel() 158 go sVersion(ctx, c, tc) 159 for i, test := range tests { 160 oldMsize := c.msize 161 tc <- &request{ifcall: test.input} 162 got := (<-rc).ofcall.(*RVersion) 163 if !reflect.DeepEqual(test.want, got) { 164 t.Errorf("%d: want: %v,\n\tgot: %v", i, test.want, got) 165 } 166 if test.input.Msize < oldMsize && c.msize != test.input.Msize { 167 t.Errorf("%d: msize not changed", i) 168 } 169 if test.input.Msize >= oldMsize && c.msize != oldMsize { 170 t.Errorf("%d: msize changed unexpectedly", i) 171 } 172 c.msize = oldMsize 173 } 174 } 175 176 func TestSAuth(t *testing.T) { 177 tests := []struct { 178 input *TAuth 179 want Msg 180 authFunc func(context.Context, *request) error 181 }{ 182 {&TAuth{Afid: NOFID, Uname: "kenji", Aname: ""}, 183 &RError{Ename: errors.New("authentication not required")}, nil}, 184 {&TAuth{Afid: NOFID, Uname: "kenji", Aname: ""}, 185 &RError{Ename: errors.New("NOFID can't be used for afid")}, 186 func(ctx context.Context, r *request) error { return nil }}, 187 {&TAuth{Afid: 0, Uname: "kenji", Aname: ""}, 188 &RAuth{Tag: 0, Aqid: Qid{0, 1, 2}}, 189 func(ctx context.Context, r *request) error { 190 r.ofcall = &RAuth{Tag: 0, Aqid: Qid{0, 1, 2}} 191 return nil 192 }}, 193 {&TAuth{Afid: 0, Uname: "kenji", Aname: "fs"}, 194 &RError{Ename: errors.New("no such file system")}, 195 func(ctx context.Context, r *request) error { 196 r.ofcall = &RAuth{Tag: 0, Aqid: Qid{0, 1, 2}} 197 return nil 198 }}, 199 } 200 for i, test := range tests { 201 func() { 202 c, rc := setupConn(nil) 203 tc := make(chan *request) 204 c.s.Auth = test.authFunc 205 ctx, cancel := context.WithCancel(context.Background()) 206 defer cancel() 207 go sAuth(ctx, c, tc) 208 tc <- &request{ifcall: test.input} 209 ofcall := (<-rc).ofcall 210 switch wantmsg := test.want.(type) { 211 case *RAuth: 212 gotmsg, ok := ofcall.(*RAuth) 213 if !ok { 214 t.Errorf("%d: unexpected message: %v", i, ofcall) 215 return 216 } 217 if !reflect.DeepEqual(wantmsg, gotmsg) { 218 t.Errorf("%d: want: %v,\n\tgot: %v", i, wantmsg, gotmsg) 219 return 220 } 221 case *RError: 222 _, ok := ofcall.(*RError) 223 if !ok { 224 t.Errorf("%d: unexpected message: %v", i, ofcall) 225 return 226 } 227 default: 228 t.Fatalf("%d: unexpected message: %v", i, wantmsg) 229 } 230 }() 231 } 232 } 233 234 // TestSAttach tests sAttach. 235 // It prepares a conn and adds an AuthFile to the FidPool with fid 0. 236 // It also sets c.fsmap["fs"] to test a Tattach message with Aname other than "". 237 func TestSAttach(t *testing.T) { 238 tests := []struct { 239 input *TAttach 240 needAuth bool 241 authOK bool 242 want Msg 243 }{ 244 {&TAttach{Fid: 0, Afid: NOFID, Uname: "kenji", Aname: ""}, 245 false, false, 246 &RError{}}, // duplicate fid 247 {&TAttach{Fid: 1, Afid: NOFID, Uname: "kenji", Aname: ""}, 248 false, true, 249 &RAttach{}}, // ok 250 {&TAttach{Fid: 2, Afid: NOFID, Uname: "kenji", Aname: ""}, 251 true, false, 252 &RError{}}, // afid is not set 253 {&TAttach{Fid: 2, Afid: 0, Uname: "kenji", Aname: ""}, 254 true, false, 255 &RError{}}, // not authOK 256 {&TAttach{Fid: 2, Afid: 0, Uname: "glenda", Aname: ""}, 257 true, true, 258 &RError{}}, // wrong user 259 {&TAttach{Fid: 2, Afid: 0, Uname: "kenji", Aname: ""}, 260 true, true, 261 &RAttach{}}, // ok 262 {&TAttach{Fid: 3, Afid: 0, Uname: "kenji", Aname: "fs"}, 263 true, true, 264 &RAttach{}}, // ok 265 {&TAttach{Fid: 4, Afid: 0, Uname: "kenji", Aname: "fss"}, 266 true, true, 267 &RError{}}, // no such file system 268 } 269 c, rc := setupConn(testfs) 270 c.s.fsmap["fs"] = testfs 271 tc := make(chan *request) 272 dammyAuth := func(context.Context, *request) error { return nil } 273 af := &AuthFile{ 274 Qid: Qid{Type: QTAUTH}, 275 Uname: "kenji", 276 Aname: "", 277 } 278 fid, err := c.fPool.add(0) 279 if err != nil { 280 t.Fatal(err) 281 } 282 fid.file = af 283 ctx, cancel := context.WithCancel(context.Background()) 284 defer cancel() 285 go sAttach(ctx, c, tc) 286 for i, test := range tests { 287 af.AuthOK = test.authOK 288 af.Aname = test.input.Aname 289 if test.needAuth { 290 c.s.Auth = dammyAuth 291 } else { 292 c.s.Auth = nil 293 } 294 req := &request{ifcall: test.input} 295 tc <- req 296 ofcall := (<-rc).ofcall 297 switch wantmsg := test.want.(type) { 298 case *RAttach: 299 _, ok := ofcall.(*RAttach) 300 if !ok { 301 t.Errorf("%d: unexpected message: %v", i, ofcall) 302 return 303 } 304 case *RError: 305 _, ok := ofcall.(*RError) 306 if !ok { 307 t.Errorf("%d: unexpected message: %v", i, ofcall) 308 return 309 } 310 default: 311 t.Fatalf("%d: unexpected message: %v", i, wantmsg) 312 } 313 } 314 } 315 316 func TestSFlush(t *testing.T) { 317 rp := newReqPool() 318 tests := []struct { 319 input *request 320 }{ 321 {&request{ifcall: &TFlush{}, 322 oldreq: &request{pool: rp, done: make(chan struct{})}}}, 323 } 324 c, rc := setupConn(nil) 325 tc := make(chan *request) 326 ctx, cancel := context.WithCancel(context.Background()) 327 defer cancel() 328 go sFlush(ctx, c, tc) 329 for i, test := range tests { 330 tc <- test.input 331 if gotmsg, ok := (<-rc).ofcall.(*RFlush); !ok { 332 t.Errorf("%d: unexpected message: %v", i, gotmsg) 333 } 334 if _, ok := <-test.input.oldreq.done; ok { 335 t.Errorf("%d: done channel not closed", i) 336 } 337 } 338 } 339 340 func TestSWalk(t *testing.T) { 341 tests := []struct { 342 input Msg 343 wantLen int // len(Ofcall.(*RWalk).Qids) 344 wantErr error 345 }{ 346 {&TWalk{Fid: 0, Newfid: 1, Wnames: []string{"a"}}, 347 1, nil}, 348 {&TWalk{Fid: 0, Newfid: 2, Wnames: []string{"b"}}, 349 1, nil}, 350 {&TWalk{Fid: 0, Newfid: 3, Wnames: []string{"dir", "file"}}, 351 2, nil}, 352 {&TWalk{Fid: 0, Newfid: 4, Wnames: []string{"dir", "hoge"}}, 353 1, nil}, // short walk 354 // 9P document says: 355 // If the first element cant be walked for any reason, 356 // RError is returned. 357 {&TWalk{Fid: 0, Newfid: 5, Wnames: []string{"hoge", "hoge"}}, 358 0, errors.New("not found")}, 359 } 360 c, rc := setupConn(testfs) 361 tc := make(chan *request) 362 fid, err := c.fPool.add(0) 363 if err != nil { 364 t.Fatal(err) 365 } 366 fid.omode = -1 367 fid.path = "." 368 fid.fs = testfs 369 ctx, cancel := context.WithCancel(context.Background()) 370 defer cancel() 371 go sWalk(ctx, c, tc) 372 for i, test := range tests { 373 req := &request{ifcall: test.input} 374 tc <- req 375 ofcall := (<-rc).ofcall 376 if test.wantErr == nil { 377 gotMsg, ok := ofcall.(*RWalk) 378 if !ok { 379 t.Errorf("%d: unexpected message: %v", i, ofcall) 380 continue 381 } 382 if test.wantLen != len(gotMsg.Qids) { 383 t.Errorf("%d: walk length mismatch: want: %d, got: %d", 384 i, test.wantLen, len(gotMsg.Qids)) 385 continue 386 } 387 } else { 388 _, ok := ofcall.(*RError) 389 if !ok { 390 t.Errorf("%d: unexpected message: %v", i, ofcall) 391 continue 392 } 393 // TODO: check error message. 394 } 395 } 396 } 397 398 func TestSOpen(t *testing.T) { 399 tests := []struct { 400 filePath string // file to open. 401 uid string 402 input Msg 403 wantMsg Msg 404 }{ 405 // ok 406 {"a", "glenda", &TOpen{Fid: 0, Mode: OREAD}, &ROpen{}}, 407 // fid not found 408 {"a", "glenda", &TOpen{Fid: 1, Mode: OREAD}, &RError{}}, 409 // permission denied 410 {"b", "glenda", &TOpen{Fid: 0, Mode: OREAD}, &RError{}}, 411 {"b", "ken", &TOpen{Fid: 0, Mode: ORDWR}, &ROpen{}}, 412 {"dir", "glenda", &TOpen{Fid: 0, Mode: OREAD}, &ROpen{}}, 413 // A directory can't be opened with OWRITE. 414 {"dir", "glenda", &TOpen{Fid: 0, Mode: OWRITE}, &RError{}}, 415 } 416 c, rc := setupConn(testfs) 417 tc := make(chan *request) 418 ctx, cancel := context.WithCancel(context.Background()) 419 defer cancel() 420 go sOpen(ctx, c, tc) 421 for i, test := range tests { 422 c.fPool.delete(0) 423 // add the file to open to c.fPool. 424 fid, err := c.fPool.add(0) 425 if err != nil { 426 t.Error(i, err) 427 continue 428 } 429 fid.omode = -1 430 fid.path = test.filePath 431 fid.uid = test.uid 432 fid.fs = testfs 433 f, err := testfs.walkPath(test.filePath) 434 if err != nil { 435 t.Error(i, err) 436 continue 437 } 438 fid.file = f 439 fid.qidpath = f.stat.Qid.Path 440 tc <- &request{ifcall: test.input} 441 ofcall := (<-rc).ofcall 442 switch test.wantMsg.(type) { 443 case *ROpen: 444 if _, ok := ofcall.(*ROpen); !ok { 445 t.Errorf("%d: unexpected message: %v", i, ofcall) 446 } 447 case *RError: 448 if _, ok := ofcall.(*RError); !ok { 449 t.Errorf("%d: unexpected message: %v", i, ofcall) 450 } 451 default: 452 t.Errorf("%d: unexpected message: %v", i, ofcall) 453 } 454 } 455 } 456 457 func TestSCreate(t *testing.T) { 458 tests := []struct { 459 dir string // pathname in which a file is to be created. 460 uid string 461 ifcall *TCreate 462 want Msg 463 }{ 464 {".", "glenda", 465 &TCreate{Name: "test", Perm: 0777, Mode: OREAD}, 466 &RCreate{}}, 467 {"dir", "glenda", 468 &TCreate{Name: "test", Perm: 0777, Mode: OREAD}, 469 &RError{Ename: ErrPerm}}, 470 {"dir", "rob", 471 &TCreate{Name: "test", Perm: 0777, Mode: OREAD}, 472 &RCreate{}}, 473 } 474 c, rc := setupConn(testfs) 475 tc := make(chan *request) 476 ctx, cancel := context.WithCancel(context.Background()) 477 defer cancel() 478 go sCreate(ctx, c, tc) 479 for i, test := range tests { 480 c.fPool.delete(0) 481 fid, err := c.fPool.add(0) 482 if err != nil { 483 t.Error(i, err) 484 continue 485 } 486 f, err := testfs.walkPath(test.dir) 487 if err != nil { 488 t.Error(i, err) 489 continue 490 } 491 fid.file = f 492 fid.omode = -1 493 fid.path = test.dir 494 fid.uid = test.uid 495 fid.fs = testfs 496 tc <- &request{ifcall: test.ifcall} 497 ofcall := (<-rc).ofcall 498 var qid Qid 499 switch test.want.(type) { 500 case *RCreate: 501 got, ok := ofcall.(*RCreate) 502 if !ok { 503 t.Errorf("%d:\n\twant: %v\n\tgot: %v", i, test.want, ofcall) 504 continue 505 } 506 qid = got.Qid 507 case *RError: 508 if _, ok := ofcall.(*RError); !ok { 509 t.Errorf("%d:\n\twant: %v\n\tgot: %v", i, test.want, ofcall) 510 continue 511 } 512 continue 513 default: 514 t.Errorf("%d: unexpected message: %v", i, test.want) 515 continue 516 } 517 fname := path.Join(test.dir, test.ifcall.Name) 518 ff, err := testfs.walkPath(fname) 519 if err != nil { 520 t.Error(i, err) 521 goto remove 522 } 523 if !reflect.DeepEqual(ff.stat.Qid, qid) { 524 t.Errorf("%d: qid mismatch: want: %v, got: %v", i, ff.stat.Qid, qid) 525 goto remove 526 } 527 remove: 528 dd, err := testfs.walkPath(test.dir) 529 if err != nil { 530 t.Fatal(i, err) 531 continue 532 } 533 for i, c := range dd.children { 534 if c.stat.Name == test.ifcall.Name { 535 dd.children = append(dd.children[:i], dd.children[i+1:]...) 536 break 537 } 538 } 539 } 540 } 541 542 func TestSRead(t *testing.T) { 543 c, rc := setupConn(testfs) 544 tc := make(chan *request) 545 ctx, cancel := context.WithCancel(context.Background()) 546 defer cancel() 547 go sRead(ctx, c, tc) 548 testSRead(t, ".", c, tc, rc) 549 } 550 551 func testSRead(t *testing.T, pathname string, c *conn, tc, rc chan *request) { 552 ff, err := testfs.OpenFile(pathname, O_RDONLY) 553 if err != nil { 554 t.Fatal(err) 555 } 556 defer ff.Close() 557 f := ff.(*testFile) 558 fi, err := f.Stat() 559 if err != nil { 560 t.Fatal(err) 561 } 562 if fi.IsDir() { 563 fid, err := c.fPool.add(0) 564 if err != nil { 565 t.Fatal(err) 566 } 567 fid.omode = OREAD 568 fid.file = f 569 fid.path = pathname 570 tc <- &request{ifcall: &TRead{Fid: 0, Offset: 0, Count: 1000}} 571 ofcall := (<-rc).ofcall 572 rread, ok := ofcall.(*RRead) 573 if !ok { 574 t.Errorf("unexpected message: %v", ofcall) 575 return 576 } 577 c.fPool.delete(0) 578 b := rread.Data 579 cstats := make(map[string]*Stat, len(f.children)) 580 for len(b) != 0 { 581 st := NewStat(b) 582 b = b[2+st.Size():] 583 cstats[st.Name] = st 584 } 585 for _, child := range f.children { 586 if !reflect.DeepEqual(cstats[child.stat.Name], &child.stat) { 587 t.Errorf("readdir returned wrong stat:\n\twant: %v\n\tgot: %v", 588 child.stat, cstats[child.stat.Name]) 589 } 590 delete(cstats, child.stat.Name) 591 cpath := path.Join(pathname, child.stat.Name) 592 testSRead(t, cpath, c, tc, rc) 593 } 594 if len(cstats) != 0 { 595 t.Errorf("readdir returned unexistent stats: %v", cstats) 596 } 597 } else { 598 fid, err := c.fPool.add(0) 599 defer c.fPool.delete(0) 600 if err != nil { 601 t.Fatal(err) 602 } 603 fid.omode = OREAD 604 fid.file = f 605 fid.path = pathname 606 tc <- &request{ifcall: &TRead{Fid: 0, Offset: 0, Count: 1000}} 607 ofcall := (<-rc).ofcall 608 rread, ok := ofcall.(*RRead) 609 if !ok { 610 t.Errorf("unexpected message: %v", ofcall) 611 return 612 } 613 if !bytes.Equal(rread.Data, f.content) { 614 t.Errorf("content mismatch: want: %v, got: %v", f.content, rread.Data) 615 return 616 } 617 } 618 } 619 620 func TestSWrite(t *testing.T) { 621 tests := []struct { 622 omode OpenMode 623 data []byte 624 err error 625 }{ 626 {ORDWR, []byte("hoge"), nil}, 627 {OREAD, []byte("hoge"), errors.New("omode")}, 628 } 629 c, rc := setupConn(testfs) 630 tc := make(chan *request) 631 ctx, cancel := context.WithCancel(context.Background()) 632 defer cancel() 633 go sWrite(ctx, c, tc) 634 for i, test := range tests { 635 func() { 636 c.fPool.delete(0) 637 fid, err := c.fPool.add(0) 638 if err != nil { 639 t.Error(err) 640 return 641 } 642 fid.omode = test.omode 643 f := &testFile{fsys: testfs} 644 fid.file = f 645 tc <- &request{ifcall: &TWrite{Count: uint32(len(test.data)), Data: test.data}} 646 ofcall := (<-rc).ofcall 647 switch ofcall := ofcall.(type) { 648 case *RWrite: 649 if test.err != nil { 650 t.Errorf("%d: unexpected message: %v", i, ofcall) 651 return 652 } 653 if !bytes.Equal(f.content, test.data) { 654 t.Errorf("%d: content mismatch: want: %v, got: %v", 655 i, test.data, f.content) 656 } 657 case *RError: 658 if test.err == nil { 659 t.Errorf("%d: unexpected err: %v", i, ofcall.Ename) 660 } 661 default: 662 t.Errorf("%d: unexpected message: %v", i, ofcall) 663 } 664 }() 665 } 666 } 667 668 func TestSClunk(t *testing.T) { 669 tests := []struct { 670 fid uint32 671 ifcall *TClunk 672 want Msg 673 }{ 674 {0, &TClunk{Fid: 0}, &RClunk{}}, 675 {0, &TClunk{Fid: 1}, &RError{}}, 676 {1, &TClunk{Fid: 1}, &RClunk{}}, 677 } 678 c, rc := setupConn(testfs) 679 tc := make(chan *request) 680 ctx, cancel := context.WithCancel(context.Background()) 681 defer cancel() 682 go sClunk(ctx, c, tc) 683 for i, test := range tests { 684 func() { 685 _, err := c.fPool.add(test.fid) 686 if err != nil { 687 t.Error(err) 688 return 689 } 690 defer c.fPool.delete(test.fid) 691 tc <- &request{ifcall: test.ifcall} 692 ofcall := (<-rc).ofcall 693 switch ofcall := ofcall.(type) { 694 case *RClunk: 695 if _, ok := test.want.(*RClunk); !ok { 696 t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", 697 i, test.want, ofcall) 698 return 699 } 700 if _, ok := c.fPool.lookup(test.fid); ok { 701 if test.fid == test.ifcall.Fid { 702 t.Errorf("%d: fid not flushed", i) 703 } 704 } else { 705 if test.fid != test.ifcall.Fid { 706 t.Errorf("%d: wrong fid flushed", i) 707 } 708 } 709 case *RError: 710 if _, ok := test.want.(*RError); !ok { 711 t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", 712 i, test.want, ofcall) 713 return 714 } 715 default: 716 t.Errorf("%d: unexpected message %v", i, ofcall) 717 } 718 }() 719 } 720 } 721 722 // TestORCLOSE tests whether a file created with ORCLOSE bit set is correctly 723 // removed when clunked. 724 func TestORCLOSE(t *testing.T) { 725 c, rc := setupConn(testfs) 726 tc0, tc1 := make(chan *request), make(chan *request) 727 ctx, cancel := context.WithCancel(context.Background()) 728 defer cancel() 729 go sCreate(ctx, c, tc0) 730 go sClunk(ctx, c, tc1) 731 fid, err := c.fPool.add(0) 732 if err != nil { 733 t.Fatal(err) 734 } 735 fid.path = "." 736 fid.fs = testfs 737 fid.uid = "glenda" 738 tc0 <- &request{ 739 ifcall: &TCreate{Fid: 0, Name: "tmp", Perm: 0777, Mode: ORCLOSE | OREAD}, 740 } 741 ofcall := (<-rc).ofcall 742 if rerr, ok := ofcall.(*RError); ok { 743 t.Fatal(rerr.Ename) 744 } 745 if _, err = testfs.walkPath("tmp"); err != nil { 746 t.Fatal(err) 747 } 748 tc1 <- &request{ifcall: &TClunk{Fid: 0}} 749 ofcall = (<-rc).ofcall 750 if rerr, ok := ofcall.(*RError); ok { 751 t.Fatal(rerr.Ename) 752 } 753 if _, err = testfs.walkPath("tmp"); err == nil { 754 t.Fatal("file not removed after closing it") 755 } 756 } 757 758 func TestSRemove(t *testing.T) { 759 tests := []struct { 760 fid uint32 761 pathname string 762 fuid string // uid of the file to be removed. 763 mode OpenMode 764 perm FileMode 765 ruid string // uid of the user who evoke remove. 766 ifcall *TRemove 767 want Msg 768 }{ 769 // ok. 770 {0, "hoge", "glenda", OREAD, 0777, "glenda", &TRemove{Fid: 0}, &RRemove{}}, 771 // unknown fid. 772 {0, "hoge", "glenda", OREAD, 0777, "glenda", &TRemove{Fid: 1}, &RError{}}, 773 // permission denied. 774 {0, "hoge", "glenda", OREAD, 0777, "kenji", &TRemove{Fid: 0}, &RError{}}, 775 // ok. 776 {0, "dir/hoge", "glenda", OREAD, 0000, "rob", &TRemove{Fid: 0}, &RRemove{}}, 777 // permission denied. 778 {0, "dir/hoge", "glenda", OREAD, 0777, "glenda", &TRemove{Fid: 0}, &RError{}}, 779 } 780 c, rc := setupConn(testfs) 781 tc := make(chan *request) 782 ctx, cancel := context.WithCancel(context.Background()) 783 defer cancel() 784 go sRemove(ctx, c, tc) 785 for i, test := range tests { 786 func() { 787 fid, err := c.fPool.add(test.fid) 788 if err != nil { 789 t.Error(i, err) 790 return 791 } 792 defer c.fPool.delete(test.fid) 793 fid.omode = -1 794 fid.path = test.pathname 795 fid.file = &testFile{stat: Stat{Name: path.Base(test.pathname)}} 796 fid.uid = test.ruid 797 fid.fs = testfs 798 dir, err := testfs.walkPath(path.Dir(test.pathname)) 799 if err != nil { 800 t.Error(i, err) 801 return 802 } 803 dir.children = append(dir.children, fid.file.(*testFile)) 804 defer func() { 805 if 0 < len(dir.children) && 806 dir.children[len(dir.children)-1].stat.Name == 807 path.Base(test.pathname) { 808 dir.children = dir.children[:len(dir.children)-1] 809 } 810 }() 811 tc <- &request{ifcall: test.ifcall} 812 ofcall := (<-rc).ofcall 813 switch ofcall := ofcall.(type) { 814 case *RRemove: 815 if _, ok := test.want.(*RRemove); !ok { 816 t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", 817 i, test.want, ofcall) 818 return 819 } 820 if _, err := testfs.OpenFile(test.pathname, O_RDONLY); err == nil { 821 t.Errorf("%d: file not removed", i) 822 testfs.Remove(test.pathname) 823 return 824 } 825 case *RError: 826 defer testfs.Remove(test.pathname) 827 if _, ok := test.want.(*RError); !ok { 828 t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", 829 i, test.want, ofcall) 830 return 831 } 832 default: 833 t.Fatalf("%d: unexpected message: %v", i, ofcall) 834 } 835 }() 836 } 837 } 838 839 func TestSStat(t *testing.T) { 840 c, rc := setupConn(testfs) 841 tc := make(chan *request) 842 ctx, cancel := context.WithCancel(context.Background()) 843 defer cancel() 844 go sStat(ctx, c, tc) 845 testSStat(t, ".", c, tc, rc) 846 } 847 848 func testSStat(t *testing.T, pathname string, c *conn, tc, rc chan *request) { 849 f, err := testfs.walkPath(pathname) 850 if err != nil { 851 t.Fatal(err) 852 } 853 st := f.stat 854 fid, err := c.fPool.add(0) 855 if err != nil { 856 t.Fatal(err) 857 } 858 fid.omode = -1 859 fid.path = pathname 860 fid.file = f 861 fid.fs = testfs 862 tc <- &request{ifcall: &TStat{Fid: 0}} 863 ofcall := (<-rc).ofcall 864 c.fPool.delete(0) 865 switch ofcall := ofcall.(type) { 866 case *RStat: 867 if !reflect.DeepEqual(&st, ofcall.Stat) { 868 t.Errorf("stat doesn't match:\n\twant: %v\n\tgot: %v", &st, ofcall.Stat) 869 } 870 case *RError: 871 t.Errorf("unexpected message: %v", ofcall) 872 default: 873 t.Fatalf("unexpected message: %v", ofcall) 874 } 875 for _, child := range f.children { 876 cpath := path.Join(pathname, child.stat.Name) 877 testSStat(t, cpath, c, tc, rc) 878 } 879 } 880 881 func TestSWstat(t *testing.T) { 882 tests := []struct { 883 fid uint32 884 path string 885 uid string 886 stat *Stat 887 ifcall *TWstat 888 want Msg 889 }{ 890 // 0 ok: rename by owner of the directory. 891 {0, "./d/hoge", "glenda", &Stat{Name: "hoge"}, 892 &TWstat{ 893 Fid: 0, 894 Stat: &Stat{Name: "oppai"}, 895 }, &RWstat{}}, 896 // 1 ok: rename by group member of the directory. 897 {0, "./d/hoge", "ken", &Stat{Name: "hoge"}, 898 &TWstat{ 899 Fid: 0, 900 Stat: &Stat{Name: "oppai"}, 901 }, &RWstat{}}, 902 // 2 permission denied: rename by other. 903 {0, "./d/hoge", "kenji", &Stat{Name: "hoge"}, 904 &TWstat{ 905 Fid: 0, 906 Stat: &Stat{Name: "oppai"}, 907 }, &RError{Ename: ErrPerm}}, 908 // 3 ok: length change by owner with write permission 909 {0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: 0700, Length: 10}, 910 &TWstat{ 911 Fid: 0, 912 Stat: &Stat{Uid: "glenda", Mode: 0700, Length: 20}, 913 }, &RWstat{}}, 914 // 4 permission denied: length change by owner without write permission 915 {0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: 0000, Length: 10}, 916 &TWstat{ 917 Fid: 0, 918 Stat: &Stat{Uid: "glenda", Mode: 0700, Length: 20}, 919 }, &RError{Ename: ErrPerm}}, 920 // 5 operation not permitted: length change of a directory to other than 0 921 {0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 10}, 922 &TWstat{ 923 Fid: 0, 924 Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 20}, 925 }, &RError{}}, 926 /* 927 // TODO: change directory length to 0. 928 {0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 10}, 929 &TWstat{ 930 Fid: 0, 931 Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length:0}, 932 }, &RWstat{}}, 933 */ 934 // 6 ok: change mode by owner 935 {0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700}, 936 &TWstat{ 937 Fid: 0, 938 Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0755}, 939 }, &RWstat{}}, 940 // 7 ok: change mode by the leader 941 {0, "hoge", "glenda", &Stat{Gid: "bell", Mode: fs.ModeDir | 0700}, 942 &TWstat{ 943 Fid: 0, 944 Stat: &Stat{Gid: "glenda", Mode: fs.ModeDir | 0755}, 945 }, &RWstat{}}, 946 // 8 permission denied: change mode by a non-leader member 947 {0, "hoge", "ken", &Stat{Gid: "bell", Mode: fs.ModeDir | 0700}, 948 &TWstat{ 949 Fid: 0, 950 Stat: &Stat{Gid: "glenda", Mode: fs.ModeDir | 0755}, 951 }, &RError{}}, 952 // 9 ok: change mtime by owner 953 {0, "hoge", "glenda", &Stat{Uid: "glenda", Mtime: 1}, 954 &TWstat{ 955 Fid: 0, 956 Stat: &Stat{Uid: "glenda", Mtime: 0}, 957 }, &RWstat{}}, 958 // 10 ok: change mtime by the leader 959 {0, "hoge", "glenda", &Stat{Gid: "bell", Mtime: 1}, 960 &TWstat{ 961 Fid: 0, 962 Stat: &Stat{Gid: "bell", Mtime: 0}, 963 }, &RWstat{}}, 964 // 11 permission denied: change mtime by a non-leader member 965 {0, "hoge", "ken", &Stat{Gid: "bell", Mtime: 0}, 966 &TWstat{ 967 Fid: 0, 968 Stat: &Stat{Gid: "bel", Mtime: 1}, 969 }, &RError{}}, 970 // 12 operation not permitted: change directory bit. 971 {0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir}, 972 &TWstat{ 973 Fid: 0, 974 Stat: &Stat{Uid: "glenda"}, 975 }, &RError{}}, 976 // 13 ok: change gid by the owner, also a member of new group. 977 {0, "hoge", "glenda", &Stat{Uid: "glenda", Gid: "kessoku"}, 978 &TWstat{ 979 Fid: 0, 980 Stat: &Stat{Uid: "glenda", Gid: "bell"}, 981 }, &RWstat{}}, 982 // 14 ok: change gid by the leader, also the leader of new group. 983 {0, "hoge", "glenda", &Stat{Uid: "ken", Gid: "glenda"}, 984 &TWstat{ 985 Fid: 0, 986 Stat: &Stat{Uid: "ken", Gid: "bell"}, 987 }, &RWstat{}}, 988 // 15 permission denied: change gid by the leader, non-member of new group. 989 {0, "hoge", "ken", &Stat{Uid: "glenda", Gid: "ken"}, 990 &TWstat{ 991 Fid: 0, 992 Stat: &Stat{Uid: "glenda", Gid: "kessoku"}, 993 }, &RError{}}, 994 // 16 permission denied: change gid by the owner, non-member of new group. 995 {0, "hoge", "ken", &Stat{Uid: "glenda", Gid: "ken"}, 996 &TWstat{ 997 Fid: 0, 998 Stat: &Stat{Uid: "glenda", Gid: "kessoku"}, 999 }, &RError{}}, 1000 // 17 operation not permitted: change uid 1001 {0, "hoge", "ken", &Stat{Uid: "glenda"}, 1002 &TWstat{ 1003 Fid: 0, 1004 Stat: &Stat{Uid: "ken"}, 1005 }, &RError{}}, 1006 // 18 ok: change mode and gid. 1007 {0, "hoge", "glenda", &Stat{Gid: "bell", Mode: 0777}, 1008 &TWstat{ 1009 Fid: 0, 1010 Stat: &Stat{Gid: "glenda", Mode: 0755}, 1011 }, &RWstat{}}, 1012 } 1013 c, rc := setupConn(testfs) 1014 tc := make(chan *request) 1015 ctx, cancel := context.WithCancel(context.Background()) 1016 defer cancel() 1017 go sWStat(ctx, c, tc) 1018 for i, test := range tests { 1019 func() { 1020 fid, err := c.fPool.add(test.fid) 1021 if err != nil { 1022 t.Error(err) 1023 return 1024 } 1025 defer c.fPool.delete(test.fid) 1026 fid.omode = OREAD 1027 f := &testFile{stat: *test.stat} 1028 fid.file = f 1029 fid.uid = test.uid 1030 fid.path = test.path 1031 fid.fs = testfs 1032 tc <- &request{ifcall: test.ifcall} 1033 ofcall := (<-rc).ofcall 1034 switch ofcall := ofcall.(type) { 1035 case *RWstat: 1036 if _, ok := test.want.(*RWstat); !ok { 1037 t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", 1038 i, test.want, ofcall) 1039 return 1040 } 1041 if !reflect.DeepEqual(&f.stat, test.ifcall.Stat) { 1042 t.Errorf("%d: stat doesn't match:\n\twant: %v\n\tgot: %v", 1043 i, test.ifcall.Stat, &f.stat) 1044 } 1045 case *RError: 1046 if _, ok := test.want.(*RError); !ok { 1047 t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", 1048 i, test.want, ofcall) 1049 } 1050 default: 1051 t.Fatalf("%d: unexpected message: %v", i, ofcall) 1052 } 1053 }() 1054 1055 } 1056 }