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