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