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 }