func TestTreeStoreWrite(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := NewStore(dir) if err != nil { t.Fatalf("unexpected error: %v", err) } key, err := treeStoreWriteACI(dir, s) if err != nil { t.Fatalf("unexpected error: %v", err) } id := "treestoreid01" // Ask the store to render the treestore _, err = s.treestore.Write(id, key, s) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be the same. _, err = s.treestore.Check(id) if err != nil { t.Fatalf("unexpected error: %v", err) } }
// Because this function is executed by multicall in a different process, it is not possible to use errwrap to return errors func extractTarCommand() error { if len(os.Args) != 5 { return fmt.Errorf("incorrect number of arguments. Usage: %s DIR {true|false} uidShift uidCount", multicallName) } if !sys.HasChrootCapability() { return fmt.Errorf("chroot capability not available.") } dir := os.Args[1] if !filepath.IsAbs(dir) { return fmt.Errorf("dir %s must be an absolute path", dir) } overwrite, err := strconv.ParseBool(os.Args[2]) if err != nil { return fmt.Errorf("error parsing overwrite argument: %v", err) } us, err := strconv.ParseUint(os.Args[3], 10, 32) if err != nil { return fmt.Errorf("error parsing uidShift argument: %v", err) } uc, err := strconv.ParseUint(os.Args[4], 10, 32) if err != nil { return fmt.Errorf("error parsing uidCount argument: %v", err) } uidRange := &user.UidRange{Shift: uint32(us), Count: uint32(uc)} if err := syscall.Chroot(dir); err != nil { return fmt.Errorf("failed to chroot in %s: %v", dir, err) } if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("failed to chdir: %v", err) } fileMapFile := os.NewFile(uintptr(fileMapFdNum), "fileMap") fileMap := map[string]struct{}{} if err := json.NewDecoder(fileMapFile).Decode(&fileMap); err != nil { return fmt.Errorf("error decoding fileMap: %v", err) } editor, err := NewUidShiftingFilePermEditor(uidRange) if err != nil { return fmt.Errorf("error determining current user: %v", err) } if err := ExtractTarInsecure(tar.NewReader(os.Stdin), "/", overwrite, fileMap, editor); err != nil { return fmt.Errorf("error extracting tar: %v", err) } // flush remaining bytes io.Copy(ioutil.Discard, os.Stdin) return nil }
func TestTreeStoreRemove(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := imagestore.NewStore(dir) if err != nil { t.Fatalf("unexpected error: %v", err) } ts, err := NewStore(dir, s) if err != nil { t.Fatalf("unexpected error: %v", err) } key, err := testStoreWriteACI(dir, s) if err != nil { t.Fatalf("unexpected error: %v", err) } id := "treestoreid01" // Test non existent dir err = ts.remove(id) if err != nil { t.Fatalf("unexpected error: %v", err) } // Test rendered tree _, err = ts.render(id, key) if err != nil { t.Fatalf("unexpected error: %v", err) } err = ts.remove(id) if err != nil { t.Fatalf("unexpected error: %v", err) } }
func extractTarCommand() error { if len(os.Args) != 3 { return fmt.Errorf("incorrect number of arguments. Usage: %s DIR {true|false}", multicallName) } if !sys.HasChrootCapability() { return fmt.Errorf("chroot capability not available.") } dir := os.Args[1] if !filepath.IsAbs(dir) { return fmt.Errorf("dir %s must be an absolute path", dir) } overwrite, err := strconv.ParseBool(os.Args[2]) if err != nil { return fmt.Errorf("error parsing overwrite argument: %v", err) } if err := syscall.Chroot(dir); err != nil { return fmt.Errorf("failed to chroot in %s: %v", dir, err) } if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("failed to chdir: %v", err) } fileMapFile := os.NewFile(uintptr(fileMapFdNum), "fileMap") fileMap := map[string]struct{}{} if err := json.NewDecoder(fileMapFile).Decode(&fileMap); err != nil { return fmt.Errorf("error decoding fileMap: %v", err) } if err := extractTar(tar.NewReader(os.Stdin), overwrite, fileMap); err != nil { return fmt.Errorf("error extracting tar: %v", err) } // flush remaining bytes io.Copy(ioutil.Discard, os.Stdin) return nil }
func TestTreeStore(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := NewStore(dir) if err != nil { t.Fatalf("unexpected error: %v", err) } imj := ` { "acKind": "ImageManifest", "acVersion": "0.7.1", "name": "example.com/test01" } ` entries := []*aci.ACIEntry{ // An empty dir { Header: &tar.Header{ Name: "rootfs/a", Typeflag: tar.TypeDir, }, }, { Contents: "hello", Header: &tar.Header{ Name: "hello.txt", Size: 5, }, }, { Header: &tar.Header{ Name: "rootfs/link.txt", Linkname: "rootfs/hello.txt", Typeflag: tar.TypeSymlink, }, }, // dangling symlink { Header: &tar.Header{ Name: "rootfs/link2.txt", Linkname: "rootfs/missingfile.txt", Typeflag: tar.TypeSymlink, }, }, { Header: &tar.Header{ Name: "rootfs/fifo", Typeflag: tar.TypeFifo, }, }, } aci, err := aci.NewACI(dir, imj, entries) if err != nil { t.Fatalf("error creating test tar: %v", err) } defer aci.Close() // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } // Import the new ACI key, err := s.WriteACI(aci, false) if err != nil { t.Fatalf("unexpected error: %v", err) } // Ask the store to render the treestore id, err := s.RenderTreeStore(key, false) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be the same. err = s.CheckTreeStore(id) if err != nil { t.Fatalf("unexpected error: %v", err) } // Change a file permission rootfs := s.GetTreeStoreRootFS(id) err = os.Chmod(filepath.Join(rootfs, "a"), 0600) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be different err = s.CheckTreeStore(id) if err == nil { t.Errorf("expected non-nil error!") } // rebuild the tree prevID := id id, err = s.RenderTreeStore(key, true) if err != nil { t.Fatalf("unexpected error: %v", err) } if id != prevID { t.Fatalf("unexpected different IDs. prevID: %s, id: %s", prevID, id) } // Add a file rootfs = s.GetTreeStoreRootFS(id) err = ioutil.WriteFile(filepath.Join(rootfs, "newfile"), []byte("newfile"), 0644) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be different err = s.CheckTreeStore(id) if err == nil { t.Errorf("expected non-nil error!") } }
func TestExtractTarTimes(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } // Do not set ns as tar has second precision time1 := time.Unix(100000, 0) time2 := time.Unix(200000, 0) time3 := time.Unix(300000, 0) entries := []*testTarEntry{ { header: &tar.Header{ Name: "folder/", Typeflag: tar.TypeDir, Mode: int64(0747), ModTime: time1, }, }, { contents: "foo", header: &tar.Header{ Name: "folder/foo.txt", Size: 3, ModTime: time2, }, }, { header: &tar.Header{ Name: "folder/symlink.txt", Typeflag: tar.TypeSymlink, Linkname: "folder/foo.txt", ModTime: time3, }, }, } testTarPath, err := newTestTar(entries) if err != nil { t.Errorf("unexpected error: %v", err) } defer os.Remove(testTarPath) containerTar, err := os.Open(testTarPath) if err != nil { t.Errorf("unexpected error: %v", err) } defer containerTar.Close() tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") if err != nil { t.Errorf("unexpected error: %v", err) } os.RemoveAll(tmpdir) err = os.MkdirAll(tmpdir, 0755) if err != nil { t.Errorf("unexpected error: %v", err) } err = ExtractTar(containerTar, tmpdir, false, nil) if err != nil { t.Errorf("unexpected error: %v", err) } err = checkTime(filepath.Join(tmpdir, "folder/"), time1) if err != nil { t.Errorf("unexpected error: %v", err) } err = checkTime(filepath.Join(tmpdir, "folder/foo.txt"), time2) if err != nil { t.Errorf("unexpected error: %v", err) } //Check only (by now) on linux if runtime.GOOS == "linux" { err = checkTime(filepath.Join(tmpdir, "folder/symlink.txt"), time3) if err != nil { t.Errorf("unexpected error: %v", err) } } }
func TestExtractTarOverwrite(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.RemoveAll(tmpdir) entries := []*testTarEntry{ { contents: "hello", header: &tar.Header{ Name: "hello.txt", Size: 5, }, }, { header: &tar.Header{ Name: "afolder", Typeflag: tar.TypeDir, }, }, { contents: "hello", header: &tar.Header{ Name: "afolder/hello.txt", Size: 5, }, }, { contents: "hello", header: &tar.Header{ Name: "afile", Size: 5, }, }, { header: &tar.Header{ Name: "folder01", Typeflag: tar.TypeDir, }, }, { contents: "hello", header: &tar.Header{ Name: "folder01/file01", Size: 5, }, }, { contents: "hello", header: &tar.Header{ Name: "filesymlinked", Size: 5, }, }, { header: &tar.Header{ Name: "linktofile", Linkname: "filesymlinked", Typeflag: tar.TypeSymlink, }, }, { header: &tar.Header{ Name: "dirsymlinked", Typeflag: tar.TypeDir, }, }, { header: &tar.Header{ Name: "linktodir", Linkname: "dirsymlinked", Typeflag: tar.TypeSymlink, }, }, } testTarPath, err := newTestTar(entries) if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(testTarPath) containerTar1, err := os.Open(testTarPath) if err != nil { t.Fatalf("unexpected error: %v", err) } defer containerTar1.Close() err = ExtractTar(containerTar1, tmpdir, false, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } // Now overwrite: // a file with a new file // a dir with a file entries = []*testTarEntry{ { contents: "newhello", header: &tar.Header{ Name: "hello.txt", Size: 8, }, }, // Now this is a file { contents: "nowafile", header: &tar.Header{ Name: "afolder", Typeflag: tar.TypeReg, Size: 8, }, }, // Now this is a dir { header: &tar.Header{ Name: "afile", Typeflag: tar.TypeDir, }, }, // Overwrite symlink to a file with a regular file // the linked file shouldn't be removed { contents: "filereplacingsymlink", header: &tar.Header{ Name: "linktofile", Typeflag: tar.TypeReg, Size: 20, }, }, // Overwrite symlink to a dir with a regular file // the linked directory and all its contents shouldn't be // removed { contents: "filereplacingsymlink", header: &tar.Header{ Name: "linktodir", Typeflag: tar.TypeReg, Size: 20, }, }, // folder01 already exists and shouldn't be removed (keeping folder01/file01) { header: &tar.Header{ Name: "folder01", Typeflag: tar.TypeDir, Mode: int64(0755), }, }, { contents: "hello", header: &tar.Header{ Name: "folder01/file02", Size: 5, Mode: int64(0644), }, }, } testTarPath, err = newTestTar(entries) if err != nil { t.Errorf("unexpected error: %v", err) } defer os.Remove(testTarPath) containerTar2, err := os.Open(testTarPath) if err != nil { t.Errorf("unexpected error: %v", err) } defer containerTar2.Close() err = ExtractTar(containerTar2, tmpdir, true, nil) expectedFiles := []*fileInfo{ &fileInfo{path: "hello.txt", typeflag: tar.TypeReg, size: 8, contents: "newhello"}, &fileInfo{path: "linktofile", typeflag: tar.TypeReg, size: 20}, &fileInfo{path: "linktodir", typeflag: tar.TypeReg, size: 20}, &fileInfo{path: "afolder", typeflag: tar.TypeReg, size: 8}, &fileInfo{path: "dirsymlinked", typeflag: tar.TypeDir}, &fileInfo{path: "afile", typeflag: tar.TypeDir}, &fileInfo{path: "filesymlinked", typeflag: tar.TypeReg, size: 5}, &fileInfo{path: "folder01", typeflag: tar.TypeDir}, &fileInfo{path: "folder01/file01", typeflag: tar.TypeReg, size: 5}, &fileInfo{path: "folder01/file02", typeflag: tar.TypeReg, size: 5}, } err = checkExpectedFiles(tmpdir, fileInfoSliceToMap(expectedFiles)) if err != nil { t.Errorf("unexpected error: %v", err) } }
func TestExtractTarPWL(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } entries := []*testTarEntry{ { header: &tar.Header{ Name: "folder/", Typeflag: tar.TypeDir, Mode: int64(0747), }, }, { contents: "foo", header: &tar.Header{ Name: "folder/foo.txt", Size: 3, }, }, { contents: "bar", header: &tar.Header{ Name: "folder/bar.txt", Size: 3, }, }, { header: &tar.Header{ Name: "folder/symlink.txt", Typeflag: tar.TypeSymlink, Linkname: "folder/foo.txt", }, }, } testTarPath, err := newTestTar(entries) if err != nil { t.Errorf("unexpected error: %v", err) } defer os.Remove(testTarPath) containerTar, err := os.Open(testTarPath) if err != nil { t.Errorf("unexpected error: %v", err) } defer containerTar.Close() tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") if err != nil { t.Errorf("unexpected error: %v", err) } defer os.RemoveAll(tmpdir) pwl := make(PathWhitelistMap) pwl["folder/foo.txt"] = struct{}{} err = ExtractTar(containerTar, tmpdir, false, pwl) if err != nil { t.Errorf("unexpected error: %v", err) } matches, err := filepath.Glob(filepath.Join(tmpdir, "folder/*.txt")) if err != nil { t.Errorf("unexpected error: %v", err) } if len(matches) != 1 { t.Errorf("unexpected number of files found: %d, wanted 1", len(matches)) } }
func TestExtractTarFolders(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } entries := []*testTarEntry{ { contents: "foo", header: &tar.Header{ Name: "deep/folder/foo.txt", Size: 3, }, }, { header: &tar.Header{ Name: "deep/folder/", Typeflag: tar.TypeDir, Mode: int64(0747), }, }, { contents: "bar", header: &tar.Header{ Name: "deep/folder/bar.txt", Size: 3, }, }, { header: &tar.Header{ Name: "deep/folder2/symlink.txt", Typeflag: tar.TypeSymlink, Linkname: "deep/folder/foo.txt", }, }, { header: &tar.Header{ Name: "deep/folder2/", Typeflag: tar.TypeDir, Mode: int64(0747), }, }, { contents: "bar", header: &tar.Header{ Name: "deep/folder2/bar.txt", Size: 3, }, }, { header: &tar.Header{ Name: "deep/deep/folder", Typeflag: tar.TypeDir, Mode: int64(0755), }, }, { header: &tar.Header{ Name: "deep/deep/", Typeflag: tar.TypeDir, Mode: int64(0747), }, }, } testTarPath, err := newTestTar(entries) if err != nil { t.Errorf("unexpected error: %v", err) } defer os.Remove(testTarPath) containerTar, err := os.Open(testTarPath) if err != nil { t.Errorf("unexpected error: %v", err) } defer containerTar.Close() tmpdir, err := ioutil.TempDir("", "rkt-temp-dir") if err != nil { t.Errorf("unexpected error: %v", err) } os.RemoveAll(tmpdir) err = os.MkdirAll(tmpdir, 0755) if err != nil { t.Errorf("unexpected error: %v", err) } defer os.RemoveAll(tmpdir) err = ExtractTar(containerTar, tmpdir, false, nil) if err != nil { t.Errorf("unexpected error: %v", err) } matches, err := filepath.Glob(filepath.Join(tmpdir, "deep/folder/*.txt")) if err != nil { t.Errorf("unexpected error: %v", err) } if len(matches) != 2 { t.Errorf("unexpected number of files found: %d, wanted 2", len(matches)) } matches, err = filepath.Glob(filepath.Join(tmpdir, "deep/folder2/*.txt")) if err != nil { t.Errorf("unexpected error: %v", err) } if len(matches) != 2 { t.Errorf("unexpected number of files found: %d, wanted 2", len(matches)) } dirInfo, err := os.Lstat(filepath.Join(tmpdir, "deep/folder")) if err != nil { t.Errorf("unexpected error: %v", err) } else if dirInfo.Mode().Perm() != os.FileMode(0747) { t.Errorf("unexpected dir mode: %s", dirInfo.Mode()) } dirInfo, err = os.Lstat(filepath.Join(tmpdir, "deep/deep")) if err != nil { t.Errorf("unexpected error: %v", err) } else if dirInfo.Mode().Perm() != os.FileMode(0747) { t.Errorf("unexpected dir mode: %s", dirInfo.Mode()) } }
func TestExtractTarHardLink(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } testExtractTarHardLink(t, extractTarHelper) }