github.com/vmware/govmomi@v0.43.0/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 }