github.com/vmware/govmomi@v0.37.2/toolbox/hgfs/archive_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  	"archive/tar"
    21  	"bytes"
    22  	"compress/gzip"
    23  	"fmt"
    24  	"io"
    25  	"log"
    26  	"net/url"
    27  	"os"
    28  	"os/exec"
    29  	"path"
    30  	"strings"
    31  	"testing"
    32  	"time"
    33  )
    34  
    35  func TestReadArchive(t *testing.T) {
    36  	Trace = testing.Verbose()
    37  
    38  	dir, err := os.MkdirTemp("", "toolbox-")
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  
    43  	nfiles := 5
    44  	subdir := path.Join(dir, "hangar-18")
    45  	_ = os.MkdirAll(subdir, 0755)
    46  	dirs := []string{dir, subdir}
    47  
    48  	for i := 0; i < nfiles; i++ {
    49  		for _, p := range dirs {
    50  			data := bytes.NewBufferString(strings.Repeat("X", i+1024))
    51  
    52  			f, ferr := os.CreateTemp(p, fmt.Sprintf("file-%d-", i))
    53  			if ferr != nil {
    54  				t.Fatal(ferr)
    55  			}
    56  			_, err = io.Copy(f, data)
    57  			if err != nil {
    58  				t.Fatal(err)
    59  			}
    60  			err = f.Close()
    61  			if err != nil {
    62  				t.Fatal(err)
    63  			}
    64  
    65  			if i == 0 {
    66  				err = os.Symlink(f.Name(), path.Join(p, "first-file"))
    67  				if err != nil {
    68  					t.Fatal(err)
    69  				}
    70  			}
    71  		}
    72  	}
    73  
    74  	c := NewClient()
    75  	c.s.RegisterFileHandler(ArchiveScheme, NewArchiveHandler())
    76  
    77  	status := c.CreateSession()
    78  	if status != StatusSuccess {
    79  		t.Fatalf("status=%d", status)
    80  	}
    81  
    82  	_, status = c.GetAttr(dir)
    83  	if status != StatusSuccess {
    84  		t.Errorf("status=%d", status)
    85  	}
    86  
    87  	handle, status := c.Open(dir + "?format=tgz")
    88  	if status != StatusSuccess {
    89  		t.Fatalf("status=%d", status)
    90  	}
    91  
    92  	var req *RequestReadV3
    93  	var offset uint64
    94  
    95  	var buf bytes.Buffer
    96  
    97  	for {
    98  		req = &RequestReadV3{
    99  			Offset:       offset,
   100  			Handle:       handle,
   101  			RequiredSize: 4096,
   102  		}
   103  
   104  		res := new(ReplyReadV3)
   105  
   106  		status = c.Dispatch(OpReadV3, req, res).Status
   107  		if status != StatusSuccess {
   108  			t.Fatalf("status=%d", status)
   109  		}
   110  
   111  		if Trace {
   112  			fmt.Fprintf(os.Stderr, "read %d: %q\n", res.ActualSize, string(res.Payload))
   113  		}
   114  
   115  		offset += uint64(res.ActualSize)
   116  		_, _ = buf.Write(res.Payload)
   117  
   118  		if res.ActualSize == 0 {
   119  			break
   120  		}
   121  	}
   122  
   123  	status = c.Close(handle)
   124  	if status != StatusSuccess {
   125  		t.Errorf("status=%d", status)
   126  	}
   127  
   128  	status = c.DestroySession()
   129  	if status != StatusSuccess {
   130  		t.Errorf("status=%d", status)
   131  	}
   132  
   133  	var files []string
   134  	gz, _ := gzip.NewReader(&buf)
   135  	tr := tar.NewReader(gz)
   136  
   137  	for {
   138  		header, terr := tr.Next()
   139  		if terr != nil {
   140  			if terr == io.EOF {
   141  				break
   142  			}
   143  			t.Fatal(terr)
   144  		}
   145  
   146  		files = append(files, header.Name)
   147  
   148  		if header.Typeflag == tar.TypeReg {
   149  			_, err = io.Copy(io.Discard, tr)
   150  			if err != nil {
   151  				t.Fatal(err)
   152  			}
   153  		}
   154  	}
   155  
   156  	nfiles++ // symlink
   157  	expect := nfiles*len(dirs) + len(dirs) - 1
   158  	if len(files) != expect {
   159  		t.Errorf("expected %d, files=%d", expect, len(files))
   160  	}
   161  
   162  	err = os.RemoveAll(dir)
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  }
   167  
   168  func TestWriteArchive(t *testing.T) {
   169  	Trace = testing.Verbose()
   170  
   171  	dir, err := os.MkdirTemp("", "toolbox-")
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  
   176  	nfiles := 5
   177  
   178  	var buf bytes.Buffer
   179  
   180  	gz := gzip.NewWriter(&buf)
   181  	tw := tar.NewWriter(gz)
   182  
   183  	for i := 0; i < nfiles; i++ {
   184  		data := bytes.NewBufferString(strings.Repeat("X", i+1024))
   185  
   186  		header := &tar.Header{
   187  			Name:     fmt.Sprintf("toolbox-file-%d", i),
   188  			ModTime:  time.Now(),
   189  			Mode:     0644,
   190  			Typeflag: tar.TypeReg,
   191  			Size:     int64(data.Len()),
   192  		}
   193  
   194  		err = tw.WriteHeader(header)
   195  		if err != nil {
   196  			t.Fatal(err)
   197  		}
   198  
   199  		_, _ = tw.Write(data.Bytes())
   200  
   201  		if i == 0 {
   202  			err = tw.WriteHeader(&tar.Header{
   203  				Linkname: header.Name,
   204  				Name:     "first-file",
   205  				ModTime:  time.Now(),
   206  				Mode:     0644,
   207  				Typeflag: tar.TypeSymlink,
   208  			})
   209  			if err != nil {
   210  				t.Fatal(err)
   211  			}
   212  
   213  			err = tw.WriteHeader(&tar.Header{
   214  				Name:     "subdir",
   215  				ModTime:  time.Now(),
   216  				Mode:     0755,
   217  				Typeflag: tar.TypeDir,
   218  			})
   219  			if err != nil {
   220  				t.Fatal(err)
   221  			}
   222  		}
   223  	}
   224  
   225  	_ = tw.Close()
   226  	_ = gz.Close()
   227  
   228  	c := NewClient()
   229  	c.s.RegisterFileHandler(ArchiveScheme, NewArchiveHandler())
   230  
   231  	status := c.CreateSession()
   232  	if status != StatusSuccess {
   233  		t.Fatalf("status=%d", status)
   234  	}
   235  
   236  	_, status = c.GetAttr(dir)
   237  	if status != StatusSuccess {
   238  		t.Errorf("status=%d", status)
   239  	}
   240  
   241  	handle, status := c.OpenWrite(dir)
   242  	if status != StatusSuccess {
   243  		t.Fatalf("status=%d", status)
   244  	}
   245  
   246  	payload := buf.Bytes()
   247  	size := uint32(buf.Len())
   248  
   249  	req := &RequestWriteV3{
   250  		Handle:       handle,
   251  		WriteFlags:   WriteAppend,
   252  		Offset:       0,
   253  		RequiredSize: size,
   254  		Payload:      payload,
   255  	}
   256  
   257  	res := new(ReplyReadV3)
   258  
   259  	status = c.Dispatch(OpWriteV3, req, res).Status
   260  
   261  	if status != StatusSuccess {
   262  		t.Errorf("status=%d", status)
   263  	}
   264  
   265  	var attr AttrV2
   266  	info, _ := os.Stat(dir)
   267  	attr.Stat(info)
   268  
   269  	status = c.SetAttr(dir, attr)
   270  	if status != StatusSuccess {
   271  		t.Errorf("status=%d", status)
   272  	}
   273  
   274  	status = c.Close(handle)
   275  	if status != StatusSuccess {
   276  		t.Errorf("status=%d", status)
   277  	}
   278  
   279  	status = c.DestroySession()
   280  	if status != StatusSuccess {
   281  		t.Errorf("status=%d", status)
   282  	}
   283  
   284  	files, err := os.ReadDir(dir)
   285  	if err != nil {
   286  		t.Error(err)
   287  	}
   288  	if len(files) != nfiles+2 {
   289  		t.Errorf("files=%d", len(files))
   290  	}
   291  
   292  	err = os.RemoveAll(dir)
   293  	if err != nil {
   294  		t.Fatal(err)
   295  	}
   296  }
   297  
   298  func cpTar(tr *tar.Reader, tw *tar.Writer) error {
   299  	for {
   300  		header, err := tr.Next()
   301  		if err != nil {
   302  			if err == io.EOF {
   303  				return nil
   304  			}
   305  
   306  			return err
   307  		}
   308  
   309  		if header.Typeflag != tar.TypeReg {
   310  			continue
   311  		}
   312  
   313  		if err = tw.WriteHeader(header); err != nil {
   314  			return err
   315  		}
   316  
   317  		_, err = io.Copy(tw, tr)
   318  		if err != nil {
   319  			log.Print(err)
   320  			return err
   321  		}
   322  	}
   323  }
   324  
   325  func TestArchiveFormat(t *testing.T) {
   326  	gzipTrailer = false
   327  
   328  	var buf bytes.Buffer
   329  
   330  	h := &ArchiveHandler{
   331  		Read: func(_ *url.URL, tr *tar.Reader) error {
   332  			tw := tar.NewWriter(&buf)
   333  			if err := cpTar(tr, tw); err != nil {
   334  				return err
   335  			}
   336  			return tw.Close()
   337  		},
   338  		Write: func(u *url.URL, tw *tar.Writer) error {
   339  			tr := tar.NewReader(&buf)
   340  
   341  			return cpTar(tr, tw)
   342  		},
   343  	}
   344  
   345  	for _, format := range []string{"tar", "tgz"} {
   346  		u := &url.URL{
   347  			Scheme:   ArchiveScheme,
   348  			Path:     ".",
   349  			RawQuery: "format=" + format,
   350  		}
   351  
   352  		_, err := h.Stat(u)
   353  		if err != nil {
   354  			t.Fatal(err)
   355  		}
   356  
   357  		// ToGuest: git archive | h.Read (to buf)
   358  		f, err := h.Open(u, OpenModeWriteOnly)
   359  		if err != nil {
   360  			t.Fatal(err)
   361  		}
   362  
   363  		cmd := exec.Command("git", "archive", "--format", format, "HEAD")
   364  
   365  		stdout, err := cmd.StdoutPipe()
   366  		if err != nil {
   367  			t.Fatal(err)
   368  		}
   369  
   370  		err = cmd.Start()
   371  		if err != nil {
   372  			t.Fatal(err)
   373  		}
   374  
   375  		n, err := io.Copy(f, stdout)
   376  		if err != nil {
   377  			t.Errorf("copy %d: %s", n, err)
   378  		}
   379  
   380  		err = f.Close()
   381  		if err != nil {
   382  			t.Error(err)
   383  		}
   384  
   385  		err = cmd.Wait()
   386  		if err != nil {
   387  			t.Error(err)
   388  		}
   389  
   390  		// FromGuest: h.Write (from buf) | tar -tvf-
   391  		f, err = h.Open(u, OpenModeReadOnly)
   392  		if err != nil {
   393  			t.Fatal(err)
   394  		}
   395  
   396  		cmd = exec.Command("tar", "-tvf-")
   397  
   398  		if format == "tgz" {
   399  			cmd.Args = append(cmd.Args, "-z")
   400  		}
   401  
   402  		stdin, err := cmd.StdinPipe()
   403  		if err != nil {
   404  			t.Fatal(err)
   405  		}
   406  
   407  		if testing.Verbose() {
   408  			cmd.Stderr = os.Stderr
   409  			cmd.Stdout = os.Stderr
   410  		}
   411  
   412  		err = cmd.Start()
   413  		if err != nil {
   414  			t.Fatal(err)
   415  		}
   416  
   417  		n, err = io.Copy(stdin, f)
   418  		if err != nil {
   419  			t.Errorf("copy %d: %s", n, err)
   420  		}
   421  
   422  		_ = stdin.Close()
   423  
   424  		err = f.Close()
   425  		if err != nil {
   426  			t.Error(err)
   427  		}
   428  
   429  		err = cmd.Wait()
   430  		if err != nil {
   431  			t.Error(err)
   432  		}
   433  
   434  		buf.Reset()
   435  	}
   436  }