github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/pkg/tar/tar_test.go (about) 1 // Copyright 2014 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tar 16 17 import ( 18 "archive/tar" 19 "fmt" 20 "io" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strings" 26 "syscall" 27 "testing" 28 "time" 29 30 "github.com/rkt/rkt/pkg/multicall" 31 "github.com/rkt/rkt/pkg/sys" 32 "github.com/rkt/rkt/pkg/user" 33 ) 34 35 func init() { 36 multicall.MaybeExec() 37 } 38 39 type testTarEntry struct { 40 header *tar.Header 41 contents string 42 } 43 44 func newTestTar(entries []*testTarEntry) (string, error) { 45 t, err := ioutil.TempFile("", "test-tar") 46 if err != nil { 47 return "", err 48 } 49 defer t.Close() 50 tw := tar.NewWriter(t) 51 for _, entry := range entries { 52 // Add default mode 53 if entry.header.Mode == 0 { 54 if entry.header.Typeflag == tar.TypeDir { 55 entry.header.Mode = 0755 56 } else { 57 entry.header.Mode = 0644 58 } 59 } 60 // Add calling user uid and gid or tests will fail 61 entry.header.Uid = os.Getuid() 62 entry.header.Gid = os.Getgid() 63 if err := tw.WriteHeader(entry.header); err != nil { 64 return "", err 65 } 66 if _, err := io.WriteString(tw, entry.contents); err != nil { 67 return "", err 68 } 69 } 70 if err := tw.Close(); err != nil { 71 return "", err 72 } 73 return t.Name(), nil 74 } 75 76 type fileInfo struct { 77 path string 78 typeflag byte 79 size int64 80 contents string 81 mode os.FileMode 82 } 83 84 func fileInfoSliceToMap(slice []*fileInfo) map[string]*fileInfo { 85 fim := make(map[string]*fileInfo, len(slice)) 86 for _, fi := range slice { 87 fim[fi.path] = fi 88 } 89 return fim 90 } 91 92 func checkExpectedFiles(dir string, expectedFiles map[string]*fileInfo) error { 93 files := make(map[string]*fileInfo) 94 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 95 fm := info.Mode() 96 if path == dir { 97 return nil 98 } 99 relpath, err := filepath.Rel(dir, path) 100 if err != nil { 101 return err 102 } 103 switch { 104 case fm.IsRegular(): 105 files[relpath] = &fileInfo{path: relpath, typeflag: tar.TypeReg, size: info.Size(), mode: info.Mode().Perm()} 106 case info.IsDir(): 107 files[relpath] = &fileInfo{path: relpath, typeflag: tar.TypeDir, mode: info.Mode().Perm()} 108 case fm&os.ModeSymlink != 0: 109 files[relpath] = &fileInfo{path: relpath, typeflag: tar.TypeSymlink, mode: info.Mode()} 110 default: 111 return fmt.Errorf("file mode not handled: %v", fm) 112 } 113 114 return nil 115 }) 116 if err != nil { 117 return err 118 } 119 120 // Set defaults for not specified expected file mode 121 for _, ef := range expectedFiles { 122 if ef.mode == 0 { 123 if ef.typeflag == tar.TypeDir { 124 ef.mode = 0755 125 } else { 126 ef.mode = 0644 127 } 128 } 129 } 130 131 for _, ef := range expectedFiles { 132 _, ok := files[ef.path] 133 if !ok { 134 return fmt.Errorf("Expected file %q not in files", ef.path) 135 } 136 137 } 138 139 for _, file := range files { 140 ef, ok := expectedFiles[file.path] 141 if !ok { 142 return fmt.Errorf("file %q not in expectedFiles", file.path) 143 } 144 if ef.typeflag != file.typeflag { 145 return fmt.Errorf("file %q: file type differs: wanted: %d, got: %d", file.path, ef.typeflag, file.typeflag) 146 } 147 if ef.typeflag == tar.TypeReg { 148 if ef.size != file.size { 149 return fmt.Errorf("file %q: size differs: wanted %d, wanted: %d", file.path, ef.size, file.size) 150 } 151 if ef.contents != "" { 152 buf, err := ioutil.ReadFile(filepath.Join(dir, file.path)) 153 if err != nil { 154 return fmt.Errorf("unexpected error: %v", err) 155 } 156 if string(buf) != ef.contents { 157 return fmt.Errorf("unexpected contents, wanted: %s, got: %s", ef.contents, buf) 158 } 159 160 } 161 } 162 // Check modes but ignore symlinks 163 if ef.mode != file.mode && ef.typeflag != tar.TypeSymlink { 164 return fmt.Errorf("file %q: mode differs: wanted %#o, got: %#o", file.path, ef.mode, file.mode) 165 } 166 167 } 168 return nil 169 } 170 171 func TestExtractTarFolders(t *testing.T) { 172 if !sys.HasChrootCapability() { 173 t.Skipf("chroot capability not available. Disabling test.") 174 } 175 testExtractTarFolders(t, extractTarHelper) 176 } 177 func TestExtractTarFoldersInsecure(t *testing.T) { 178 testExtractTarFolders(t, extractTarInsecureHelper) 179 } 180 func testExtractTarFolders(t *testing.T, extractTar func(io.Reader, string) error) { 181 entries := []*testTarEntry{ 182 { 183 contents: "foo", 184 header: &tar.Header{ 185 Name: "deep/folder/foo.txt", 186 Size: 3, 187 }, 188 }, 189 { 190 header: &tar.Header{ 191 Name: "deep/folder/", 192 Typeflag: tar.TypeDir, 193 Mode: int64(0747), 194 }, 195 }, 196 { 197 contents: "bar", 198 header: &tar.Header{ 199 Name: "deep/folder/bar.txt", 200 Size: 3, 201 }, 202 }, 203 { 204 header: &tar.Header{ 205 Name: "deep/folder2/symlink.txt", 206 Typeflag: tar.TypeSymlink, 207 Linkname: "deep/folder/foo.txt", 208 }, 209 }, 210 { 211 header: &tar.Header{ 212 Name: "deep/folder2/", 213 Typeflag: tar.TypeDir, 214 Mode: int64(0747), 215 }, 216 }, 217 { 218 contents: "bar", 219 header: &tar.Header{ 220 Name: "deep/folder2/bar.txt", 221 Size: 3, 222 }, 223 }, 224 { 225 header: &tar.Header{ 226 Name: "deep/deep/folder", 227 Typeflag: tar.TypeDir, 228 Mode: int64(0755), 229 }, 230 }, 231 { 232 header: &tar.Header{ 233 Name: "deep/deep/", 234 Typeflag: tar.TypeDir, 235 Mode: int64(0747), 236 }, 237 }, 238 } 239 240 testTarPath, err := newTestTar(entries) 241 if err != nil { 242 t.Errorf("unexpected error: %v", err) 243 } 244 defer os.Remove(testTarPath) 245 containerTar, err := os.Open(testTarPath) 246 if err != nil { 247 t.Errorf("unexpected error: %v", err) 248 } 249 defer containerTar.Close() 250 tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") 251 if err != nil { 252 t.Errorf("unexpected error: %v", err) 253 } 254 os.RemoveAll(tmpdir) 255 err = os.MkdirAll(tmpdir, 0755) 256 if err != nil { 257 t.Errorf("unexpected error: %v", err) 258 } 259 defer os.RemoveAll(tmpdir) 260 err = extractTar(containerTar, tmpdir) 261 if err != nil { 262 t.Errorf("unexpected error: %v", err) 263 } 264 matches, err := filepath.Glob(filepath.Join(tmpdir, "deep/folder/*.txt")) 265 if err != nil { 266 t.Errorf("unexpected error: %v", err) 267 } 268 if len(matches) != 2 { 269 t.Errorf("unexpected number of files found: %d, wanted 2", len(matches)) 270 } 271 matches, err = filepath.Glob(filepath.Join(tmpdir, "deep/folder2/*.txt")) 272 if err != nil { 273 t.Errorf("unexpected error: %v", err) 274 } 275 if len(matches) != 2 { 276 t.Errorf("unexpected number of files found: %d, wanted 2", len(matches)) 277 } 278 279 dirInfo, err := os.Lstat(filepath.Join(tmpdir, "deep/folder")) 280 if err != nil { 281 t.Errorf("unexpected error: %v", err) 282 } else if dirInfo.Mode().Perm() != os.FileMode(0747) { 283 t.Errorf("unexpected dir mode: %s", dirInfo.Mode()) 284 } 285 dirInfo, err = os.Lstat(filepath.Join(tmpdir, "deep/deep")) 286 if err != nil { 287 t.Errorf("unexpected error: %v", err) 288 } else if dirInfo.Mode().Perm() != os.FileMode(0747) { 289 t.Errorf("unexpected dir mode: %s", dirInfo.Mode()) 290 } 291 } 292 293 func TestExtractTarFileToBuf(t *testing.T) { 294 entries := []*testTarEntry{ 295 { 296 header: &tar.Header{ 297 Name: "folder/", 298 Typeflag: tar.TypeDir, 299 Mode: int64(0747), 300 }, 301 }, 302 { 303 contents: "foo", 304 header: &tar.Header{ 305 Name: "folder/foo.txt", 306 Size: 3, 307 }, 308 }, 309 { 310 contents: "bar", 311 header: &tar.Header{ 312 Name: "folder/bar.txt", 313 Size: 3, 314 }, 315 }, 316 { 317 header: &tar.Header{ 318 Name: "folder/symlink.txt", 319 Typeflag: tar.TypeSymlink, 320 Linkname: "folder/foo.txt", 321 }, 322 }, 323 } 324 testTarPath, err := newTestTar(entries) 325 if err != nil { 326 t.Fatalf("unexpected error: %v", err) 327 } 328 defer os.Remove(testTarPath) 329 containerTar1, err := os.Open(testTarPath) 330 if err != nil { 331 t.Fatalf("unexpected error: %v", err) 332 } 333 defer containerTar1.Close() 334 335 tr := tar.NewReader(containerTar1) 336 buf, err := extractFileFromTar(tr, "folder/foo.txt") 337 if err != nil { 338 t.Errorf("unexpected error: %v", err) 339 } 340 if string(buf) != "foo" { 341 t.Errorf("unexpected contents, wanted: %s, got: %s", "foo", buf) 342 } 343 344 containerTar2, err := os.Open(testTarPath) 345 if err != nil { 346 t.Errorf("unexpected error: %v", err) 347 } 348 defer containerTar2.Close() 349 tr = tar.NewReader(containerTar2) 350 buf, err = extractFileFromTar(tr, "folder/symlink.txt") 351 if err == nil { 352 t.Errorf("expected error") 353 } 354 } 355 356 func TestExtractTarPWL(t *testing.T) { 357 if !sys.HasChrootCapability() { 358 t.Skipf("chroot capability not available. Disabling test.") 359 } 360 testExtractTarPWL(t, extractTarHelperPWL) 361 } 362 func TestExtractTarPWLInsecure(t *testing.T) { 363 testExtractTarPWL(t, extractTarInsecureHelperPWL) 364 } 365 func testExtractTarPWL(t *testing.T, extractTar func(rdr io.Reader, target string, pwl PathWhitelistMap) error) { 366 entries := []*testTarEntry{ 367 { 368 header: &tar.Header{ 369 Name: "folder/", 370 Typeflag: tar.TypeDir, 371 Mode: int64(0747), 372 }, 373 }, 374 { 375 contents: "foo", 376 header: &tar.Header{ 377 Name: "folder/foo.txt", 378 Size: 3, 379 }, 380 }, 381 { 382 contents: "bar", 383 header: &tar.Header{ 384 Name: "folder/bar.txt", 385 Size: 3, 386 }, 387 }, 388 { 389 header: &tar.Header{ 390 Name: "folder/symlink.txt", 391 Typeflag: tar.TypeSymlink, 392 Linkname: "folder/foo.txt", 393 }, 394 }, 395 } 396 testTarPath, err := newTestTar(entries) 397 if err != nil { 398 t.Errorf("unexpected error: %v", err) 399 } 400 defer os.Remove(testTarPath) 401 containerTar, err := os.Open(testTarPath) 402 if err != nil { 403 t.Errorf("unexpected error: %v", err) 404 } 405 defer containerTar.Close() 406 tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") 407 if err != nil { 408 t.Errorf("unexpected error: %v", err) 409 } 410 defer os.RemoveAll(tmpdir) 411 412 pwl := make(PathWhitelistMap) 413 pwl["folder/foo.txt"] = struct{}{} 414 err = extractTar(containerTar, tmpdir, pwl) 415 if err != nil { 416 t.Errorf("unexpected error: %v", err) 417 } 418 matches, err := filepath.Glob(filepath.Join(tmpdir, "folder/*.txt")) 419 if err != nil { 420 t.Errorf("unexpected error: %v", err) 421 } 422 if len(matches) != 1 { 423 t.Errorf("unexpected number of files found: %d, wanted 1", len(matches)) 424 } 425 } 426 427 func TestExtractTarOverwrite(t *testing.T) { 428 if !sys.HasChrootCapability() { 429 t.Skipf("chroot capability not available. Disabling test.") 430 } 431 testExtractTarOverwrite(t, extractTarOverwriteHelper) 432 } 433 func TestExtractTarOverwriteInsecure(t *testing.T) { 434 testExtractTarOverwrite(t, extractTarInsecureHelper) 435 } 436 func testExtractTarOverwrite(t *testing.T, extractTar func(io.Reader, string) error) { 437 tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") 438 if err != nil { 439 t.Fatalf("unexpected error: %v", err) 440 } 441 defer os.RemoveAll(tmpdir) 442 443 entries := []*testTarEntry{ 444 { 445 contents: "hello", 446 header: &tar.Header{ 447 Name: "hello.txt", 448 Size: 5, 449 }, 450 }, 451 { 452 header: &tar.Header{ 453 Name: "afolder", 454 Typeflag: tar.TypeDir, 455 }, 456 }, 457 { 458 contents: "hello", 459 header: &tar.Header{ 460 Name: "afolder/hello.txt", 461 Size: 5, 462 }, 463 }, 464 { 465 contents: "hello", 466 header: &tar.Header{ 467 Name: "afile", 468 Size: 5, 469 }, 470 }, 471 { 472 header: &tar.Header{ 473 Name: "folder01", 474 Typeflag: tar.TypeDir, 475 }, 476 }, 477 { 478 contents: "hello", 479 header: &tar.Header{ 480 Name: "folder01/file01", 481 Size: 5, 482 }, 483 }, 484 { 485 contents: "hello", 486 header: &tar.Header{ 487 Name: "filesymlinked", 488 Size: 5, 489 }, 490 }, 491 { 492 header: &tar.Header{ 493 Name: "linktofile", 494 Linkname: "filesymlinked", 495 Typeflag: tar.TypeSymlink, 496 }, 497 }, 498 499 { 500 header: &tar.Header{ 501 Name: "dirsymlinked", 502 Typeflag: tar.TypeDir, 503 }, 504 }, 505 { 506 header: &tar.Header{ 507 Name: "linktodir", 508 Linkname: "dirsymlinked", 509 Typeflag: tar.TypeSymlink, 510 }, 511 }, 512 } 513 514 testTarPath, err := newTestTar(entries) 515 if err != nil { 516 t.Fatalf("unexpected error: %v", err) 517 } 518 defer os.Remove(testTarPath) 519 containerTar1, err := os.Open(testTarPath) 520 if err != nil { 521 t.Fatalf("unexpected error: %v", err) 522 } 523 defer containerTar1.Close() 524 err = extractTar(containerTar1, tmpdir) 525 if err != nil { 526 t.Fatalf("unexpected error: %v", err) 527 } 528 529 // Now overwrite: 530 // a file with a new file 531 // a dir with a file 532 entries = []*testTarEntry{ 533 { 534 contents: "newhello", 535 header: &tar.Header{ 536 Name: "hello.txt", 537 Size: 8, 538 }, 539 }, 540 // Now this is a file 541 { 542 contents: "nowafile", 543 header: &tar.Header{ 544 Name: "afolder", 545 Typeflag: tar.TypeReg, 546 Size: 8, 547 }, 548 }, 549 // Now this is a dir 550 { 551 header: &tar.Header{ 552 Name: "afile", 553 Typeflag: tar.TypeDir, 554 }, 555 }, 556 // Overwrite symlink to a file with a regular file 557 // the linked file shouldn't be removed 558 { 559 contents: "filereplacingsymlink", 560 header: &tar.Header{ 561 Name: "linktofile", 562 Typeflag: tar.TypeReg, 563 Size: 20, 564 }, 565 }, 566 // Overwrite symlink to a dir with a regular file 567 // the linked directory and all its contents shouldn't be 568 // removed 569 { 570 contents: "filereplacingsymlink", 571 header: &tar.Header{ 572 Name: "linktodir", 573 Typeflag: tar.TypeReg, 574 Size: 20, 575 }, 576 }, 577 // folder01 already exists and shouldn't be removed (keeping folder01/file01) 578 { 579 header: &tar.Header{ 580 Name: "folder01", 581 Typeflag: tar.TypeDir, 582 Mode: int64(0755), 583 }, 584 }, 585 { 586 contents: "hello", 587 header: &tar.Header{ 588 Name: "folder01/file02", 589 Size: 5, 590 Mode: int64(0644), 591 }, 592 }, 593 } 594 testTarPath, err = newTestTar(entries) 595 if err != nil { 596 t.Errorf("unexpected error: %v", err) 597 } 598 defer os.Remove(testTarPath) 599 containerTar2, err := os.Open(testTarPath) 600 if err != nil { 601 t.Errorf("unexpected error: %v", err) 602 } 603 defer containerTar2.Close() 604 err = extractTar(containerTar2, tmpdir) 605 606 expectedFiles := []*fileInfo{ 607 {path: "hello.txt", typeflag: tar.TypeReg, size: 8, contents: "newhello"}, 608 {path: "linktofile", typeflag: tar.TypeReg, size: 20}, 609 {path: "linktodir", typeflag: tar.TypeReg, size: 20}, 610 {path: "afolder", typeflag: tar.TypeReg, size: 8}, 611 {path: "dirsymlinked", typeflag: tar.TypeDir}, 612 {path: "afile", typeflag: tar.TypeDir}, 613 {path: "filesymlinked", typeflag: tar.TypeReg, size: 5}, 614 {path: "folder01", typeflag: tar.TypeDir}, 615 {path: "folder01/file01", typeflag: tar.TypeReg, size: 5}, 616 {path: "folder01/file02", typeflag: tar.TypeReg, size: 5}, 617 } 618 619 err = checkExpectedFiles(tmpdir, fileInfoSliceToMap(expectedFiles)) 620 if err != nil { 621 t.Errorf("unexpected error: %v", err) 622 } 623 } 624 625 func TestExtractTarTimes(t *testing.T) { 626 if !sys.HasChrootCapability() { 627 t.Skipf("chroot capability not available. Disabling test.") 628 } 629 testExtractTarTimes(t, extractTarHelper) 630 } 631 func TestExtractTarTimesInsecure(t *testing.T) { 632 testExtractTarTimes(t, extractTarInsecureHelper) 633 } 634 func testExtractTarTimes(t *testing.T, extractTar func(io.Reader, string) error) { 635 // Do not set ns as tar has second precision 636 time1 := time.Unix(100000, 0) 637 time2 := time.Unix(200000, 0) 638 time3 := time.Unix(300000, 0) 639 entries := []*testTarEntry{ 640 { 641 header: &tar.Header{ 642 Name: "folder/", 643 Typeflag: tar.TypeDir, 644 Mode: int64(0747), 645 ModTime: time1, 646 }, 647 }, 648 { 649 contents: "foo", 650 header: &tar.Header{ 651 Name: "folder/foo.txt", 652 Size: 3, 653 ModTime: time2, 654 }, 655 }, 656 { 657 header: &tar.Header{ 658 Name: "folder/symlink.txt", 659 Typeflag: tar.TypeSymlink, 660 Linkname: "folder/foo.txt", 661 ModTime: time3, 662 }, 663 }, 664 } 665 666 testTarPath, err := newTestTar(entries) 667 if err != nil { 668 t.Errorf("unexpected error: %v", err) 669 } 670 defer os.Remove(testTarPath) 671 containerTar, err := os.Open(testTarPath) 672 if err != nil { 673 t.Errorf("unexpected error: %v", err) 674 } 675 defer containerTar.Close() 676 tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") 677 if err != nil { 678 t.Errorf("unexpected error: %v", err) 679 } 680 os.RemoveAll(tmpdir) 681 err = os.MkdirAll(tmpdir, 0755) 682 if err != nil { 683 t.Errorf("unexpected error: %v", err) 684 } 685 err = extractTar(containerTar, tmpdir) 686 if err != nil { 687 t.Errorf("unexpected error: %v", err) 688 } 689 err = checkTime(filepath.Join(tmpdir, "folder/"), time1) 690 if err != nil { 691 t.Errorf("unexpected error: %v", err) 692 } 693 err = checkTime(filepath.Join(tmpdir, "folder/foo.txt"), time2) 694 if err != nil { 695 t.Errorf("unexpected error: %v", err) 696 } 697 698 //Check only (by now) on linux 699 if runtime.GOOS == "linux" { 700 err = checkTime(filepath.Join(tmpdir, "folder/symlink.txt"), time3) 701 if err != nil { 702 t.Errorf("unexpected error: %v", err) 703 } 704 } 705 } 706 707 func checkTime(path string, time time.Time) error { 708 info, err := os.Lstat(path) 709 if err != nil { 710 return err 711 } 712 713 if info.ModTime() != time { 714 return fmt.Errorf("%s: info.ModTime: %s, different from expected time: %s", path, info.ModTime(), time) 715 } 716 return nil 717 } 718 719 func TestExtractTarHardLink(t *testing.T) { 720 if !sys.HasChrootCapability() { 721 t.Skipf("chroot capability not available. Disabling test.") 722 } 723 testExtractTarHardLink(t, extractTarHelper) 724 } 725 func TestExtractTarHardLinkInsecure(t *testing.T) { 726 testExtractTarHardLink(t, extractTarInsecureHelper) 727 } 728 func testExtractTarHardLink(t *testing.T, extractTar func(io.Reader, string) error) { 729 entries := []*testTarEntry{ 730 { 731 contents: "foo", 732 header: &tar.Header{ 733 Name: "/foo.txt", 734 Size: 3, 735 }, 736 }, 737 { 738 header: &tar.Header{ 739 Name: "foolink", 740 Typeflag: tar.TypeLink, 741 Linkname: "/foo.txt", 742 }, 743 }, 744 } 745 746 testTarPath, err := newTestTar(entries) 747 if err != nil { 748 t.Errorf("unexpected error: %v", err) 749 } 750 defer os.Remove(testTarPath) 751 containerTar, err := os.Open(testTarPath) 752 if err != nil { 753 t.Errorf("unexpected error: %v", err) 754 } 755 defer containerTar.Close() 756 tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") 757 if err != nil { 758 t.Errorf("unexpected error: %v", err) 759 } 760 os.RemoveAll(tmpdir) 761 err = os.MkdirAll(tmpdir, 0755) 762 if err != nil { 763 t.Errorf("unexpected error: %v", err) 764 } 765 defer os.RemoveAll(tmpdir) 766 err = extractTar(containerTar, tmpdir) 767 if err != nil { 768 t.Errorf("unexpected error: %v", err) 769 } 770 var origFile syscall.Stat_t 771 err = syscall.Stat(filepath.Join(tmpdir, "/foo.txt"), &origFile) 772 if err != nil { 773 t.Errorf("unexpected error: %v", err) 774 } 775 var linkedFile syscall.Stat_t 776 err = syscall.Stat(filepath.Join(tmpdir, "/foolink"), &linkedFile) 777 if err != nil { 778 t.Errorf("unexpected error: %v", err) 779 } 780 if origFile.Nlink != 2 { 781 t.Errorf("wrong number of nlinks on original file, expecting: 2, actual: %d", origFile.Nlink) 782 } 783 if linkedFile.Nlink != 2 { 784 t.Errorf("wrong number of nlinks on linked file, expecting: 2, actual: %d", origFile.Nlink) 785 } 786 if origFile.Ino != linkedFile.Ino { 787 t.Errorf("original file and linked file have different inodes") 788 } 789 } 790 791 func TestExtractTarXattr(t *testing.T) { 792 if !sys.HasChrootCapability() { 793 t.Skipf("chroot capability not available. Disabling test.") 794 } 795 796 entries := []*testTarEntry{ 797 { 798 contents: "foo", 799 header: &tar.Header{ 800 Name: "folder/foo.txt", 801 Size: 3, 802 Mode: int64(0755), 803 Xattrs: map[string]string{ 804 "security.capability": "foo", 805 "user.baz": "bar", 806 "faulty": "xattr", 807 }, 808 }, 809 }, 810 } 811 812 expected := []struct { 813 Xattrs map[string]string 814 }{ 815 { 816 Xattrs: map[string]string{ 817 "security.capability": "foo", 818 "user.baz": "bar", 819 }, 820 }, 821 } 822 823 testTarPath, err := newTestTar(entries) 824 if err != nil { 825 t.Errorf("unexpected error: %v", err) 826 } 827 defer os.Remove(testTarPath) 828 829 containerTar, err := os.Open(testTarPath) 830 if err != nil { 831 t.Errorf("unexpected error: %v", err) 832 } 833 defer containerTar.Close() 834 835 wd, err := os.Getwd() 836 if err != nil { 837 t.Errorf("unexpected error: %v", err) 838 } 839 840 tmpdir, err := ioutil.TempDir(wd, "rkt-temp-dir") 841 if err != nil { 842 t.Errorf("unexpected error: %v", err) 843 } 844 os.RemoveAll(tmpdir) 845 846 err = os.MkdirAll(tmpdir, 0755) 847 if err != nil { 848 t.Errorf("unexpected error: %v", err) 849 } 850 defer os.RemoveAll(tmpdir) 851 852 err = ExtractTar(containerTar, tmpdir, true, user.NewBlankUidRange(), nil) 853 if err != nil { 854 if strings.Contains(err.Error(), "operation not supported") { 855 t.Skipf("xattr unsupported, disabling test, error %v", err) 856 } 857 858 t.Errorf("unexpected error: %v", err) 859 } 860 861 for i, expected := range expected { 862 for k, v := range expected.Xattrs { 863 p := filepath.Join(tmpdir, entries[i].header.Name) 864 dest := make([]byte, len(v)) 865 866 _, err := syscall.Getxattr(p, k, dest) 867 if err != nil { 868 t.Errorf("unexpected error: %v", err) 869 } 870 871 if got := string(dest); got != v { 872 t.Errorf("Expected xattr %q=%q, got %q", k, v, got) 873 } 874 } 875 } 876 } 877 878 func extractTarOverwriteHelper(rdr io.Reader, target string) error { 879 return ExtractTar(rdr, target, true, user.NewBlankUidRange(), nil) 880 } 881 882 func extractTarHelper(rdr io.Reader, target string) error { 883 return extractTarHelperPWL(rdr, target, nil) 884 } 885 886 func extractTarHelperPWL(rdr io.Reader, target string, pwl PathWhitelistMap) error { 887 return ExtractTar(rdr, target, false, user.NewBlankUidRange(), pwl) 888 } 889 890 func extractTarInsecureHelper(rdr io.Reader, target string) error { 891 return extractTarInsecureHelperPWL(rdr, target, nil) 892 } 893 894 func extractTarInsecureHelperPWL(rdr io.Reader, target string, pwl PathWhitelistMap) error { 895 editor, err := NewUidShiftingFilePermEditor(user.NewBlankUidRange()) 896 if err != nil { 897 return err 898 } 899 return ExtractTarInsecure(tar.NewReader(rdr), target, true, pwl, editor) 900 }