// CopyToDir will copy all elements specified in the froms slice into the // directory inside the current ACI specified by the to string. func (a *ACBuild) CopyToDir(froms []string, to string) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() target := path.Join(a.CurrentACIPath, aci.RootfsDir, to) targetInfo, err := os.Stat(target) switch { case os.IsNotExist(err): err := os.MkdirAll(target, 0755) if err != nil { return err } case err != nil: return err case !targetInfo.IsDir(): return fmt.Errorf("target %q is not a directory", to) } for _, from := range froms { _, file := path.Split(from) tmptarget := path.Join(target, file) err := fileutil.CopyTree(from, tmptarget, uid.NewBlankUidRange()) if err != nil { return err } } return nil }
// ExtractImage will extract the contents of the image at path to the directory // at dst. If fileMap is set, only files in it will be extracted. func ExtractImage(path, dst string, fileMap map[string]struct{}) error { dst, err := filepath.Abs(dst) if err != nil { return err } file, err := os.Open(path) if err != nil { return err } defer file.Close() dr, err := aci.NewCompressedReader(file) if err != nil { return fmt.Errorf("error decompressing image: %v", err) } defer dr.Close() uidRange := uid.NewBlankUidRange() if os.Geteuid() == 0 { return rkttar.ExtractTar(dr, dst, true, uidRange, fileMap) } editor, err := rkttar.NewUidShiftingFilePermEditor(uidRange) if err != nil { return fmt.Errorf("error determining current user: %v", err) } return rkttar.ExtractTarInsecure(tar.NewReader(dr), dst, true, fileMap, editor) }
func (a *ACBuild) beginFromLocalDirectory(start string) error { err := os.MkdirAll(a.CurrentACIPath, 0755) if err != nil { return err } err = fileutil.CopyTree(start, path.Join(a.CurrentACIPath, aci.RootfsDir), uid.NewBlankUidRange()) if err != nil { return err } return a.writeEmptyManifest() }
// Copy will copy the directory/file at from to the path to inside the untarred // ACI at acipath. func Copy(acipath, from, to string) error { target := path.Join(acipath, aci.RootfsDir, to) dir, _ := path.Split(target) if dir != "" { err := os.MkdirAll(dir, 0755) if err != nil { return err } } return fileutil.CopyTree(from, target, uid.NewBlankUidRange()) }
// UnTar will extract the contents at the tar file at tarpath to the directory // at dst. If fileMap is set, only files in it will be extracted. func UnTar(tarpath, dst string, fileMap map[string]struct{}) error { dst, err := filepath.Abs(dst) if err != nil { return err } tarfile, err := os.Open(tarpath) if err != nil { return err } defer tarfile.Close() return tar.ExtractTar(tarfile, dst, true, uid.NewBlankUidRange(), fileMap) }
// ExtractImage will extract the contents of the image at path to the directory // at dst. If fileMap is set, only files in it will be extracted. func ExtractImage(path, dst string, fileMap map[string]struct{}) error { dst, err := filepath.Abs(dst) if err != nil { return err } file, err := os.Open(path) if err != nil { return err } defer file.Close() dr, err := aci.NewCompressedReader(file) if err != nil { return fmt.Errorf("error decompressing image: %v", err) } defer dr.Close() return tar.ExtractTar(dr, dst, true, uid.NewBlankUidRange(), fileMap) }
// Copy will copy the directory/file at from to the path to inside the untarred // ACI at a.CurrentACIPath. func (a *ACBuild) Copy(from, to string) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() target := path.Join(a.CurrentACIPath, aci.RootfsDir, to) dir, _ := path.Split(target) if dir != "" { err := os.MkdirAll(dir, 0755) if err != nil { return err } } return fileutil.CopyTree(from, target, uid.NewBlankUidRange()) }
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()) } }