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  }