github.com/vmware/govmomi@v0.37.2/toolbox/hgfs/server_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 hgfs
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path"
    23  	"runtime"
    24  	"testing"
    25  )
    26  
    27  type Client struct {
    28  	s         *Server
    29  	SessionID uint64
    30  }
    31  
    32  func NewClient() *Client {
    33  	s := NewServer()
    34  
    35  	return &Client{
    36  		s: s,
    37  	}
    38  }
    39  
    40  func (c *Client) Dispatch(op int32, req interface{}, res interface{}) *Packet {
    41  	var err error
    42  	p := new(Packet)
    43  	p.Payload, err = MarshalBinary(req)
    44  	if err != nil {
    45  		panic(err)
    46  	}
    47  
    48  	p.Header.Version = 0x1
    49  	p.Header.Dummy = OpNewHeader
    50  	p.Header.HeaderSize = headerSize
    51  	p.Header.PacketSize = headerSize + uint32(len(p.Payload))
    52  	p.Header.SessionID = c.SessionID
    53  	p.Header.Op = op
    54  
    55  	data, err := p.MarshalBinary()
    56  	if err != nil {
    57  		panic(err)
    58  	}
    59  
    60  	data, err = c.s.Dispatch(data)
    61  	if err != nil {
    62  		panic(err)
    63  	}
    64  
    65  	p = new(Packet)
    66  	err = p.UnmarshalBinary(data)
    67  	if err != nil {
    68  		panic(err)
    69  	}
    70  
    71  	if p.Status == StatusSuccess {
    72  		err = UnmarshalBinary(p.Payload, res)
    73  		if err != nil {
    74  			panic(err)
    75  		}
    76  	}
    77  
    78  	return p
    79  }
    80  
    81  func (c *Client) CreateSession() uint32 {
    82  	req := new(RequestCreateSessionV4)
    83  	res := new(ReplyCreateSessionV4)
    84  
    85  	p := c.Dispatch(OpCreateSessionV4, req, res)
    86  
    87  	if p.Status == StatusSuccess {
    88  		c.SessionID = res.SessionID
    89  	}
    90  
    91  	return p.Status
    92  }
    93  
    94  func (c *Client) DestroySession() uint32 {
    95  	req := new(RequestDestroySessionV4)
    96  	res := new(ReplyDestroySessionV4)
    97  
    98  	return c.Dispatch(OpDestroySessionV4, req, res).Status
    99  }
   100  
   101  func (c *Client) GetAttr(name string) (*AttrV2, uint32) {
   102  	req := new(RequestGetattrV2)
   103  	res := new(ReplyGetattrV2)
   104  
   105  	req.FileName.FromString(name)
   106  
   107  	p := c.Dispatch(OpGetattrV2, req, res)
   108  
   109  	if p.Status != StatusSuccess {
   110  		return nil, p.Status
   111  	}
   112  
   113  	return &res.Attr, p.Status
   114  }
   115  
   116  func (c *Client) SetAttr(name string, attr AttrV2) uint32 {
   117  	req := new(RequestSetattrV2)
   118  	res := new(ReplySetattrV2)
   119  
   120  	req.FileName.FromString(name)
   121  
   122  	req.Attr = attr
   123  
   124  	p := c.Dispatch(OpSetattrV2, req, res)
   125  
   126  	if p.Status != StatusSuccess {
   127  		return p.Status
   128  	}
   129  
   130  	return p.Status
   131  }
   132  
   133  func (c *Client) Open(name string, write ...bool) (uint32, uint32) {
   134  	req := new(RequestOpen)
   135  	res := new(ReplyOpen)
   136  
   137  	if len(write) == 1 && write[0] {
   138  		req.OpenMode = OpenModeWriteOnly
   139  	}
   140  
   141  	req.FileName.FromString(name)
   142  
   143  	p := c.Dispatch(OpOpen, req, res)
   144  	if p.Status != StatusSuccess {
   145  		return 0, p.Status
   146  	}
   147  
   148  	return res.Handle, p.Status
   149  }
   150  
   151  func (c *Client) OpenWrite(name string) (uint32, uint32) {
   152  	req := new(RequestOpenV3)
   153  	res := new(ReplyOpenV3)
   154  
   155  	req.OpenMode = OpenModeWriteOnly
   156  	req.OpenFlags = OpenCreateEmpty
   157  	req.FileName.FromString(name)
   158  
   159  	p := c.Dispatch(OpOpenV3, req, res)
   160  	if p.Status != StatusSuccess {
   161  		return 0, p.Status
   162  	}
   163  
   164  	// cover the unsupported lock type path
   165  	req.DesiredLock = LockOpportunistic
   166  	status := c.Dispatch(OpOpenV3, req, res).Status
   167  	if status != StatusOperationNotSupported {
   168  		return 0, status
   169  	}
   170  
   171  	// cover the unsupported open mode path
   172  	req.DesiredLock = LockNone
   173  	req.OpenMode = OpenCreateSafe
   174  	status = c.Dispatch(OpOpenV3, req, res).Status
   175  	if status != StatusAccessDenied {
   176  		return 0, status
   177  	}
   178  
   179  	return res.Handle, p.Status
   180  }
   181  
   182  func (c *Client) Close(handle uint32) uint32 {
   183  	req := new(RequestClose)
   184  	res := new(ReplyClose)
   185  
   186  	req.Handle = handle
   187  
   188  	return c.Dispatch(OpClose, req, res).Status
   189  }
   190  
   191  func TestStaleSession(t *testing.T) {
   192  	c := NewClient()
   193  
   194  	// list of methods that can return StatusStaleSession
   195  	invalid := []func() uint32{
   196  		func() uint32 { _, status := c.Open("enoent"); return status },
   197  		func() uint32 { return c.Dispatch(OpReadV3, new(RequestReadV3), new(ReplyReadV3)).Status },
   198  		func() uint32 { return c.Dispatch(OpWriteV3, new(RequestWriteV3), new(ReplyWriteV3)).Status },
   199  		func() uint32 { return c.Close(0) },
   200  		c.DestroySession,
   201  	}
   202  
   203  	for i, f := range invalid {
   204  		status := f()
   205  		if status != StatusStaleSession {
   206  			t.Errorf("%d: status=%d", i, status)
   207  		}
   208  	}
   209  }
   210  
   211  func TestSessionMax(t *testing.T) {
   212  	c := NewClient()
   213  	var status uint32
   214  
   215  	for i := 0; i <= maxSessions+1; i++ {
   216  		status = c.CreateSession()
   217  	}
   218  
   219  	if status != StatusTooManySessions {
   220  		t.Errorf("status=%d", status)
   221  	}
   222  }
   223  
   224  func TestSessionDestroy(t *testing.T) {
   225  	Trace = true
   226  	c := NewClient()
   227  	c.CreateSession()
   228  	_, status := c.Open("/etc/resolv.conf")
   229  	if status != StatusSuccess {
   230  		t.Errorf("status=%d", status)
   231  	}
   232  	c.DestroySession()
   233  
   234  	if c.s.removeSession(c.SessionID) {
   235  		t.Error("session was not removed")
   236  	}
   237  }
   238  
   239  func TestInvalidOp(t *testing.T) {
   240  	c := NewClient()
   241  	status := c.Dispatch(1024, new(RequestClose), new(ReplyClose)).Status
   242  	if status != StatusOperationNotSupported {
   243  		t.Errorf("status=%d", status)
   244  	}
   245  }
   246  
   247  func TestReadV3(t *testing.T) {
   248  	Trace = testing.Verbose()
   249  
   250  	c := NewClient()
   251  	c.CreateSession()
   252  
   253  	_, status := c.GetAttr("enoent")
   254  
   255  	if status != StatusNoSuchFileOrDir {
   256  		t.Errorf("status=%d", status)
   257  	}
   258  
   259  	_, status = c.Open("enoent")
   260  	if status != StatusNoSuchFileOrDir {
   261  		t.Errorf("status=%d", status)
   262  	}
   263  
   264  	fname := "/etc/resolv.conf"
   265  
   266  	attr, _ := c.GetAttr(path.Dir(fname))
   267  	if attr.Type != FileTypeDirectory {
   268  		t.Errorf("type=%d", attr.Type)
   269  	}
   270  
   271  	attr, _ = c.GetAttr(fname)
   272  	if attr.Type != FileTypeRegular {
   273  		t.Errorf("type=%d", attr.Type)
   274  	}
   275  
   276  	if attr.Size == 0 {
   277  		t.Errorf("size=%d", attr.Size)
   278  	}
   279  
   280  	handle, status := c.Open(fname)
   281  	if status != StatusSuccess {
   282  		t.Fatalf("status=%d", status)
   283  	}
   284  
   285  	var req *RequestReadV3
   286  	var offset uint64
   287  	size := uint32(attr.Size / 2)
   288  
   289  	for offset = 0; offset < attr.Size; {
   290  		req = &RequestReadV3{
   291  			Offset:       offset,
   292  			Handle:       handle,
   293  			RequiredSize: size,
   294  		}
   295  
   296  		res := new(ReplyReadV3)
   297  
   298  		status = c.Dispatch(OpReadV3, req, res).Status
   299  
   300  		if status != StatusSuccess {
   301  			t.Fatalf("status=%d", status)
   302  		}
   303  
   304  		if Trace {
   305  			fmt.Fprintf(os.Stderr, "read %d: %q\n", res.ActualSize, string(res.Payload))
   306  		}
   307  
   308  		offset += uint64(res.ActualSize)
   309  	}
   310  
   311  	if uint64(offset) != attr.Size {
   312  		t.Errorf("size %d vs %d", offset, attr.Size)
   313  	}
   314  
   315  	status = c.Dispatch(OpReadV3, new(RequestReadV3), new(ReplyReadV3)).Status
   316  	if status != StatusInvalidHandle {
   317  		t.Fatalf("status=%d", status)
   318  	}
   319  
   320  	status = c.Close(0)
   321  	if status != StatusInvalidHandle {
   322  		t.Fatalf("status=%d", status)
   323  	}
   324  
   325  	status = c.Close(handle)
   326  	if status != StatusSuccess {
   327  		t.Fatalf("status=%d", status)
   328  	}
   329  
   330  	status = c.DestroySession()
   331  	if status != StatusSuccess {
   332  		t.Fatalf("status=%d", status)
   333  	}
   334  }
   335  
   336  func TestWriteV3(t *testing.T) {
   337  	if runtime.GOOS != "linux" {
   338  		t.Skip("requires Linux")
   339  	}
   340  
   341  	Trace = testing.Verbose()
   342  
   343  	f, err := os.CreateTemp("", "toolbox")
   344  	if err != nil {
   345  		t.Fatal(err)
   346  	}
   347  	_ = f.Close()
   348  
   349  	name := f.Name()
   350  
   351  	c := NewClient()
   352  	c.CreateSession()
   353  
   354  	_, status := c.Open("enoent", true)
   355  	// write not supported yet
   356  	if status != StatusAccessDenied {
   357  		t.Errorf("status=%d", status)
   358  	}
   359  
   360  	handle, status := c.OpenWrite(name)
   361  	if status != StatusSuccess {
   362  		t.Fatalf("status=%d", status)
   363  	}
   364  
   365  	payload := []byte("one two three\n")
   366  	size := uint32(len(payload))
   367  
   368  	req := &RequestWriteV3{
   369  		Handle:       handle,
   370  		WriteFlags:   WriteAppend,
   371  		Offset:       0,
   372  		RequiredSize: size,
   373  		Payload:      payload,
   374  	}
   375  
   376  	res := new(ReplyReadV3)
   377  
   378  	status = c.Dispatch(OpWriteV3, req, res).Status
   379  
   380  	if status != StatusSuccess {
   381  		t.Errorf("status=%d", status)
   382  	}
   383  
   384  	if size != res.ActualSize {
   385  		t.Errorf("%d vs %d", size, res.ActualSize)
   386  	}
   387  
   388  	status = c.Dispatch(OpWriteV3, new(RequestWriteV3), new(ReplyWriteV3)).Status
   389  	if status != StatusInvalidHandle {
   390  		t.Fatalf("status=%d", status)
   391  	}
   392  
   393  	status = c.Close(handle)
   394  	if status != StatusSuccess {
   395  		t.Errorf("status=%d", status)
   396  	}
   397  
   398  	attr, _ := c.GetAttr(name)
   399  	if attr.Size != uint64(size) {
   400  		t.Errorf("%d vs %d", size, attr.Size)
   401  	}
   402  
   403  	attr.OwnerPerms |= PermExec
   404  
   405  	errors := []struct {
   406  		err    error
   407  		status uint32
   408  	}{
   409  		{os.ErrPermission, StatusOperationNotPermitted},
   410  		{os.ErrNotExist, StatusNoSuchFileOrDir},
   411  		{os.ErrExist, StatusFileExists},
   412  		{nil, StatusSuccess},
   413  	}
   414  
   415  	for _, e := range errors {
   416  		c.s.chown = func(_ string, _ int, _ int) error {
   417  			return e.err
   418  		}
   419  
   420  		status = c.SetAttr(name, *attr)
   421  		if status != e.status {
   422  			t.Errorf("status=%d", status)
   423  		}
   424  	}
   425  
   426  	c.s.chown = func(_ string, _ int, _ int) error {
   427  		return nil
   428  	}
   429  
   430  	for _, e := range errors {
   431  		c.s.chmod = func(_ string, _ os.FileMode) error {
   432  			return e.err
   433  		}
   434  
   435  		status = c.SetAttr(name, *attr)
   436  		if status != e.status {
   437  			t.Errorf("status=%d", status)
   438  		}
   439  	}
   440  
   441  	status = c.DestroySession()
   442  	if status != StatusSuccess {
   443  		t.Errorf("status=%d", status)
   444  	}
   445  
   446  	_ = os.Remove(name)
   447  }