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 uidShift argument: %v", err) } uidRange := &uid.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 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, uid.NewBlankUidRange(), 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, uid.NewBlankUidRange(), 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, uid.NewBlankUidRange(), 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, uid.NewBlankUidRange(), 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, uid.NewBlankUidRange(), 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()) } }