github.com/vmware/govmomi@v0.37.1/toolbox/command_test.go (about) 1 /* 2 Copyright (c) 2017 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package toolbox 18 19 import ( 20 "bytes" 21 "context" 22 "encoding" 23 "encoding/binary" 24 "errors" 25 "fmt" 26 "io" 27 "os" 28 "path/filepath" 29 "runtime" 30 "strconv" 31 "strings" 32 "testing" 33 "time" 34 35 "github.com/vmware/govmomi/toolbox/hgfs" 36 "github.com/vmware/govmomi/toolbox/process" 37 "github.com/vmware/govmomi/toolbox/vix" 38 ) 39 40 type CommandClient struct { 41 Service *Service 42 Header *vix.CommandRequestHeader 43 creds []byte 44 } 45 46 func NewCommandClient() *CommandClient { 47 Trace = testing.Verbose() 48 hgfs.Trace = Trace 49 50 creds, _ := (&vix.UserCredentialNamePassword{ 51 Name: "user", 52 Password: "pass", 53 }).MarshalBinary() 54 55 header := new(vix.CommandRequestHeader) 56 header.Magic = vix.CommandMagicWord 57 58 header.UserCredentialType = vix.UserCredentialTypeNamePassword 59 header.CredentialLength = uint32(len(creds)) 60 61 in := new(mockChannelIn) 62 out := new(mockChannelOut) 63 64 return &CommandClient{ 65 creds: creds, 66 Header: header, 67 Service: NewService(in, out), 68 } 69 } 70 71 func (c *CommandClient) Request(op uint32, m encoding.BinaryMarshaler) []byte { 72 b, err := m.MarshalBinary() 73 if err != nil { 74 panic(err) 75 } 76 77 c.Header.OpCode = op 78 c.Header.BodyLength = uint32(len(b)) 79 80 var buf bytes.Buffer 81 _, _ = buf.Write([]byte("\"reqname\"\x00")) 82 _ = binary.Write(&buf, binary.LittleEndian, c.Header) 83 84 _, _ = buf.Write(b) 85 86 data := append(buf.Bytes(), c.creds...) 87 reply, err := c.Service.Command.Dispatch(data) 88 if err != nil { 89 panic(err) 90 } 91 92 return reply 93 } 94 95 func vixRC(buf []byte) int { 96 args := bytes.SplitN(buf, []byte{' '}, 2) 97 rc, err := strconv.Atoi(string(args[0])) 98 if err != nil { 99 panic(err) 100 } 101 return rc 102 } 103 104 func TestVixRelayedCommandHandler(t *testing.T) { 105 Trace = true 106 if !testing.Verbose() { 107 // cover Trace paths but discard output 108 traceLog = io.Discard 109 } 110 111 in := new(mockChannelIn) 112 out := new(mockChannelOut) 113 114 service := NewService(in, out) 115 116 cmd := service.Command 117 118 msg := []byte("\"reqname\"\x00") 119 120 _, err := cmd.Dispatch(msg) // io.EOF 121 if err == nil { 122 t.Fatal("expected error") 123 } 124 125 header := new(vix.CommandRequestHeader) 126 127 marshal := func(m ...encoding.BinaryMarshaler) []byte { 128 var buf bytes.Buffer 129 _, _ = buf.Write(msg) 130 _ = binary.Write(&buf, binary.LittleEndian, header) 131 132 for _, e := range m { 133 b, err := e.MarshalBinary() 134 if err != nil { 135 panic(err) 136 } 137 _, _ = buf.Write(b) 138 } 139 140 return buf.Bytes() 141 } 142 143 // header.Magic not set 144 reply, _ := cmd.Dispatch(marshal()) 145 rc := vixRC(reply) 146 if rc != vix.InvalidMessageHeader { 147 t.Fatalf("%q", reply) 148 } 149 150 // header.OpCode not set 151 header.Magic = vix.CommandMagicWord 152 reply, _ = cmd.Dispatch(marshal()) 153 rc = vixRC(reply) 154 if rc != vix.UnrecognizedCommandInGuest { 155 t.Fatalf("%q", reply) 156 } 157 158 // valid request for GetToolsState 159 header.OpCode = vix.CommandGetToolsState 160 reply, _ = cmd.Dispatch(marshal()) 161 rc = vixRC(reply) 162 if rc != vix.OK { 163 t.Fatalf("%q", reply) 164 } 165 166 // header.UserCredentialType not set 167 header.OpCode = vix.CommandStartProgram 168 request := new(vix.StartProgramRequest) 169 reply, _ = cmd.Dispatch(marshal()) 170 rc = vixRC(reply) 171 if rc != vix.AuthenticationFail { 172 t.Fatalf("%q", reply) 173 } 174 175 creds, _ := (&vix.UserCredentialNamePassword{ 176 Name: "user", 177 Password: "pass", 178 }).MarshalBinary() 179 180 header.BodyLength = uint32(binary.Size(request.Body)) 181 header.UserCredentialType = vix.UserCredentialTypeNamePassword 182 header.CredentialLength = uint32(len(creds)) 183 184 // ProgramPath not set 185 buf := append(marshal(request), creds...) 186 reply, _ = cmd.Dispatch(buf) 187 rc = vixRC(reply) 188 if rc != vix.FileNotFound { 189 t.Fatalf("%q", reply) 190 } 191 192 cmd.ProcessStartCommand = func(pm *process.Manager, r *vix.StartProgramRequest) (int64, error) { 193 return -1, nil 194 } 195 196 // valid request for StartProgram 197 buf = append(marshal(request), creds...) 198 reply, _ = cmd.Dispatch(buf) 199 rc = vixRC(reply) 200 if rc != vix.OK { 201 t.Fatalf("%q", reply) 202 } 203 204 cmd.Authenticate = func(_ vix.CommandRequestHeader, data []byte) error { 205 var c vix.UserCredentialNamePassword 206 if err := c.UnmarshalBinary(data); err != nil { 207 panic(err) 208 } 209 210 return errors.New("you shall not pass") 211 } 212 213 // fail auth with our own handler 214 buf = append(marshal(request), creds...) 215 reply, _ = cmd.Dispatch(buf) 216 rc = vixRC(reply) 217 if rc != vix.AuthenticationFail { 218 t.Fatalf("%q", reply) 219 } 220 221 cmd.Authenticate = nil 222 223 // cause Vix.UserCredentialNamePassword.UnmarshalBinary to error 224 // first by EOF reading header, second in base64 decode 225 for _, l := range []uint32{1, 10} { 226 header.CredentialLength = l 227 buf = append(marshal(request), creds...) 228 reply, _ = cmd.Dispatch(buf) 229 rc = vixRC(reply) 230 if rc != vix.AuthenticationFail { 231 t.Fatalf("%q", reply) 232 } 233 } 234 } 235 236 // cover misc error paths 237 func TestVixCommandErrors(t *testing.T) { 238 r := new(vix.StartProgramRequest) 239 err := r.UnmarshalBinary(nil) 240 if err == nil { 241 t.Error("expected error") 242 } 243 244 r.Body.NumEnvVars = 1 245 buf, _ := r.MarshalBinary() 246 err = r.UnmarshalBinary(buf) 247 if err == nil { 248 t.Error("expected error") 249 } 250 251 c := new(CommandServer) 252 _, err = c.StartCommand(r.CommandRequestHeader, nil) 253 if err == nil { 254 t.Error("expected error") 255 } 256 } 257 258 func TestVixInitiateDirTransfer(t *testing.T) { 259 c := NewCommandClient() 260 261 dir := os.TempDir() 262 263 for _, enable := range []bool{true, false} { 264 expect := vix.NotAFile 265 if enable { 266 expect = vix.OK 267 } else { 268 // validate we behave as open-vm-tools does when the directory archive feature is disabled 269 c.Service.Command.FileServer.RegisterFileHandler(hgfs.ArchiveScheme, nil) 270 } 271 272 fromGuest := &vix.ListFilesRequest{GuestPathName: dir} 273 toGuest := &vix.InitiateFileTransferToGuestRequest{GuestPathName: dir} 274 toGuest.Body.Overwrite = true 275 276 tests := []struct { 277 op uint32 278 request encoding.BinaryMarshaler 279 }{ 280 {vix.CommandInitiateFileTransferFromGuest, fromGuest}, 281 {vix.CommandInitiateFileTransferToGuest, toGuest}, 282 } 283 284 for _, test := range tests { 285 reply := c.Request(test.op, test.request) 286 287 rc := vixRC(reply) 288 289 if rc != expect { 290 t.Errorf("rc=%d", rc) 291 } 292 } 293 } 294 } 295 296 func TestVixInitiateFileTransfer(t *testing.T) { 297 c := NewCommandClient() 298 299 request := new(vix.ListFilesRequest) 300 301 f, err := os.CreateTemp("", "toolbox") 302 if err != nil { 303 t.Fatal(err) 304 } 305 306 for _, s := range []string{"a", "b", "c", "d", "e"} { 307 _, _ = f.WriteString(strings.Repeat(s, 40)) 308 } 309 310 _ = f.Close() 311 312 name := f.Name() 313 314 // 1st pass file exists == OK, 2nd pass does not exist == FAIL 315 for _, fail := range []bool{false, true} { 316 request.GuestPathName = name 317 318 reply := c.Request(vix.CommandInitiateFileTransferFromGuest, request) 319 320 rc := vixRC(reply) 321 322 if Trace { 323 fmt.Fprintf(os.Stderr, "%s: %s\n", name, string(reply)) 324 } 325 326 if fail { 327 if rc == vix.OK { 328 t.Errorf("%s: %d", name, rc) 329 } 330 } else { 331 if rc != vix.OK { 332 t.Errorf("%s: %d", name, rc) 333 } 334 335 err = os.Remove(name) 336 if err != nil { 337 t.Error(err) 338 } 339 } 340 } 341 } 342 343 func TestVixInitiateFileTransferWrite(t *testing.T) { 344 c := NewCommandClient() 345 346 request := new(vix.InitiateFileTransferToGuestRequest) 347 348 f, err := os.CreateTemp("", "toolbox") 349 if err != nil { 350 t.Fatal(err) 351 } 352 353 _ = f.Close() 354 355 name := f.Name() 356 357 tests := []struct { 358 force bool 359 fail bool 360 }{ 361 {false, true}, // exists == FAIL 362 {true, false}, // exists, but overwrite == OK 363 {false, false}, // does not exist == OK 364 } 365 366 for i, test := range tests { 367 request.GuestPathName = name 368 request.Body.Overwrite = test.force 369 370 reply := c.Request(vix.CommandInitiateFileTransferToGuest, request) 371 372 rc := vixRC(reply) 373 374 if Trace { 375 fmt.Fprintf(os.Stderr, "%s: %s\n", name, string(reply)) 376 } 377 378 if test.fail { 379 if rc == vix.OK { 380 t.Errorf("%d: %d", i, rc) 381 } 382 } else { 383 if rc != vix.OK { 384 t.Errorf("%d: %d", i, rc) 385 } 386 if test.force { 387 _ = os.Remove(name) 388 } 389 } 390 } 391 } 392 393 func TestVixProcessHgfsPacket(t *testing.T) { 394 c := NewCommandClient() 395 396 c.Header.CommonFlags = vix.CommandGuestReturnsBinary 397 398 request := new(vix.CommandHgfsSendPacket) 399 400 op := new(hgfs.RequestCreateSessionV4) 401 packet := new(hgfs.Packet) 402 packet.Payload, _ = op.MarshalBinary() 403 packet.Header.Version = hgfs.HeaderVersion 404 packet.Header.Dummy = hgfs.OpNewHeader 405 packet.Header.HeaderSize = uint32(binary.Size(&packet.Header)) 406 packet.Header.PacketSize = packet.Header.HeaderSize + uint32(len(packet.Payload)) 407 packet.Header.Op = hgfs.OpCreateSessionV4 408 409 request.Packet, _ = packet.MarshalBinary() 410 request.Body.PacketSize = uint32(len(request.Packet)) 411 412 reply := c.Request(vix.HgfsSendPacketCommand, request) 413 414 rc := vixRC(reply) 415 if rc != vix.OK { 416 t.Fatalf("rc: %d", rc) 417 } 418 419 ix := bytes.IndexByte(reply, '#') 420 reply = reply[ix+1:] 421 err := packet.UnmarshalBinary(reply) 422 if err != nil { 423 t.Fatal(err) 424 } 425 426 if packet.Status != hgfs.StatusSuccess { 427 t.Errorf("status=%d", packet.Status) 428 } 429 430 if packet.Dummy != hgfs.OpNewHeader { 431 t.Errorf("dummy=%d", packet.Dummy) 432 } 433 434 session := new(hgfs.ReplyCreateSessionV4) 435 err = session.UnmarshalBinary(packet.Payload) 436 if err != nil { 437 t.Fatal(err) 438 } 439 440 if session.NumCapabilities == 0 || int(session.NumCapabilities) != len(session.Capabilities) { 441 t.Errorf("NumCapabilities=%d", session.NumCapabilities) 442 } 443 } 444 445 func TestVixListProcessesEx(t *testing.T) { 446 c := NewCommandClient() 447 448 c.Service.Command.ProcessStartCommand = func(pm *process.Manager, r *vix.StartProgramRequest) (int64, error) { 449 var p *process.Process 450 switch r.ProgramPath { 451 case "foo": 452 p = process.NewFunc(func(ctx context.Context, arg string) error { 453 return nil 454 }) 455 default: 456 return -1, os.ErrNotExist 457 } 458 459 return pm.Start(r, p) 460 } 461 462 exec := &vix.StartProgramRequest{ 463 ProgramPath: "foo", 464 } 465 466 reply := c.Request(vix.CommandStartProgram, exec) 467 rc := vixRC(reply) 468 if rc != vix.OK { 469 t.Fatalf("rc: %d", rc) 470 } 471 472 r := bytes.Trim(bytes.Split(reply, []byte{' '})[2], "\x00") 473 pid, _ := strconv.Atoi(string(r)) 474 475 exec.ProgramPath = "bar" 476 reply = c.Request(vix.CommandStartProgram, exec) 477 rc = vixRC(reply) 478 t.Log(vix.Error(rc).Error()) 479 if rc != vix.FileNotFound { 480 t.Fatalf("rc: %d", rc) 481 } 482 if vix.ErrorCode(os.ErrNotExist) != rc { 483 t.Fatalf("rc: %d", rc) 484 } 485 486 <-time.Tick(time.Millisecond * 100) 487 488 ps := new(vix.ListProcessesRequest) 489 490 ps.Pids = []int64{int64(pid)} 491 492 reply = c.Request(vix.CommandListProcessesEx, ps) 493 rc = vixRC(reply) 494 if rc != vix.OK { 495 t.Fatalf("rc: %d", rc) 496 } 497 498 n := bytes.Count(reply, []byte("<proc>")) 499 if n != len(ps.Pids) { 500 t.Errorf("ps -p %d=%d", pid, n) 501 } 502 503 kill := new(vix.KillProcessRequest) 504 kill.Body.Pid = ps.Pids[0] 505 506 reply = c.Request(vix.CommandTerminateProcess, kill) 507 rc = vixRC(reply) 508 if rc != vix.OK { 509 t.Fatalf("rc: %d", rc) 510 } 511 512 kill.Body.Pid = 33333 513 reply = c.Request(vix.CommandTerminateProcess, kill) 514 rc = vixRC(reply) 515 if rc != vix.NoSuchProcess { 516 t.Fatalf("rc: %d", rc) 517 } 518 } 519 520 func TestVixGetenv(t *testing.T) { 521 c := NewCommandClient() 522 523 env := os.Environ() 524 key := strings.SplitN(env[0], "=", 2)[0] 525 526 tests := []struct { 527 names []string 528 expect int 529 }{ 530 {nil, len(env)}, // all env 531 {[]string{key, "ENOENT"}, 1}, // specific vars, 1 exists 1 does not 532 } 533 534 for i, test := range tests { 535 env := &vix.ReadEnvironmentVariablesRequest{ 536 Names: test.names, 537 } 538 reply := c.Request(vix.CommandReadEnvVariables, env) 539 rc := vixRC(reply) 540 if rc != vix.OK { 541 t.Fatalf("%d) rc: %d", i, rc) 542 } 543 544 num := bytes.Count(reply, []byte("<ev>")) 545 if num != test.expect { 546 t.Errorf("%d) getenv(%v): %d", i, test.names, num) 547 } 548 } 549 } 550 551 func TestVixDirectories(t *testing.T) { 552 c := NewCommandClient() 553 554 mktemp := &vix.CreateTempFileRequest{ 555 FilePrefix: "toolbox-", 556 } 557 558 // mktemp -d 559 reply := c.Request(vix.CommandCreateTemporaryDirectory, mktemp) 560 rc := vixRC(reply) 561 if rc != vix.OK { 562 t.Fatalf("rc: %d", rc) 563 } 564 565 dir := strings.TrimSuffix(string(reply[4:]), "\x00") 566 567 mkdir := &vix.DirRequest{ 568 GuestPathName: dir, 569 } 570 571 // mkdir $dir == EEXIST 572 reply = c.Request(vix.CommandCreateDirectoryEx, mkdir) 573 rc = vixRC(reply) 574 if rc != vix.FileAlreadyExists { 575 t.Fatalf("rc: %d", rc) 576 } 577 578 // mkdir $dir/ok == OK 579 mkdir.GuestPathName = dir + "/ok" 580 reply = c.Request(vix.CommandCreateDirectoryEx, mkdir) 581 rc = vixRC(reply) 582 if rc != vix.OK { 583 t.Fatalf("rc: %d", rc) 584 } 585 586 // rm of a dir should fail, regardless if empty or not 587 reply = c.Request(vix.CommandDeleteGuestFileEx, &vix.FileRequest{ 588 GuestPathName: mkdir.GuestPathName, 589 }) 590 rc = vixRC(reply) 591 if rc != vix.NotAFile { 592 t.Errorf("rc: %d", rc) 593 } 594 595 // rmdir $dir/ok == OK 596 reply = c.Request(vix.CommandDeleteGuestDirectoryEx, mkdir) 597 rc = vixRC(reply) 598 if rc != vix.OK { 599 t.Fatalf("rc: %d", rc) 600 } 601 602 // rmdir $dir/ok == ENOENT 603 reply = c.Request(vix.CommandDeleteGuestDirectoryEx, mkdir) 604 rc = vixRC(reply) 605 if rc != vix.FileNotFound { 606 t.Fatalf("rc: %d", rc) 607 } 608 609 // mkdir $dir/1/2 == ENOENT (parent directory does not exist) 610 mkdir.GuestPathName = dir + "/1/2" 611 reply = c.Request(vix.CommandCreateDirectoryEx, mkdir) 612 rc = vixRC(reply) 613 if rc != vix.FileNotFound { 614 t.Fatalf("rc: %d", rc) 615 } 616 617 // mkdir -p $dir/1/2 == OK 618 mkdir.Body.Recursive = true 619 reply = c.Request(vix.CommandCreateDirectoryEx, mkdir) 620 rc = vixRC(reply) 621 if rc != vix.OK { 622 t.Fatalf("rc: %d", rc) 623 } 624 625 // rmdir $dir == ENOTEMPTY 626 mkdir.GuestPathName = dir 627 mkdir.Body.Recursive = false 628 reply = c.Request(vix.CommandDeleteGuestDirectoryEx, mkdir) 629 rc = vixRC(reply) 630 if rc != vix.DirectoryNotEmpty { 631 t.Fatalf("rc: %d", rc) 632 } 633 634 // rm -rf $dir == OK 635 mkdir.Body.Recursive = true 636 reply = c.Request(vix.CommandDeleteGuestDirectoryEx, mkdir) 637 rc = vixRC(reply) 638 if rc != vix.OK { 639 t.Fatalf("rc: %d", rc) 640 } 641 } 642 643 func TestVixFiles(t *testing.T) { 644 if runtime.GOOS != "linux" { 645 t.Skip("requires Linux") 646 } 647 648 c := NewCommandClient() 649 650 mktemp := &vix.CreateTempFileRequest{ 651 FilePrefix: "toolbox-", 652 } 653 654 // mktemp -d 655 reply := c.Request(vix.CommandCreateTemporaryDirectory, mktemp) 656 rc := vixRC(reply) 657 if rc != vix.OK { 658 t.Fatalf("rc: %d", rc) 659 } 660 661 dir := strings.TrimSuffix(string(reply[4:]), "\x00") 662 663 max := 12 664 var total int 665 666 // mktemp 667 for i := 0; i <= max; i++ { 668 mktemp = &vix.CreateTempFileRequest{ 669 DirectoryPath: dir, 670 } 671 672 reply = c.Request(vix.CommandCreateTemporaryFileEx, mktemp) 673 rc = vixRC(reply) 674 if rc != vix.OK { 675 t.Fatalf("rc: %d", rc) 676 } 677 } 678 679 // name of the last file temp file we created, we'll mess around with it then delete it 680 name := strings.TrimSuffix(string(reply[4:]), "\x00") 681 // for testing symlinks 682 link := filepath.Join(dir, "a-link") 683 err := os.Symlink(name, link) 684 if err != nil { 685 t.Fatal(err) 686 } 687 688 for _, fpath := range []string{name, link} { 689 // test ls of a single file 690 ls := &vix.ListFilesRequest{ 691 GuestPathName: fpath, 692 } 693 694 reply = c.Request(vix.CommandListFiles, ls) 695 696 rc = vixRC(reply) 697 if rc != vix.OK { 698 t.Fatalf("rc: %d", rc) 699 } 700 701 num := bytes.Count(reply, []byte("<fxi>")) 702 if num != 1 { 703 t.Errorf("ls %s: %d", name, num) 704 } 705 706 num = bytes.Count(reply, []byte("<rem>0</rem>")) 707 if num != 1 { 708 t.Errorf("ls %s: %d", name, num) 709 } 710 711 ft := 0 712 target := "" 713 if fpath == link { 714 target = name 715 ft = vix.FileAttributesSymlink 716 } 717 718 num = bytes.Count(reply, []byte(fmt.Sprintf("<slt>%s</slt>", target))) 719 if num != 1 { 720 t.Errorf("ls %s: %d", name, num) 721 } 722 723 num = bytes.Count(reply, []byte(fmt.Sprintf("<ft>%d</ft>", ft))) 724 if num != 1 { 725 t.Errorf("ls %s: %d", name, num) 726 } 727 } 728 729 mv := &vix.RenameFileRequest{ 730 OldPathName: name, 731 NewPathName: name + "-new", 732 } 733 734 for _, expect := range []int{vix.OK, vix.FileNotFound} { 735 reply = c.Request(vix.CommandMoveGuestFileEx, mv) 736 rc = vixRC(reply) 737 if rc != expect { 738 t.Errorf("rc: %d", rc) 739 } 740 741 if expect == vix.OK { 742 // test file type is properly checked 743 reply = c.Request(vix.CommandMoveGuestDirectory, &vix.RenameFileRequest{ 744 OldPathName: mv.NewPathName, 745 NewPathName: name, 746 }) 747 rc = vixRC(reply) 748 if rc != vix.NotADirectory { 749 t.Errorf("rc: %d", rc) 750 } 751 752 // test Overwrite flag is properly checked 753 reply = c.Request(vix.CommandMoveGuestFileEx, &vix.RenameFileRequest{ 754 OldPathName: mv.NewPathName, 755 NewPathName: mv.NewPathName, 756 }) 757 rc = vixRC(reply) 758 if rc != vix.FileAlreadyExists { 759 t.Errorf("rc: %d", rc) 760 } 761 } 762 } 763 764 // rmdir of a file should fail 765 reply = c.Request(vix.CommandDeleteGuestDirectoryEx, &vix.DirRequest{ 766 GuestPathName: mv.NewPathName, 767 }) 768 769 rc = vixRC(reply) 770 if rc != vix.NotADirectory { 771 t.Errorf("rc: %d", rc) 772 } 773 774 file := &vix.FileRequest{ 775 GuestPathName: mv.NewPathName, 776 } 777 778 for _, expect := range []int{vix.OK, vix.FileNotFound} { 779 reply = c.Request(vix.CommandDeleteGuestFileEx, file) 780 rc = vixRC(reply) 781 if rc != expect { 782 t.Errorf("rc: %d", rc) 783 } 784 } 785 786 // ls again now that file is gone 787 reply = c.Request(vix.CommandListFiles, &vix.ListFilesRequest{ 788 GuestPathName: name, 789 }) 790 791 rc = vixRC(reply) 792 if rc != vix.FileNotFound { 793 t.Errorf("rc: %d", rc) 794 } 795 796 // ls 797 ls := &vix.ListFilesRequest{ 798 GuestPathName: dir, 799 } 800 ls.Body.MaxResults = 5 // default is 50 801 802 for i := 0; i < 5; i++ { 803 reply = c.Request(vix.CommandListFiles, ls) 804 805 if Trace { 806 fmt.Fprintf(os.Stderr, "%s: %q\n", dir, string(reply[4:])) 807 } 808 809 var rem int 810 _, err := fmt.Fscanf(bytes.NewReader(reply[4:]), "<rem>%d</rem>", &rem) 811 if err != nil { 812 t.Fatal(err) 813 } 814 815 num := bytes.Count(reply, []byte("<fxi>")) 816 total += num 817 ls.Body.Offset += uint64(num) 818 819 if rem == 0 { 820 break 821 } 822 } 823 824 if total != max+1 { 825 t.Errorf("expected %d, got %d", max, total) 826 } 827 828 // Test invalid offset, making sure it doesn't cause panic (issue #934) 829 ls.Body.Offset += 10 830 _ = c.Request(vix.CommandListFiles, ls) 831 832 // mv $dir ${dir}-old 833 mv = &vix.RenameFileRequest{ 834 OldPathName: dir, 835 NewPathName: dir + "-old", 836 } 837 838 for _, expect := range []int{vix.OK, vix.FileNotFound} { 839 reply = c.Request(vix.CommandMoveGuestDirectory, mv) 840 rc = vixRC(reply) 841 if rc != expect { 842 t.Errorf("rc: %d", rc) 843 } 844 845 if expect == vix.OK { 846 // test file type is properly checked 847 reply = c.Request(vix.CommandMoveGuestFileEx, &vix.RenameFileRequest{ 848 OldPathName: mv.NewPathName, 849 NewPathName: dir, 850 }) 851 rc = vixRC(reply) 852 if rc != vix.NotAFile { 853 t.Errorf("rc: %d", rc) 854 } 855 856 // test Overwrite flag is properly checked 857 reply = c.Request(vix.CommandMoveGuestDirectory, &vix.RenameFileRequest{ 858 OldPathName: mv.NewPathName, 859 NewPathName: mv.NewPathName, 860 }) 861 rc = vixRC(reply) 862 if rc != vix.FileAlreadyExists { 863 t.Errorf("rc: %d", rc) 864 } 865 } 866 } 867 868 rmdir := &vix.DirRequest{ 869 GuestPathName: mv.NewPathName, 870 } 871 872 // rm -rm $dir 873 for _, rmr := range []bool{false, true} { 874 rmdir.Body.Recursive = rmr 875 876 reply = c.Request(vix.CommandDeleteGuestDirectoryEx, rmdir) 877 rc = vixRC(reply) 878 if rmr { 879 if rc != vix.OK { 880 t.Fatalf("rc: %d", rc) 881 } 882 } else { 883 if rc != vix.DirectoryNotEmpty { 884 t.Fatalf("rc: %d", rc) 885 } 886 } 887 } 888 } 889 890 func TestVixFileChangeAttributes(t *testing.T) { 891 if os.Getuid() == 0 { 892 t.Skip("running as root") 893 } 894 895 c := NewCommandClient() 896 897 f, err := os.CreateTemp("", "toolbox-") 898 if err != nil { 899 t.Fatal(err) 900 } 901 _ = f.Close() 902 name := f.Name() 903 904 // touch,chown,chmod 905 chattr := &vix.SetGuestFileAttributesRequest{ 906 GuestPathName: name, 907 } 908 909 h := &chattr.Body 910 911 tests := []struct { 912 expect int 913 f func() 914 }{ 915 { 916 vix.OK, func() {}, 917 }, 918 { 919 vix.OK, func() { 920 h.FileOptions = vix.FileAttributeSetModifyDate 921 h.ModificationTime = time.Now().Unix() 922 }, 923 }, 924 { 925 vix.OK, func() { 926 h.FileOptions = vix.FileAttributeSetAccessDate 927 h.AccessTime = time.Now().Unix() 928 }, 929 }, 930 { 931 vix.FileAccessError, func() { 932 h.FileOptions = vix.FileAttributeSetUnixOwnerid 933 h.OwnerID = 0 // fails as we are not root 934 }, 935 }, 936 { 937 vix.FileAccessError, func() { 938 h.FileOptions = vix.FileAttributeSetUnixGroupid 939 h.GroupID = 0 // fails as we are not root 940 }, 941 }, 942 { 943 vix.OK, func() { 944 h.FileOptions = vix.FileAttributeSetUnixOwnerid 945 h.OwnerID = int32(os.Getuid()) 946 }, 947 }, 948 { 949 vix.OK, func() { 950 h.FileOptions = vix.FileAttributeSetUnixGroupid 951 h.GroupID = int32(os.Getgid()) 952 }, 953 }, 954 { 955 vix.OK, func() { 956 h.FileOptions = vix.FileAttributeSetUnixPermissions 957 h.Permissions = int32(os.FileMode(0755).Perm()) 958 }, 959 }, 960 { 961 vix.FileNotFound, func() { 962 _ = os.Remove(name) 963 }, 964 }, 965 } 966 967 for i, test := range tests { 968 test.f() 969 reply := c.Request(vix.CommandSetGuestFileAttributes, chattr) 970 rc := vixRC(reply) 971 972 if rc != test.expect { 973 t.Errorf("%d: rc=%d", i, rc) 974 } 975 } 976 }