github.com/vmware/govmomi@v0.37.2/toolbox/command.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  	"encoding/base64"
    22  	"encoding/binary"
    23  	"encoding/hex"
    24  	"fmt"
    25  	"log"
    26  	"os"
    27  	"path/filepath"
    28  	"runtime"
    29  	"strings"
    30  	"time"
    31  
    32  	"github.com/vmware/govmomi/toolbox/hgfs"
    33  	"github.com/vmware/govmomi/toolbox/process"
    34  	"github.com/vmware/govmomi/toolbox/vix"
    35  )
    36  
    37  type CommandHandler func(vix.CommandRequestHeader, []byte) ([]byte, error)
    38  
    39  type CommandServer struct {
    40  	Out *ChannelOut
    41  
    42  	ProcessManager *process.Manager
    43  
    44  	Authenticate func(vix.CommandRequestHeader, []byte) error
    45  
    46  	ProcessStartCommand func(*process.Manager, *vix.StartProgramRequest) (int64, error)
    47  
    48  	handlers map[uint32]CommandHandler
    49  
    50  	FileServer *hgfs.Server
    51  }
    52  
    53  func registerCommandServer(service *Service) *CommandServer {
    54  	server := &CommandServer{
    55  		Out:            service.out,
    56  		ProcessManager: process.NewManager(),
    57  	}
    58  
    59  	server.handlers = map[uint32]CommandHandler{
    60  		vix.CommandGetToolsState:                 server.GetToolsState,
    61  		vix.CommandStartProgram:                  server.StartCommand,
    62  		vix.CommandTerminateProcess:              server.KillProcess,
    63  		vix.CommandListProcessesEx:               server.ListProcesses,
    64  		vix.CommandReadEnvVariables:              server.ReadEnvironmentVariables,
    65  		vix.CommandCreateTemporaryFileEx:         server.CreateTemporaryFile,
    66  		vix.CommandCreateTemporaryDirectory:      server.CreateTemporaryDirectory,
    67  		vix.CommandDeleteGuestFileEx:             server.DeleteFile,
    68  		vix.CommandCreateDirectoryEx:             server.CreateDirectory,
    69  		vix.CommandDeleteGuestDirectoryEx:        server.DeleteDirectory,
    70  		vix.CommandMoveGuestFileEx:               server.MoveFile,
    71  		vix.CommandMoveGuestDirectory:            server.MoveDirectory,
    72  		vix.CommandListFiles:                     server.ListFiles,
    73  		vix.CommandSetGuestFileAttributes:        server.SetGuestFileAttributes,
    74  		vix.CommandInitiateFileTransferFromGuest: server.InitiateFileTransferFromGuest,
    75  		vix.CommandInitiateFileTransferToGuest:   server.InitiateFileTransferToGuest,
    76  		vix.HgfsSendPacketCommand:                server.ProcessHgfsPacket,
    77  	}
    78  
    79  	server.ProcessStartCommand = DefaultStartCommand
    80  
    81  	service.RegisterHandler("Vix_1_Relayed_Command", server.Dispatch)
    82  
    83  	return server
    84  }
    85  
    86  func commandResult(header vix.CommandRequestHeader, rc int, err error, response []byte) []byte {
    87  	// All Foundry tools commands return results that start with a foundry error
    88  	// and a guest-OS-specific error (e.g. errno)
    89  	errno := 0
    90  
    91  	if err != nil {
    92  		// TODO: inspect err for system error, setting errno
    93  
    94  		response = []byte(err.Error())
    95  
    96  		log.Printf("[vix] op=%d error: %s", header.OpCode, err)
    97  	}
    98  
    99  	buf := bytes.NewBufferString(fmt.Sprintf("%d %d ", rc, errno))
   100  
   101  	if header.CommonFlags&vix.CommandGuestReturnsBinary != 0 {
   102  		// '#' delimits end of ascii and the start of the binary data (see ToolsDaemonTcloReceiveVixCommand)
   103  		_ = buf.WriteByte('#')
   104  	}
   105  
   106  	_, _ = buf.Write(response)
   107  
   108  	if header.CommonFlags&vix.CommandGuestReturnsBinary == 0 {
   109  		// this is not binary data, so it should be a NULL terminated string (see ToolsDaemonTcloReceiveVixCommand)
   110  		_ = buf.WriteByte(0)
   111  	}
   112  
   113  	return buf.Bytes()
   114  }
   115  
   116  func (c *CommandServer) Dispatch(data []byte) ([]byte, error) {
   117  	// See ToolsDaemonTcloGetQuotedString
   118  	if data[0] == '"' {
   119  		data = data[1:]
   120  	}
   121  
   122  	var name string
   123  
   124  	ix := bytes.IndexByte(data, '"')
   125  	if ix > 0 {
   126  		name = string(data[:ix])
   127  		data = data[ix+1:]
   128  	}
   129  	// skip the NULL
   130  	if data[0] == 0 {
   131  		data = data[1:]
   132  	}
   133  
   134  	if Trace {
   135  		fmt.Fprintf(os.Stderr, "vix dispatch %q...\n%s\n", name, hex.Dump(data))
   136  	}
   137  
   138  	var header vix.CommandRequestHeader
   139  	buf := bytes.NewBuffer(data)
   140  	err := binary.Read(buf, binary.LittleEndian, &header)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	if header.Magic != vix.CommandMagicWord {
   146  		return commandResult(header, vix.InvalidMessageHeader, nil, nil), nil
   147  	}
   148  
   149  	handler, ok := c.handlers[header.OpCode]
   150  	if !ok {
   151  		return commandResult(header, vix.UnrecognizedCommandInGuest, nil, nil), nil
   152  	}
   153  
   154  	if header.OpCode != vix.CommandGetToolsState {
   155  		// Every command expect GetToolsState requires authentication
   156  		creds := buf.Bytes()[header.BodyLength:]
   157  
   158  		err = c.authenticate(header, creds[:header.CredentialLength])
   159  		if err != nil {
   160  			return commandResult(header, vix.AuthenticationFail, err, nil), nil
   161  		}
   162  	}
   163  
   164  	rc := vix.OK
   165  
   166  	response, err := handler(header, buf.Bytes())
   167  	if err != nil {
   168  		rc = vix.ErrorCode(err)
   169  	}
   170  
   171  	return commandResult(header, rc, err, response), nil
   172  }
   173  
   174  func (c *CommandServer) RegisterHandler(op uint32, handler CommandHandler) {
   175  	c.handlers[op] = handler
   176  }
   177  
   178  func (c *CommandServer) GetToolsState(_ vix.CommandRequestHeader, _ []byte) ([]byte, error) {
   179  	hostname, _ := os.Hostname()
   180  	osname := fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH)
   181  
   182  	// Note that vmtoolsd sends back 40 or so of these properties, sticking with the minimal set for now.
   183  	props := vix.PropertyList{
   184  		vix.NewStringProperty(vix.PropertyGuestOsVersion, osname),
   185  		vix.NewStringProperty(vix.PropertyGuestOsVersionShort, osname),
   186  		vix.NewStringProperty(vix.PropertyGuestToolsProductNam, "VMware Tools (Go)"),
   187  		vix.NewStringProperty(vix.PropertyGuestToolsVersion, "10.0.5 build-3227872 (Compatible)"),
   188  		vix.NewStringProperty(vix.PropertyGuestName, hostname),
   189  		vix.NewInt32Property(vix.PropertyGuestToolsAPIOptions, 0x0001), // TODO: const VIX_TOOLSFEATURE_SUPPORT_GET_HANDLE_STATE
   190  		vix.NewInt32Property(vix.PropertyGuestOsFamily, 1),             // TODO: const GUEST_OS_FAMILY_*
   191  		vix.NewBoolProperty(vix.PropertyGuestStartProgramEnabled, true),
   192  		vix.NewBoolProperty(vix.PropertyGuestTerminateProcessEnabled, true),
   193  		vix.NewBoolProperty(vix.PropertyGuestListProcessesEnabled, true),
   194  		vix.NewBoolProperty(vix.PropertyGuestReadEnvironmentVariableEnabled, true),
   195  		vix.NewBoolProperty(vix.PropertyGuestMakeDirectoryEnabled, true),
   196  		vix.NewBoolProperty(vix.PropertyGuestDeleteFileEnabled, true),
   197  		vix.NewBoolProperty(vix.PropertyGuestDeleteDirectoryEnabled, true),
   198  		vix.NewBoolProperty(vix.PropertyGuestMoveDirectoryEnabled, true),
   199  		vix.NewBoolProperty(vix.PropertyGuestMoveFileEnabled, true),
   200  		vix.NewBoolProperty(vix.PropertyGuestCreateTempFileEnabled, true),
   201  		vix.NewBoolProperty(vix.PropertyGuestCreateTempDirectoryEnabled, true),
   202  		vix.NewBoolProperty(vix.PropertyGuestListFilesEnabled, true),
   203  		vix.NewBoolProperty(vix.PropertyGuestChangeFileAttributesEnabled, true),
   204  		vix.NewBoolProperty(vix.PropertyGuestInitiateFileTransferFromGuestEnabled, true),
   205  		vix.NewBoolProperty(vix.PropertyGuestInitiateFileTransferToGuestEnabled, true),
   206  	}
   207  
   208  	src, _ := props.MarshalBinary()
   209  	enc := base64.StdEncoding
   210  	buf := make([]byte, enc.EncodedLen(len(src)))
   211  	enc.Encode(buf, src)
   212  
   213  	return buf, nil
   214  }
   215  
   216  func (c *CommandServer) StartCommand(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   217  	r := &vix.StartProgramRequest{
   218  		CommandRequestHeader: header,
   219  	}
   220  
   221  	err := r.UnmarshalBinary(data)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	pid, err := c.ProcessStartCommand(c.ProcessManager, r)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	return append([]byte(fmt.Sprintf("%d", pid)), 0), nil
   232  }
   233  
   234  func DefaultStartCommand(m *process.Manager, r *vix.StartProgramRequest) (int64, error) {
   235  	p := process.New()
   236  
   237  	switch r.ProgramPath {
   238  	case "http.RoundTrip":
   239  		p = process.NewRoundTrip()
   240  	default:
   241  		// Standard vmware-tools requires an absolute path,
   242  		// we'll enable IO redirection by default without an absolute path.
   243  		if !strings.Contains(r.ProgramPath, "/") {
   244  			p = p.WithIO()
   245  		}
   246  	}
   247  
   248  	return m.Start(r, p)
   249  }
   250  
   251  func (c *CommandServer) KillProcess(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   252  	r := &vix.KillProcessRequest{
   253  		CommandRequestHeader: header,
   254  	}
   255  
   256  	err := r.UnmarshalBinary(data)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	if c.ProcessManager.Kill(r.Body.Pid) {
   262  		return nil, err
   263  	}
   264  
   265  	// TODO: could kill process started outside of toolbox
   266  
   267  	return nil, vix.Error(vix.NoSuchProcess)
   268  }
   269  
   270  func (c *CommandServer) ListProcesses(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   271  	r := &vix.ListProcessesRequest{
   272  		CommandRequestHeader: header,
   273  	}
   274  
   275  	err := r.UnmarshalBinary(data)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  
   280  	state := c.ProcessManager.ListProcesses(r.Pids)
   281  
   282  	return state, nil
   283  }
   284  
   285  func (c *CommandServer) ReadEnvironmentVariables(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   286  	r := &vix.ReadEnvironmentVariablesRequest{
   287  		CommandRequestHeader: header,
   288  	}
   289  
   290  	err := r.UnmarshalBinary(data)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  
   295  	buf := new(bytes.Buffer)
   296  
   297  	if len(r.Names) == 0 {
   298  		for _, e := range os.Environ() {
   299  			_, _ = buf.WriteString(fmt.Sprintf("<ev>%s</ev>", process.EscapeXML.Replace(e)))
   300  		}
   301  	} else {
   302  		for _, key := range r.Names {
   303  			val := os.Getenv(key)
   304  			if val == "" {
   305  				continue
   306  			}
   307  			_, _ = buf.WriteString(fmt.Sprintf("<ev>%s=%s</ev>", process.EscapeXML.Replace(key), process.EscapeXML.Replace(val)))
   308  		}
   309  	}
   310  
   311  	return buf.Bytes(), nil
   312  }
   313  
   314  func (c *CommandServer) CreateTemporaryFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   315  	r := &vix.CreateTempFileRequest{
   316  		CommandRequestHeader: header,
   317  	}
   318  
   319  	err := r.UnmarshalBinary(data)
   320  	if err != nil {
   321  		return nil, err
   322  	}
   323  
   324  	f, err := os.CreateTemp(r.DirectoryPath, r.FilePrefix+"vmware")
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	_ = f.Close()
   330  
   331  	return []byte(f.Name()), nil
   332  }
   333  
   334  func (c *CommandServer) CreateTemporaryDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   335  	r := &vix.CreateTempFileRequest{
   336  		CommandRequestHeader: header,
   337  	}
   338  
   339  	err := r.UnmarshalBinary(data)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  
   344  	name, err := os.MkdirTemp(r.DirectoryPath, r.FilePrefix+"vmware")
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  
   349  	return []byte(name), nil
   350  }
   351  
   352  func (c *CommandServer) DeleteFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   353  	r := &vix.FileRequest{
   354  		CommandRequestHeader: header,
   355  	}
   356  
   357  	err := r.UnmarshalBinary(data)
   358  	if err != nil {
   359  		return nil, err
   360  	}
   361  
   362  	info, err := os.Stat(r.GuestPathName)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  
   367  	if info.IsDir() {
   368  		return nil, vix.Error(vix.NotAFile)
   369  	}
   370  
   371  	err = os.Remove(r.GuestPathName)
   372  
   373  	return nil, err
   374  }
   375  
   376  func (c *CommandServer) DeleteDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   377  	r := &vix.DirRequest{
   378  		CommandRequestHeader: header,
   379  	}
   380  
   381  	err := r.UnmarshalBinary(data)
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  
   386  	info, err := os.Stat(r.GuestPathName)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  
   391  	if !info.IsDir() {
   392  		return nil, vix.Error(vix.NotADirectory)
   393  	}
   394  
   395  	if r.Body.Recursive {
   396  		err = os.RemoveAll(r.GuestPathName)
   397  	} else {
   398  		err = os.Remove(r.GuestPathName)
   399  	}
   400  
   401  	return nil, err
   402  }
   403  
   404  func (c *CommandServer) CreateDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   405  	r := &vix.DirRequest{
   406  		CommandRequestHeader: header,
   407  	}
   408  
   409  	err := r.UnmarshalBinary(data)
   410  	if err != nil {
   411  		return nil, err
   412  	}
   413  
   414  	mkdir := os.Mkdir
   415  
   416  	if r.Body.Recursive {
   417  		mkdir = os.MkdirAll
   418  	}
   419  
   420  	err = mkdir(r.GuestPathName, 0700)
   421  
   422  	return nil, err
   423  }
   424  
   425  func (c *CommandServer) MoveDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   426  	r := &vix.RenameFileRequest{
   427  		CommandRequestHeader: header,
   428  	}
   429  
   430  	err := r.UnmarshalBinary(data)
   431  	if err != nil {
   432  		return nil, err
   433  	}
   434  
   435  	info, err := os.Stat(r.OldPathName)
   436  	if err != nil {
   437  		return nil, err
   438  	}
   439  
   440  	if !info.IsDir() {
   441  		return nil, vix.Error(vix.NotADirectory)
   442  	}
   443  
   444  	if !r.Body.Overwrite {
   445  		_, err = os.Stat(r.NewPathName)
   446  		if err == nil {
   447  			return nil, vix.Error(vix.FileAlreadyExists)
   448  		}
   449  	}
   450  
   451  	return nil, os.Rename(r.OldPathName, r.NewPathName)
   452  }
   453  
   454  func (c *CommandServer) MoveFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   455  	r := &vix.RenameFileRequest{
   456  		CommandRequestHeader: header,
   457  	}
   458  
   459  	err := r.UnmarshalBinary(data)
   460  	if err != nil {
   461  		return nil, err
   462  	}
   463  
   464  	info, err := os.Stat(r.OldPathName)
   465  	if err != nil {
   466  		return nil, err
   467  	}
   468  
   469  	if info.IsDir() {
   470  		return nil, vix.Error(vix.NotAFile)
   471  	}
   472  
   473  	if !r.Body.Overwrite {
   474  		_, err = os.Stat(r.NewPathName)
   475  		if err == nil {
   476  			return nil, vix.Error(vix.FileAlreadyExists)
   477  		}
   478  	}
   479  
   480  	return nil, os.Rename(r.OldPathName, r.NewPathName)
   481  }
   482  
   483  func (c *CommandServer) ListFiles(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   484  	r := &vix.ListFilesRequest{
   485  		CommandRequestHeader: header,
   486  	}
   487  
   488  	err := r.UnmarshalBinary(data)
   489  	if err != nil {
   490  		return nil, err
   491  	}
   492  
   493  	info, err := os.Lstat(r.GuestPathName)
   494  	if err != nil {
   495  		return nil, err
   496  	}
   497  
   498  	var dir string
   499  	var files []os.FileInfo
   500  
   501  	if info.IsDir() {
   502  		dir = r.GuestPathName
   503  		entries, err := os.ReadDir(r.GuestPathName)
   504  		if err != nil {
   505  			return nil, err
   506  		}
   507  		for _, entry := range entries {
   508  			file, _ := entry.Info()
   509  			files = append(files, file)
   510  		}
   511  	} else {
   512  		dir = filepath.Dir(r.GuestPathName)
   513  		files = append(files, info)
   514  	}
   515  
   516  	offset := r.Body.Offset + uint64(r.Body.Index)
   517  	total := uint64(len(files)) - offset
   518  	if int(offset) < len(files) {
   519  		files = files[offset:]
   520  	} else {
   521  		total = 0 // offset is not valid (open-vm-tools behaves the same in this case)
   522  	}
   523  
   524  	var remaining uint64
   525  
   526  	if r.Body.MaxResults > 0 && total > uint64(r.Body.MaxResults) {
   527  		remaining = total - uint64(r.Body.MaxResults)
   528  		files = files[:r.Body.MaxResults]
   529  	}
   530  
   531  	buf := new(bytes.Buffer)
   532  	buf.WriteString(fmt.Sprintf("<rem>%d</rem>", remaining))
   533  
   534  	for _, info = range files {
   535  		buf.WriteString(fileExtendedInfoFormat(dir, info))
   536  	}
   537  
   538  	return buf.Bytes(), nil
   539  }
   540  
   541  func chtimes(r *vix.SetGuestFileAttributesRequest) error {
   542  	var mtime, atime *time.Time
   543  
   544  	if r.IsSet(vix.FileAttributeSetModifyDate) {
   545  		t := time.Unix(r.Body.ModificationTime, 0)
   546  		mtime = &t
   547  	}
   548  
   549  	if r.IsSet(vix.FileAttributeSetAccessDate) {
   550  		t := time.Unix(r.Body.AccessTime, 0)
   551  		atime = &t
   552  	}
   553  
   554  	if mtime == nil && atime == nil {
   555  		return nil
   556  	}
   557  
   558  	info, err := os.Stat(r.GuestPathName)
   559  	if err != nil {
   560  		return err
   561  	}
   562  
   563  	if mtime == nil {
   564  		t := info.ModTime()
   565  		mtime = &t
   566  	}
   567  
   568  	if atime == nil {
   569  		t := info.ModTime()
   570  		atime = &t
   571  	}
   572  
   573  	return os.Chtimes(r.GuestPathName, *atime, *mtime)
   574  }
   575  
   576  func chown(r *vix.SetGuestFileAttributesRequest) error {
   577  	uid := -1
   578  	gid := -1
   579  
   580  	if r.IsSet(vix.FileAttributeSetUnixOwnerid) {
   581  		uid = int(r.Body.OwnerID)
   582  	}
   583  
   584  	if r.IsSet(vix.FileAttributeSetUnixGroupid) {
   585  		gid = int(r.Body.GroupID)
   586  	}
   587  
   588  	if uid == -1 && gid == -1 {
   589  		return nil
   590  	}
   591  
   592  	return os.Chown(r.GuestPathName, uid, gid)
   593  }
   594  
   595  func chmod(r *vix.SetGuestFileAttributesRequest) error {
   596  	if r.IsSet(vix.FileAttributeSetUnixPermissions) {
   597  		return os.Chmod(r.GuestPathName, os.FileMode(r.Body.Permissions).Perm())
   598  	}
   599  
   600  	return nil
   601  }
   602  
   603  func (c *CommandServer) SetGuestFileAttributes(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   604  	r := &vix.SetGuestFileAttributesRequest{
   605  		CommandRequestHeader: header,
   606  	}
   607  
   608  	err := r.UnmarshalBinary(data)
   609  	if err != nil {
   610  		return nil, err
   611  	}
   612  
   613  	for _, set := range []func(*vix.SetGuestFileAttributesRequest) error{chtimes, chown, chmod} {
   614  		err = set(r)
   615  		if err != nil {
   616  			return nil, err
   617  		}
   618  	}
   619  
   620  	return nil, nil
   621  }
   622  
   623  func (c *CommandServer) InitiateFileTransferFromGuest(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   624  	r := &vix.ListFilesRequest{
   625  		CommandRequestHeader: header,
   626  	}
   627  
   628  	err := r.UnmarshalBinary(data)
   629  	if err != nil {
   630  		return nil, err
   631  	}
   632  
   633  	info, err := c.FileServer.Stat(r.GuestPathName)
   634  	if err != nil {
   635  		return nil, err
   636  	}
   637  
   638  	if info.Mode()&os.ModeSymlink == os.ModeSymlink {
   639  		return nil, vix.Error(vix.InvalidArg)
   640  	}
   641  
   642  	if info.IsDir() {
   643  		return nil, vix.Error(vix.NotAFile)
   644  	}
   645  
   646  	return []byte(fileExtendedInfoFormat("", info)), nil
   647  }
   648  
   649  func (c *CommandServer) InitiateFileTransferToGuest(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   650  	r := &vix.InitiateFileTransferToGuestRequest{
   651  		CommandRequestHeader: header,
   652  	}
   653  
   654  	err := r.UnmarshalBinary(data)
   655  	if err != nil {
   656  		return nil, err
   657  	}
   658  
   659  	info, err := c.FileServer.Stat(r.GuestPathName)
   660  	if err == nil {
   661  		if info.Mode()&os.ModeSymlink == os.ModeSymlink {
   662  			return nil, vix.Error(vix.InvalidArg)
   663  		}
   664  
   665  		if info.IsDir() {
   666  			return nil, vix.Error(vix.NotAFile)
   667  		}
   668  
   669  		if !r.Body.Overwrite {
   670  			return nil, vix.Error(vix.FileAlreadyExists)
   671  		}
   672  	} else if !os.IsNotExist(err) {
   673  		return nil, err
   674  	}
   675  
   676  	return nil, nil
   677  }
   678  
   679  func (c *CommandServer) ProcessHgfsPacket(header vix.CommandRequestHeader, data []byte) ([]byte, error) {
   680  	r := &vix.CommandHgfsSendPacket{
   681  		CommandRequestHeader: header,
   682  	}
   683  
   684  	err := r.UnmarshalBinary(data)
   685  	if err != nil {
   686  		return nil, err
   687  	}
   688  
   689  	return c.FileServer.Dispatch(r.Packet)
   690  }
   691  
   692  func (c *CommandServer) authenticate(r vix.CommandRequestHeader, data []byte) error {
   693  	if c.Authenticate != nil {
   694  		return c.Authenticate(r, data)
   695  	}
   696  
   697  	switch r.UserCredentialType {
   698  	case vix.UserCredentialTypeNamePassword:
   699  		var c vix.UserCredentialNamePassword
   700  
   701  		if err := c.UnmarshalBinary(data); err != nil {
   702  			return err
   703  		}
   704  
   705  		if Trace {
   706  			fmt.Fprintf(traceLog, "ignoring credentials: %q:%q\n", c.Name, c.Password)
   707  		}
   708  
   709  		return nil
   710  	default:
   711  		return fmt.Errorf("unsupported UserCredentialType=%d", r.UserCredentialType)
   712  	}
   713  }