github.com/openflowlabs/storage@v1.12.13/pkg/chrootarchive/archive_test.go (about) 1 package chrootarchive 2 3 import ( 4 "bytes" 5 "fmt" 6 "hash/crc32" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "syscall" 14 "testing" 15 "time" 16 17 "github.com/containers/storage/pkg/archive" 18 "github.com/containers/storage/pkg/idtools" 19 "github.com/containers/storage/pkg/reexec" 20 "github.com/containers/storage/pkg/system" 21 ) 22 23 func init() { 24 reexec.Init() 25 } 26 27 var chrootArchiver = NewArchiver(nil) 28 29 func TarUntar(src, dst string) error { 30 return chrootArchiver.TarUntar(src, dst) 31 } 32 33 func CopyFileWithTar(src, dst string) (err error) { 34 return chrootArchiver.CopyFileWithTar(src, dst) 35 } 36 37 func UntarPath(src, dst string) error { 38 return chrootArchiver.UntarPath(src, dst) 39 } 40 41 func CopyWithTar(src, dst string) error { 42 return chrootArchiver.CopyWithTar(src, dst) 43 } 44 45 func TestChrootTarUntar(t *testing.T) { 46 tmpdir, err := ioutil.TempDir("", "storage-TestChrootTarUntar") 47 if err != nil { 48 t.Fatal(err) 49 } 50 defer os.RemoveAll(tmpdir) 51 src := filepath.Join(tmpdir, "src") 52 if err := system.MkdirAll(src, 0700, ""); err != nil { 53 t.Fatal(err) 54 } 55 if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil { 56 t.Fatal(err) 57 } 58 if err := ioutil.WriteFile(filepath.Join(src, "lolo"), []byte("hello lolo"), 0644); err != nil { 59 t.Fatal(err) 60 } 61 stream, err := archive.Tar(src, archive.Uncompressed) 62 if err != nil { 63 t.Fatal(err) 64 } 65 dest := filepath.Join(tmpdir, "src") 66 if err := system.MkdirAll(dest, 0700, ""); err != nil { 67 t.Fatal(err) 68 } 69 if err := Untar(stream, dest, &archive.TarOptions{ExcludePatterns: []string{"lolo"}}); err != nil { 70 t.Fatal(err) 71 } 72 } 73 74 // gh#10426: Verify the fix for having a huge excludes list (like on `docker load` with large # of 75 // local images) 76 func TestChrootUntarWithHugeExcludesList(t *testing.T) { 77 tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarHugeExcludes") 78 if err != nil { 79 t.Fatal(err) 80 } 81 defer os.RemoveAll(tmpdir) 82 src := filepath.Join(tmpdir, "src") 83 if err := system.MkdirAll(src, 0700, ""); err != nil { 84 t.Fatal(err) 85 } 86 if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil { 87 t.Fatal(err) 88 } 89 stream, err := archive.Tar(src, archive.Uncompressed) 90 if err != nil { 91 t.Fatal(err) 92 } 93 dest := filepath.Join(tmpdir, "dest") 94 if err := system.MkdirAll(dest, 0700, ""); err != nil { 95 t.Fatal(err) 96 } 97 options := &archive.TarOptions{} 98 //65534 entries of 64-byte strings ~= 4MB of environment space which should overflow 99 //on most systems when passed via environment or command line arguments 100 excludes := make([]string, 65534) 101 for i := 0; i < 65534; i++ { 102 excludes[i] = strings.Repeat(string(i), 64) 103 } 104 options.ExcludePatterns = excludes 105 if err := Untar(stream, dest, options); err != nil { 106 t.Fatal(err) 107 } 108 } 109 110 func TestChrootUntarEmptyArchive(t *testing.T) { 111 tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarEmptyArchive") 112 if err != nil { 113 t.Fatal(err) 114 } 115 defer os.RemoveAll(tmpdir) 116 if err := Untar(nil, tmpdir, nil); err == nil { 117 t.Fatal("expected error on empty archive") 118 } 119 } 120 121 func prepareSourceDirectory(numberOfFiles int, targetPath string, makeSymLinks bool) (int, error) { 122 fileData := []byte("fooo") 123 for n := 0; n < numberOfFiles; n++ { 124 fileName := fmt.Sprintf("file-%d", n) 125 if err := ioutil.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil { 126 return 0, err 127 } 128 if makeSymLinks { 129 if err := os.Symlink(filepath.Join(targetPath, fileName), filepath.Join(targetPath, fileName+"-link")); err != nil { 130 return 0, err 131 } 132 } 133 } 134 totalSize := numberOfFiles * len(fileData) 135 return totalSize, nil 136 } 137 138 func getHash(filename string) (uint32, error) { 139 stream, err := ioutil.ReadFile(filename) 140 if err != nil { 141 return 0, err 142 } 143 hash := crc32.NewIEEE() 144 hash.Write(stream) 145 return hash.Sum32(), nil 146 } 147 148 func compareDirectories(src string, dest string) error { 149 changes, err := archive.ChangesDirs(dest, &idtools.IDMappings{}, src, &idtools.IDMappings{}) 150 if err != nil { 151 return err 152 } 153 if len(changes) > 0 { 154 return fmt.Errorf("Unexpected differences after untar: %v", changes) 155 } 156 return nil 157 } 158 159 func compareDirectoriesChown(src string, dest string, uid, gid int) error { 160 uidmap := []idtools.IDMap{{ContainerID: 0, HostID: uid, Size: 1}} 161 gidmap := []idtools.IDMap{{ContainerID: 0, HostID: gid, Size: 1}} 162 mappings := idtools.NewIDMappingsFromMaps(uidmap, gidmap) 163 changes, err := archive.ChangesDirs(dest, mappings, src, &idtools.IDMappings{}) 164 if err != nil { 165 return err 166 } 167 if len(changes) > 0 { 168 return fmt.Errorf("Unexpected differences after untar: %v", changes) 169 } 170 return nil 171 } 172 173 func compareFiles(src string, dest string) error { 174 srcHash, err := getHash(src) 175 if err != nil { 176 return err 177 } 178 destHash, err := getHash(dest) 179 if err != nil { 180 return err 181 } 182 if srcHash != destHash { 183 return fmt.Errorf("%s is different from %s", src, dest) 184 } 185 return nil 186 } 187 188 func compareFilesChown(src string, dest string, uid, gid int) error { 189 if err := compareFiles(src, dest); err != nil { 190 return err 191 } 192 fi, err := os.Lstat(dest) 193 if err == nil { 194 statuid := fi.Sys().(*syscall.Stat_t).Uid 195 statgid := fi.Sys().(*syscall.Stat_t).Gid 196 if statuid != uint32(uid) || statgid != uint32(gid) { 197 return fmt.Errorf("%d:%d ownership on %s is different expected %d:%d", statuid, statgid, dest, uid, gid) 198 } 199 } 200 return err 201 202 } 203 204 func TestChrootTarUntarWithSymlink(t *testing.T) { 205 // TODO Windows: Figure out why this is failing 206 if runtime.GOOS == "windows" { 207 t.Skip("Failing on Windows") 208 } 209 tmpdir, err := ioutil.TempDir("", "storage-TestChrootTarUntarWithSymlink") 210 if err != nil { 211 t.Fatal(err) 212 } 213 defer os.RemoveAll(tmpdir) 214 src := filepath.Join(tmpdir, "src") 215 if err := system.MkdirAll(src, 0700, ""); err != nil { 216 t.Fatal(err) 217 } 218 if _, err := prepareSourceDirectory(10, src, false); err != nil { 219 t.Fatal(err) 220 } 221 dest := filepath.Join(tmpdir, "dest") 222 if err := TarUntar(src, dest); err != nil { 223 t.Fatal(err) 224 } 225 if err := compareDirectories(src, dest); err != nil { 226 t.Fatal(err) 227 } 228 } 229 230 func TestChrootCopyWithTar(t *testing.T) { 231 // TODO Windows: Figure out why this is failing 232 if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { 233 t.Skip("Failing on Windows and Solaris") 234 } 235 tmpdir, err := ioutil.TempDir("", "storage-TestChrootCopyWithTar") 236 if err != nil { 237 t.Fatal(err) 238 } 239 defer os.RemoveAll(tmpdir) 240 src := filepath.Join(tmpdir, "src") 241 if err := system.MkdirAll(src, 0700, ""); err != nil { 242 t.Fatal(err) 243 } 244 if _, err := prepareSourceDirectory(10, src, true); err != nil { 245 t.Fatal(err) 246 } 247 248 // Copy directory 249 dest := filepath.Join(tmpdir, "dest") 250 if err := CopyWithTar(src, dest); err != nil { 251 t.Fatal(err) 252 } 253 if err := compareDirectories(src, dest); err != nil { 254 t.Fatal(err) 255 } 256 257 // Copy file 258 srcfile := filepath.Join(src, "file-1") 259 dest = filepath.Join(tmpdir, "destFile") 260 destfile := filepath.Join(dest, "file-1") 261 if err := CopyWithTar(srcfile, destfile); err != nil { 262 t.Fatal(err) 263 } 264 if err := compareFiles(srcfile, destfile); err != nil { 265 t.Fatal(err) 266 } 267 268 // Copy symbolic link 269 srcLinkfile := filepath.Join(src, "file-1-link") 270 dest = filepath.Join(tmpdir, "destSymlink") 271 destLinkfile := filepath.Join(dest, "file-1-link") 272 if err := CopyWithTar(srcLinkfile, destLinkfile); err != nil { 273 t.Fatal(err) 274 } 275 if err := compareFiles(srcLinkfile, destLinkfile); err != nil { 276 t.Fatal(err) 277 } 278 } 279 280 func TestChrootCopyWithTarAndChown(t *testing.T) { 281 // TODO Windows: Figure out why this is failing 282 if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { 283 t.Skip("Failing on Windows and Solaris") 284 } 285 tmpdir, err := ioutil.TempDir("", "storage-TestChrootCopyWithTar") 286 if err != nil { 287 t.Fatal(err) 288 } 289 defer os.RemoveAll(tmpdir) 290 src := filepath.Join(tmpdir, "src") 291 if err := system.MkdirAll(src, 0700, ""); err != nil { 292 t.Fatal(err) 293 } 294 if _, err := prepareSourceDirectory(10, src, true); err != nil { 295 t.Fatal(err) 296 } 297 uid := 1000 298 gid := 1001 299 owner := idtools.IDPair{UID: uid, GID: gid} 300 idMap := idtools.IDMap{ContainerID: 0, HostID: 0, Size: 65536} 301 uidMap := []idtools.IDMap{idMap} 302 gidMap := []idtools.IDMap{idMap} 303 copyFunc := CopyWithTarAndChown(&owner, nil, uidMap, gidMap) 304 // Copy directory 305 dest := filepath.Join(tmpdir, "dest") 306 if err := copyFunc(src, dest); err != nil { 307 t.Fatal(err) 308 } 309 if err := compareDirectoriesChown(src, dest, uid, gid); err != nil { 310 t.Fatal(err) 311 } 312 313 // Copy file 314 srcfile := filepath.Join(src, "file-1") 315 dest = filepath.Join(tmpdir, "destFile") 316 destfile := filepath.Join(dest, "file-1") 317 if err := copyFunc(srcfile, destfile); err != nil { 318 t.Fatal(err) 319 } 320 if err := compareFilesChown(srcfile, destfile, uid, gid); err != nil { 321 t.Fatal(err) 322 } 323 324 // Copy symbolic link 325 srcLinkfile := filepath.Join(src, "file-1-link") 326 dest = filepath.Join(tmpdir, "destSymlink") 327 destLinkfile := filepath.Join(dest, "file-1-link") 328 if err := copyFunc(srcLinkfile, destLinkfile); err != nil { 329 t.Fatal(err) 330 } 331 if err := compareFilesChown(srcLinkfile, destLinkfile, uid, gid); err != nil { 332 t.Fatal(err) 333 } 334 } 335 336 func TestChrootCopyFileWithTar(t *testing.T) { 337 tmpdir, err := ioutil.TempDir("", "storage-TestChrootCopyFileWithTar") 338 if err != nil { 339 t.Fatal(err) 340 } 341 defer os.RemoveAll(tmpdir) 342 src := filepath.Join(tmpdir, "src") 343 if err := system.MkdirAll(src, 0700, ""); err != nil { 344 t.Fatal(err) 345 } 346 if _, err := prepareSourceDirectory(10, src, true); err != nil { 347 t.Fatal(err) 348 } 349 350 // Copy directory 351 dest := filepath.Join(tmpdir, "dest") 352 if err := CopyFileWithTar(src, dest); err == nil { 353 t.Fatal("Expected error on copying directory") 354 } 355 356 // Copy file 357 srcfile := filepath.Join(src, "file-1") 358 dest = filepath.Join(tmpdir, "destFile") 359 destfile := filepath.Join(dest, "file-1") 360 if err := CopyFileWithTar(srcfile, destfile); err != nil { 361 t.Fatal(err) 362 } 363 if err := compareFiles(srcfile, destfile); err != nil { 364 t.Fatal(err) 365 } 366 367 // Copy symbolic link 368 srcLinkfile := filepath.Join(src, "file-1-link") 369 dest = filepath.Join(tmpdir, "destSymlink") 370 destLinkfile := filepath.Join(dest, "file-1-link") 371 if err := CopyFileWithTar(srcLinkfile, destLinkfile); err != nil { 372 t.Fatal(err) 373 } 374 if err := compareFiles(srcLinkfile, destLinkfile); err != nil { 375 t.Fatal(err) 376 } 377 } 378 379 func TestChrootCopyFileWithTarAndChown(t *testing.T) { 380 tmpdir, err := ioutil.TempDir("", "storage-TestChrootCopyFileWithTar") 381 if err != nil { 382 t.Fatal(err) 383 } 384 defer os.RemoveAll(tmpdir) 385 src := filepath.Join(tmpdir, "src") 386 if err := system.MkdirAll(src, 0700, ""); err != nil { 387 t.Fatal(err) 388 } 389 if _, err := prepareSourceDirectory(10, src, true); err != nil { 390 t.Fatal(err) 391 } 392 393 uid := 1000 394 gid := 1001 395 owner := idtools.IDPair{UID: uid, GID: gid} 396 idMap := idtools.IDMap{ContainerID: 0, HostID: 0, Size: 65536} 397 uidMap := []idtools.IDMap{idMap} 398 gidMap := []idtools.IDMap{idMap} 399 copyFunc := CopyFileWithTarAndChown(&owner, nil, uidMap, gidMap) 400 // Copy directory 401 dest := filepath.Join(tmpdir, "dest") 402 if err := copyFunc(src, dest); err == nil { 403 t.Fatal("Expected error on copying directory") 404 } 405 406 // Copy file 407 srcfile := filepath.Join(src, "file-1") 408 dest = filepath.Join(tmpdir, "destFile") 409 destfile := filepath.Join(dest, "file-1") 410 if err := copyFunc(srcfile, destfile); err != nil { 411 t.Fatal(err) 412 } 413 if err := compareFilesChown(srcfile, destfile, uid, gid); err != nil { 414 t.Fatal(err) 415 } 416 417 // Copy symbolic link 418 srcLinkfile := filepath.Join(src, "file-1-link") 419 dest = filepath.Join(tmpdir, "destSymlink") 420 destLinkfile := filepath.Join(dest, "file-1-link") 421 if err := copyFunc(srcLinkfile, destLinkfile); err != nil { 422 t.Fatal(err) 423 } 424 if err := compareFilesChown(srcLinkfile, destLinkfile, uid, gid); err != nil { 425 t.Fatal(err) 426 } 427 } 428 429 func TestChrootUntarPath(t *testing.T) { 430 // TODO Windows: Figure out why this is failing 431 if runtime.GOOS == "windows" { 432 t.Skip("Failing on Windows") 433 } 434 tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarPath") 435 if err != nil { 436 t.Fatal(err) 437 } 438 defer os.RemoveAll(tmpdir) 439 src := filepath.Join(tmpdir, "src") 440 if err := system.MkdirAll(src, 0700, ""); err != nil { 441 t.Fatal(err) 442 } 443 if _, err := prepareSourceDirectory(10, src, false); err != nil { 444 t.Fatal(err) 445 } 446 dest := filepath.Join(tmpdir, "dest") 447 // Untar a directory 448 if err := UntarPath(src, dest); err == nil { 449 t.Fatal("Expected error on untaring a directory") 450 } 451 452 tarFunc := archive.TarPath(nil, nil) 453 // Untar a tar file 454 stream, err := tarFunc(src) 455 if err != nil { 456 t.Fatal(err) 457 } 458 buf := new(bytes.Buffer) 459 buf.ReadFrom(stream) 460 tarfile := filepath.Join(tmpdir, "src.tar") 461 if err := ioutil.WriteFile(tarfile, buf.Bytes(), 0644); err != nil { 462 t.Fatal(err) 463 } 464 if err := UntarPath(tarfile, dest); err != nil { 465 t.Fatal(err) 466 } 467 if err := compareDirectories(src, dest); err != nil { 468 t.Fatal(err) 469 } 470 } 471 472 func TestChrootUntarPathAndChown(t *testing.T) { 473 // TODO Windows: Figure out why this is failing 474 if runtime.GOOS == "windows" { 475 t.Skip("Failing on Windows") 476 } 477 tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarPath") 478 if err != nil { 479 t.Fatal(err) 480 } 481 defer os.RemoveAll(tmpdir) 482 src := filepath.Join(tmpdir, "src") 483 if err := system.MkdirAll(src, 0700, ""); err != nil { 484 t.Fatal(err) 485 } 486 if _, err := prepareSourceDirectory(10, src, false); err != nil { 487 t.Fatal(err) 488 } 489 dest := filepath.Join(tmpdir, "dest") 490 491 uid := 1000 492 gid := 1001 493 owner := idtools.IDPair{UID: uid, GID: gid} 494 idMap := idtools.IDMap{ContainerID: 0, HostID: 0, Size: 65536} 495 uidMap := []idtools.IDMap{idMap} 496 gidMap := []idtools.IDMap{idMap} 497 untarFunc := UntarPathAndChown(&owner, nil, uidMap, gidMap) 498 // Untar a directory 499 if err := untarFunc(src, dest); err == nil { 500 t.Fatal("Expected error on untaring a directory") 501 } 502 503 // Untar a tar file 504 tarFunc := archive.TarPath(nil, nil) 505 // Untar a tar file 506 stream, err := tarFunc(src) 507 if err != nil { 508 t.Fatal(err) 509 } 510 511 buf := new(bytes.Buffer) 512 buf.ReadFrom(stream) 513 tarfile := filepath.Join(tmpdir, "src.tar") 514 if err := ioutil.WriteFile(tarfile, buf.Bytes(), 0644); err != nil { 515 t.Fatal(err) 516 } 517 if err := untarFunc(tarfile, dest); err != nil { 518 t.Fatal(err) 519 } 520 if err := compareDirectoriesChown(src, dest, uid, gid); err != nil { 521 t.Fatal(err) 522 } 523 } 524 525 type slowEmptyTarReader struct { 526 size int 527 offset int 528 chunkSize int 529 } 530 531 // Read is a slow reader of an empty tar (like the output of "tar c --files-from /dev/null") 532 func (s *slowEmptyTarReader) Read(p []byte) (int, error) { 533 time.Sleep(100 * time.Millisecond) 534 count := s.chunkSize 535 if len(p) < s.chunkSize { 536 count = len(p) 537 } 538 for i := 0; i < count; i++ { 539 p[i] = 0 540 } 541 s.offset += count 542 if s.offset > s.size { 543 return count, io.EOF 544 } 545 return count, nil 546 } 547 548 func TestChrootUntarEmptyArchiveFromSlowReader(t *testing.T) { 549 tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarEmptyArchiveFromSlowReader") 550 if err != nil { 551 t.Fatal(err) 552 } 553 defer os.RemoveAll(tmpdir) 554 dest := filepath.Join(tmpdir, "dest") 555 if err := system.MkdirAll(dest, 0700, ""); err != nil { 556 t.Fatal(err) 557 } 558 stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024} 559 if err := Untar(stream, dest, nil); err != nil { 560 t.Fatal(err) 561 } 562 } 563 564 func TestChrootApplyEmptyArchiveFromSlowReader(t *testing.T) { 565 tmpdir, err := ioutil.TempDir("", "storage-TestChrootApplyEmptyArchiveFromSlowReader") 566 if err != nil { 567 t.Fatal(err) 568 } 569 defer os.RemoveAll(tmpdir) 570 dest := filepath.Join(tmpdir, "dest") 571 if err := system.MkdirAll(dest, 0700, ""); err != nil { 572 t.Fatal(err) 573 } 574 stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024} 575 if _, err := ApplyLayer(dest, stream); err != nil { 576 t.Fatal(err) 577 } 578 } 579 580 func TestChrootApplyDotDotFile(t *testing.T) { 581 tmpdir, err := ioutil.TempDir("", "storage-TestChrootApplyDotDotFile") 582 if err != nil { 583 t.Fatal(err) 584 } 585 defer os.RemoveAll(tmpdir) 586 src := filepath.Join(tmpdir, "src") 587 if err := system.MkdirAll(src, 0700, ""); err != nil { 588 t.Fatal(err) 589 } 590 if err := ioutil.WriteFile(filepath.Join(src, "..gitme"), []byte(""), 0644); err != nil { 591 t.Fatal(err) 592 } 593 stream, err := archive.Tar(src, archive.Uncompressed) 594 if err != nil { 595 t.Fatal(err) 596 } 597 dest := filepath.Join(tmpdir, "dest") 598 if err := system.MkdirAll(dest, 0700, ""); err != nil { 599 t.Fatal(err) 600 } 601 if _, err := ApplyLayer(dest, stream); err != nil { 602 t.Fatal(err) 603 } 604 }