func entar(w io.Writer, dir string, ww io.Writer) error { msg.Write(ww, msg.User, []byte("entar\n")) tw := tar.NewWriter(w) err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { if err != nil { return err } hdr := new(tar.Header) hdr.Name = "./app" + path[len(dir):] hdr.Mode = int64(fi.Mode() & os.ModePerm) if fi.IsDir() { hdr.Typeflag = tar.TypeDir } else { hdr.Typeflag = tar.TypeReg hdr.Size = fi.Size() } if err = tw.WriteHeader(hdr); err != nil { return err } if !fi.IsDir() { var f *os.File f, err = os.Open(path) if err != nil { return err } _, err = io.Copy(tw, f) } return err }) if err != nil { return err } return tw.Close() }
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) error { // convert whiteouts to AUFS format if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 { // we just rename the file and make it normal hdr.Name = WhiteoutPrefix + hdr.Name hdr.Mode = 0600 hdr.Typeflag = tar.TypeReg } if fi.Mode()&os.ModeDir != 0 { // convert opaque dirs to AUFS format by writing an empty file with the prefix opaque, err := system.Lgetxattr(path, "trusted.overlay.opaque") if err != nil { return err } if opaque != nil && len(opaque) == 1 && opaque[0] == 'y' { // create a header for the whiteout file // it should inherit some properties from the parent, but be a regular file *hdr = tar.Header{ Typeflag: tar.TypeReg, Mode: hdr.Mode & int64(os.ModePerm), Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), Size: 0, Uid: hdr.Uid, Uname: hdr.Uname, Gid: hdr.Gid, Gname: hdr.Gname, AccessTime: hdr.AccessTime, ChangeTime: hdr.ChangeTime, } } } return nil }
func sparseCompress() { err := os.Mkdir("sparse", 0777) tarfile, err := os.Create("sparse/sparse.tar") if err != nil { log.Fatalln(err) } defer tarfile.Close() var fileWriter io.WriteCloser = tarfile tfw := tar.NewWriter(fileWriter) defer tfw.Close() file, err := os.Open("sparse.img") defer file.Close() fileInfo, err := file.Stat() header := new(tar.Header) header.Name = file.Name() header.Size = fileInfo.Size() header.Mode = int64(fileInfo.Mode()) header.ModTime = fileInfo.ModTime() header.Typeflag = tar.TypeGNUSparse err = tfw.WriteHeader(header) if err != nil { log.Fatalln(err) } _, err = io.Copy(tfw, file) if err != nil { log.Fatalln(err) } }
func TestUntarCreatesDeeperPathsIfNotMentioned(t *testing.T) { StartTest(t) defer FinishTest(t) // create a buffer and tar.Writer buffer := bytes.NewBufferString("") archive := tar.NewWriter(buffer) writeFile := func(name, contents string) { b := []byte(contents) header := new(tar.Header) header.Name = name header.Typeflag = tar.TypeReg header.Mode = 0644 header.Mode |= c_ISREG header.ModTime = time.Now() header.Size = int64(len(b)) TestExpectSuccess(t, archive.WriteHeader(header)) _, err := archive.Write(b) TestExpectSuccess(t, err) TestExpectSuccess(t, archive.Flush()) } // generate the mock tar... this will write to a file in a directory that // isn't already created within the tar writeFile("./a_directory/file", "foo") archive.Close() // create temp folder to extract to tempDir := TempDir(t) extractionPath := path.Join(tempDir, "pkg") err := os.MkdirAll(extractionPath, 0755) TestExpectSuccess(t, err) // extract r := bytes.NewReader(buffer.Bytes()) u := NewUntar(r, extractionPath) u.AbsoluteRoot = tempDir TestExpectSuccess(t, u.Extract()) fileExists := func(name string) { _, err := os.Stat(path.Join(tempDir, name)) TestExpectSuccess(t, err) } fileContents := func(name, contents string) { b, err := ioutil.ReadFile(path.Join(tempDir, name)) TestExpectSuccess(t, err) TestEqual(t, string(b), contents) } fileExists("./pkg/a_directory/file") fileContents("./pkg/a_directory/file", "foo") }
func writeHosts(tw *tar.Writer) error { etcheader := tar.Header{} etcheader.Name = "rootfs/etc/" etcheader.Mode = 0644 etcheader.Typeflag = tar.TypeDir tw.WriteHeader(&etcheader) content := []byte("127.0.0.1 localhost\n::1 localhost\n") header := tar.Header{} header.Name = "rootfs/etc/hosts" header.Mode = 0644 header.Typeflag = tar.TypeReg header.Size = int64(len(content)) tw.WriteHeader(&header) if _, err := tw.Write(content); err != nil { return fmt.Errorf("cannot write manifest: %v", err) } return nil }
func AddFileToTar(tw *tar.Writer, filepath string) os.Error { d, e := os.Stat(filepath) if e != nil { return e } h := tar.Header{ Name: filepath, Mode: int64(d.Mode), Uid: d.Uid, Gid: d.Gid, Size: int64(d.Size), Atime: int64(d.Atime_ns / 1e9), Ctime: int64(d.Ctime_ns / 1e9), Mtime: int64(d.Mtime_ns / 1e9), } if d.IsDirectory() { h.Typeflag = tar.TypeDir } else if d.IsRegular() { h.Typeflag = tar.TypeReg } else { fmt.Fprintf(os.Stderr, "Skipped non-regular file: \"%s\"\n", filepath) return nil } tw.WriteHeader(&h) if !d.IsDirectory() { f, e := os.Open(filepath, os.O_RDONLY, 0) if e != nil { return e } _, e = io.Copy(tw, f) f.Close() if e != nil { return e } } return nil }
func addPath(writer *tar.Writer, rootPath string, prefixLen int, dereferenceEtc bool) error { walkFn := func(source string, info os.FileInfo, err error) error { if err != nil { return fmt.Errorf("cannot read directory '%s': %v", rootPath, err) } header := tar.Header{} relPath := source[(prefixLen):] header.Name = path.Join("rootfs", relPath) header.ModTime = info.ModTime() header.Mode = int64(info.Mode().Perm()) if info.Mode().IsRegular() || (dereferenceEtc && info.Mode()&os.ModeSymlink != 0 && strings.HasPrefix(relPath, "etc/")) { header.Typeflag = tar.TypeReg header.Size = info.Size() if err := writer.WriteHeader(&header); err != nil { return fmt.Errorf("cannot write aci image entry header '%s': %v", source, err) } return addFile(writer, source) } else { if info.Mode()&os.ModeSymlink != 0 { target, err := os.Readlink(source) if err != nil { return fmt.Errorf("cannot read resolve symlink '%s', %v", target, err) } header.Linkname = target header.Typeflag = tar.TypeSymlink } else if info.Mode().IsDir() { header.Typeflag = tar.TypeDir } else { return fmt.Errorf("not implemented") } if err := writer.WriteHeader(&header); err != nil { return fmt.Errorf("cannot write aci image entry '%s': %v", source, err) } } return nil } return filepath.Walk(rootPath, walkFn) }
// Deal with files func tarGzFile(srcFile string, recPath string, tw *tar.Writer, fi os.FileInfo) { if fi.IsDir() { // fmt.Println("??") // Create tar header hdr := new(tar.Header) // if last character of header name is '/' it also can be directory // but if you don't set Typeflag, error will occur when you untargz hdr.Name = recPath // + "/" // fmt.Println(hdr.Name) hdr.Typeflag = tar.TypeDir // hdr.Size = 0 //hdr.Mode = 0755 | c_ISDIR // hdr.Mode = int64(fi.Mode()) // 加这个会有错误!!! // hdr.ModTime = fi.ModTime() // 加这个会有错误!! // Write hander err := tw.WriteHeader(hdr) if err != nil { panic(err) } } else { // File reader fr, err := os.Open(srcFile) if err != nil { panic(err) } defer fr.Close() // Create tar header hdr := new(tar.Header) hdr.Name = recPath // fmt.Println(hdr.Name) hdr.Size = fi.Size() hdr.Mode = int64(fi.Mode()) hdr.ModTime = fi.ModTime() // Write hander err = tw.WriteHeader(hdr) if err != nil { panic(err) } // Write file data _, err = io.Copy(tw, fr) if err != nil { panic(err) } } }
// Deal with files func tarGzFile(srcFile string, recPath string, tw *tar.Writer, fi os.FileInfo) error { //转换字符集 recPath = Utf8ToGBK(recPath) if fi.IsDir() { // Create tar header hdr := new(tar.Header) // if last character of header name is '/' it also can be directory // but if you don't set Typeflag, error will occur when you untargz hdr.Name = recPath + "/" hdr.Typeflag = tar.TypeDir hdr.Size = 0 //hdr.Mode = 0755 | c_ISDIR hdr.Mode = int64(fi.Mode()) hdr.ModTime = fi.ModTime() // Write hander err := tw.WriteHeader(hdr) return err } else { // File reader fr, err := os.Open(srcFile) if err != nil { return err } defer fr.Close() // Create tar header hdr := new(tar.Header) hdr.Name = recPath hdr.Size = fi.Size() hdr.Mode = int64(fi.Mode()) hdr.ModTime = fi.ModTime() // Write hander err = tw.WriteHeader(hdr) if err != nil { return err } // Write file data _, err = io.Copy(tw, fr) return err } }
func populateHeaderUnix(h *tar.Header, fi os.FileInfo, seen map[uint64]string) { st, ok := fi.Sys().(*syscall.Stat_t) if !ok { return } h.Uid = int(st.Uid) h.Gid = int(st.Gid) // If we have already seen this inode, generate a hardlink p, ok := seen[uint64(st.Ino)] if ok { h.Linkname = p h.Typeflag = tar.TypeLink } else { seen[uint64(st.Ino)] = h.Name } }
func writeManifest(tw *tar.Writer, manifest map[string]interface{}) error { buf, err := json.Marshal(manifest) if err != nil { return fmt.Errorf("cannot serialize manifest: %v", err) } header := tar.Header{} header.Name = "manifest" header.Mode = 0644 header.Size = int64(len(buf)) header.Typeflag = tar.TypeReg tw.WriteHeader(&header) if _, err := tw.Write(buf); err != nil { return fmt.Errorf("cannot write manifest: %v", err) } return nil }
// appends as link pointing at a previously written item func AppendLink(tarfn string, info Info, src string, dst string) (err error) { //var (twp *tar.Writer; fh ReadWriteSeekCloser; pos uint64) if tw, fh, _, err := OpenForAppend(tarfn); err == nil { defer fh.Close() defer tw.Close() if err := writeInfo(tw, info); err == nil { hdr := new(tar.Header) hdr.Name = src hdr.Typeflag = tar.TypeSymlink hdr.Linkname = dst if err := tw.WriteHeader(hdr); err == nil { err = tw.Flush() } } } return }
func TarGzWrite(_path string, tw *tar.Writer, fi os.FileInfo) { fr, err := os.Open(_path) handleError(err) defer fr.Close() h := new(tar.Header) h.Name = _path[len(ChallengesPath+"/"):] h.Size = fi.Size() h.Mode = int64(fi.Mode()) h.ModTime = fi.ModTime() h.Typeflag = tar.TypeReg err = tw.WriteHeader(h) handleError(err) _, err = io.Copy(tw, fr) handleError(err) }
// WriteToTarStream writes a byte array of data into a tar stream func WriteToTarStream(stream *tar.Writer, data []byte, header tar.Header) error { if header.Mode == 0 { header.Mode = 0644 } if header.Size == 0 { header.Size = int64(len(data)) } if header.Typeflag == 0 { header.Typeflag = tar.TypeReg } if err := stream.WriteHeader(&header); err != nil { return err } if _, err := stream.Write(data); err != nil { return err } return nil }
// Deal with files func tarGzFile(srcFile string, recPath string, tw *tar.Writer, fi os.FileInfo) { if fi.IsDir() { // Create tar header hdr := new(tar.Header) hdr.Name = recPath + "/" hdr.Typeflag = tar.TypeDir hdr.Size = 0 //hdr.Mode = 0755 | c_ISDIR hdr.Mode = int64(fi.Mode()) hdr.ModTime = fi.ModTime() // Write hander err := tw.WriteHeader(hdr) if err != nil { panic(err) } } else { // File reader fr, err := os.Open(srcFile) if err != nil { panic(err) } defer fr.Close() // Create tar header hdr := new(tar.Header) hdr.Name = recPath hdr.Size = fi.Size() hdr.Mode = int64(fi.Mode()) hdr.ModTime = fi.ModTime() // Write hander err = tw.WriteHeader(hdr) if err != nil { panic(err) } // Write file data _, err = io.Copy(tw, fr) if err != nil { panic(err) } } }
func populateHeaderUnix(h *tar.Header, fi os.FileInfo, seen map[uint64]string) { st, ok := fi.Sys().(*syscall.Stat_t) if !ok { return } h.Uid = int(st.Uid) h.Gid = int(st.Gid) if st.Mode&syscall.S_IFMT == syscall.S_IFBLK || st.Mode&syscall.S_IFMT == syscall.S_IFCHR { h.Devminor = int64(C.my_minor(C.dev_t(st.Rdev))) h.Devmajor = int64(C.my_major(C.dev_t(st.Rdev))) } // If we have already seen this inode, generate a hardlink p, ok := seen[uint64(st.Ino)] if ok { h.Linkname = p h.Typeflag = tar.TypeLink } else { seen[uint64(st.Ino)] = h.Name } }
func IterDirectory(dirPath string, tw *tar.Writer) { dir, err := os.Open(dirPath) handleError(err) defer dir.Close() dirStat, err := os.Stat(dirPath) handleError(err) fis, err := dir.Readdir(0) handleError(err) h := new(tar.Header) h.Name = dirPath[len(ChallengesPath+"/"):] h.Mode = 0600 h.ModTime = dirStat.ModTime() h.Typeflag = tar.TypeDir err = tw.WriteHeader(h) handleError(err) for _, fi := range fis { curPath := dirPath + "/" + fi.Name() if fi.IsDir() { IterDirectory(curPath, tw) } else { TarGzWrite(curPath, tw, fi) } } }
func TestUntarWhitelist(t *testing.T) { StartTest(t) defer FinishTest(t) // create a buffer and tar.Writer buffer := bytes.NewBufferString("") archive := tar.NewWriter(buffer) writeDirectory := func(name string) { header := new(tar.Header) header.Name = name + "/" header.Typeflag = tar.TypeDir header.Mode = 0755 header.Mode |= c_ISDIR header.ModTime = time.Now() TestExpectSuccess(t, archive.WriteHeader(header)) } writeFile := func(name, contents string) { b := []byte(contents) header := new(tar.Header) header.Name = name header.Typeflag = tar.TypeReg header.Mode = 0644 header.Mode |= c_ISREG header.ModTime = time.Now() header.Size = int64(len(b)) TestExpectSuccess(t, archive.WriteHeader(header)) _, err := archive.Write(b) TestExpectSuccess(t, err) TestExpectSuccess(t, archive.Flush()) } writeDirectory(".") writeFile("./foo", "foo") writeFile("./foobar", "foobar") writeFile("./doesntexist", "foo") writeDirectory("./usr") writeDirectory("./usr/bin") writeFile("./usr/bin/bash", "bash") writeDirectory("./usr/bin/other") writeFile("./usr/bin/other/sh", "sh") writeDirectory("./usr/nope") writeFile("./usr/nope/not", "notthere") writeDirectory("./usr/justdir") writeFile("./usr/justdir/not", "notthere") writeDirectory("./etc") writeFile("./etc/not", "notthere") archive.Close() // create temp folder to extract to tempDir := TempDir(t) // extract r := bytes.NewReader(buffer.Bytes()) u := NewUntar(r, tempDir) u.PathWhitelist = []string{ "/foo", "/usr/bin/", "/usr/justdir", } TestExpectSuccess(t, u.Extract()) fileExists := func(name string) { _, err := os.Stat(path.Join(tempDir, name)) TestExpectSuccess(t, err) } fileNotExists := func(name string) { _, err := os.Stat(path.Join(tempDir, name)) TestExpectError(t, err) } fileExists("/foo") fileExists("/usr/bin/bash") fileExists("/usr/bin/other/sh") fileExists("/usr/justdir") fileNotExists("/foobar") fileNotExists("/doesntexist") fileNotExists("/usr/nope/not") fileNotExists("/usr/justdir/not") fileNotExists("/etc/not") }
func createArchive() int { archive := tar.NewWriter(output) defer archive.Close() exit_value := 0 for _, f := range fileList { err := filepath.Walk(f, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if verbose { fmt.Fprintf(os.Stderr, "%s\n", path) } var hdr tar.Header hdr.Name = path hdr.Size = info.Size() hdr.Mode = int64(info.Mode()) hdr.ModTime = info.ModTime() // this is not portable: hdr.Uid = int(info.Sys().(*syscall.Stat_t).Uid) hdr.Gid = int(info.Sys().(*syscall.Stat_t).Gid) if user, err := user.LookupId(fmt.Sprintf("%d", hdr.Uid)); err == nil { hdr.Uname = user.Name } // TODO: lookup group, too. switch info.Mode() & os.ModeType { case 0: hdr.Typeflag = tar.TypeReg case os.ModeDir: hdr.Typeflag = tar.TypeDir case os.ModeSymlink: hdr.Typeflag = tar.TypeSymlink linkname, err := os.Readlink(path) if err != nil { fmt.Fprintf(os.Stderr, "Warning: can't readlink a symlink: %v\n", err) return nil } else { hdr.Linkname = linkname } case os.ModeNamedPipe: hdr.Typeflag = tar.TypeFifo case os.ModeSocket: fmt.Fprintf(os.Stderr, "Warning: can't tar a socket\n") return nil case os.ModeDevice: fmt.Fprintf(os.Stderr, "Warning: device files are currently unsupported\n") return nil /* if (info.Mode() & os.ModeCharDevice) != 0 { os.Typeflag = tar.TypeChar } else { os.Typeflag = tar.TypeBlock } */ } if err := archive.WriteHeader(&hdr); err != nil { fmt.Fprintf(os.Stderr, "Writing archive header for %s failed: %v\n", path, err) exit_value = 1 return nil } defer archive.Flush() if hdr.Typeflag == tar.TypeReg { if f, err := os.Open(path); err != nil { fmt.Fprintf(os.Stderr, "Opening file %s failed: %v\n", path, err) exit_value = 1 return nil } else { io.Copy(archive, f) f.Close() } } return nil }) if err != nil { fmt.Fprintf(os.Stderr, "An error occured: %v\n", err) exit_value = 1 } } return exit_value }
func TestUntarExtractOverwriting(t *testing.T) { StartTest(t) defer FinishTest(t) // create a buffer and tar.Writer buffer := bytes.NewBufferString("") archive := tar.NewWriter(buffer) writeDirectory := func(name string) { header := new(tar.Header) header.Name = name + "/" header.Typeflag = tar.TypeDir header.Mode = 0755 header.Mode |= c_ISDIR header.ModTime = time.Now() TestExpectSuccess(t, archive.WriteHeader(header)) } writeFile := func(name, contents string) { b := []byte(contents) header := new(tar.Header) header.Name = name header.Typeflag = tar.TypeReg header.Mode = 0644 header.Mode |= c_ISREG header.ModTime = time.Now() header.Size = int64(len(b)) TestExpectSuccess(t, archive.WriteHeader(header)) _, err := archive.Write(b) TestExpectSuccess(t, err) TestExpectSuccess(t, archive.Flush()) } writeSymlink := func(name, link string) { header := new(tar.Header) header.Name = name header.Linkname = link header.Typeflag = tar.TypeSymlink header.Mode = 0644 header.Mode |= c_ISLNK header.ModTime = time.Now() TestExpectSuccess(t, archive.WriteHeader(header)) } // create temp folder to extract to tempDir := TempDir(t) fileExists := func(name string) { _, err := os.Stat(path.Join(tempDir, name)) TestExpectSuccess(t, err) } fileContents := func(name, contents string) { b, err := ioutil.ReadFile(path.Join(tempDir, name)) TestExpectSuccess(t, err) TestEqual(t, string(b), contents) } fileSymlinks := func(name, link string) { l, err := os.Readlink(path.Join(tempDir, name)) TestExpectSuccess(t, err) TestEqual(t, l, link) } // generate the mock tar writeDirectory(".") writeFile("./foo", "foo") writeDirectory("./usr") writeDirectory("./usr/bin") writeFile("./usr/bin/bash", "bash") writeSymlink("./usr/bin/sh", "bash") writeDirectory("./etc") writeFile("./etc/awesome", "awesome") writeFile("./var", "vvv") archive.Close() // extract r := bytes.NewReader(buffer.Bytes()) u := NewUntar(r, tempDir) TestExpectSuccess(t, u.Extract()) // validate the first tar fileExists("./foo") fileContents("./foo", "foo") fileExists("./usr") fileExists("./usr/bin") fileExists("./usr/bin/bash") fileContents("./usr/bin/bash", "bash") fileSymlinks("./usr/bin/sh", "bash") fileExists("./etc/awesome") fileContents("./etc/awesome", "awesome") fileExists("./var") fileContents("./var", "vvv") // create another tar and then extract it buffer2 := bytes.NewBufferString("") archive = tar.NewWriter(buffer2) // write the 2nd tar writeDirectory(".") writeFile("./foo", "bar") writeDirectory("./usr") writeDirectory("./usr/bin") writeFile("./usr/bin/zsh", "zsh") writeSymlink("./usr/bin/sh", "zsh") writeFile("./etc", "etc") // replace the directory with a file writeDirectory("./var") // replace the file with a directory writeFile("./var/lib", "lll") archive.Close() // extract the 2nd tar r = bytes.NewReader(buffer2.Bytes()) u = NewUntar(r, tempDir) TestExpectSuccess(t, u.Extract()) // verify the contents were overwritten as expected fileContents("./foo", "bar") fileContents("./usr/bin/zsh", "zsh") fileSymlinks("./usr/bin/sh", "zsh") fileContents("./etc", "etc") fileContents("./var/lib", "lll") }
func TestUntarIDMappings(t *testing.T) { StartTest(t) defer FinishTest(t) // create a buffer and tar.Writer buffer := bytes.NewBufferString("") archive := tar.NewWriter(buffer) writeDirectoryWithOwners := func(name string, uid, gid int) { header := new(tar.Header) header.Name = name + "/" header.Typeflag = tar.TypeDir header.Mode = 0755 header.Mode |= c_ISDIR header.ModTime = time.Now() header.Uid = uid header.Gid = gid TestExpectSuccess(t, archive.WriteHeader(header)) } writeFileWithOwners := func(name, contents string, uid, gid int) { b := []byte(contents) header := new(tar.Header) header.Name = name header.Typeflag = tar.TypeReg header.Mode = 0644 header.Mode |= c_ISREG header.ModTime = time.Now() header.Size = int64(len(b)) header.Uid = uid header.Gid = gid TestExpectSuccess(t, archive.WriteHeader(header)) _, err := archive.Write(b) TestExpectSuccess(t, err) TestExpectSuccess(t, archive.Flush()) } writeDirectoryWithOwners(".", 0, 0) writeFileWithOwners("./foo", "foo", 0, 0) archive.Close() // setup our mapping func usr, err := user.Current() TestExpectSuccess(t, err) myUid, err := strconv.Atoi(usr.Uid) TestExpectSuccess(t, err) myGid, err := strconv.Atoi(usr.Gid) TestExpectSuccess(t, err) uidFuncCalled := false gidFuncCalled := false uidMappingFunc := func(uid int) (int, error) { uidFuncCalled = true TestEqual(t, uid, 0) return myUid, nil } gidMappingFunc := func(gid int) (int, error) { gidFuncCalled = true TestEqual(t, gid, 0) return myGid, nil } // extract tempDir := TempDir(t) r := bytes.NewReader(buffer.Bytes()) u := NewUntar(r, tempDir) u.PreserveOwners = true u.OwnerMappingFunc = uidMappingFunc u.GroupMappingFunc = gidMappingFunc TestExpectSuccess(t, u.Extract()) // verify it was called TestEqual(t, uidFuncCalled, true) TestEqual(t, gidFuncCalled, true) // verify the file stat, err := os.Stat(path.Join(tempDir, "foo")) TestExpectSuccess(t, err) sys := stat.Sys().(*syscall.Stat_t) TestEqual(t, sys.Uid, uint32(myUid)) TestEqual(t, sys.Gid, uint32(myGid)) }
func TestUntarExtractFollowingSymlinks(t *testing.T) { StartTest(t) defer FinishTest(t) // create a buffer and tar.Writer buffer := bytes.NewBufferString("") archive := tar.NewWriter(buffer) writeDirectory := func(name string) { header := new(tar.Header) header.Name = name + "/" header.Typeflag = tar.TypeDir header.Mode = 0755 header.Mode |= c_ISDIR header.ModTime = time.Now() TestExpectSuccess(t, archive.WriteHeader(header)) } writeFile := func(name, contents string) { b := []byte(contents) header := new(tar.Header) header.Name = name header.Typeflag = tar.TypeReg header.Mode = 0644 header.Mode |= c_ISREG header.ModTime = time.Now() header.Size = int64(len(b)) TestExpectSuccess(t, archive.WriteHeader(header)) _, err := archive.Write(b) TestExpectSuccess(t, err) TestExpectSuccess(t, archive.Flush()) } writeSymlink := func(name, link string) { header := new(tar.Header) header.Name = name header.Linkname = link header.Typeflag = tar.TypeSymlink header.Mode = 0644 header.Mode |= c_ISLNK header.ModTime = time.Now() TestExpectSuccess(t, archive.WriteHeader(header)) } // generate the mock tar writeDirectory(".") writeFile("./foo", "foo") writeDirectory("./usr") writeDirectory("./usr/bin") writeFile("./usr/bin/bash", "bash") writeSymlink("./usr/bin/sh", "bash") // now write a symlink that is an absolute path and then a file in it writeSymlink("./etc", "/realetc") writeFile("./etc/zz", "zz") archive.Close() // create temp folder to extract to tempDir := TempDir(t) extractionPath := path.Join(tempDir, "pkg") err := os.MkdirAll(extractionPath, 0755) TestExpectSuccess(t, err) err = os.MkdirAll(path.Join(tempDir, "realetc"), 0755) TestExpectSuccess(t, err) // extract r := bytes.NewReader(buffer.Bytes()) u := NewUntar(r, extractionPath) u.AbsoluteRoot = tempDir TestExpectSuccess(t, u.Extract()) fileExists := func(name string) { _, err := os.Stat(path.Join(tempDir, name)) TestExpectSuccess(t, err) } fileContents := func(name, contents string) { b, err := ioutil.ReadFile(path.Join(tempDir, name)) TestExpectSuccess(t, err) TestEqual(t, string(b), contents) } fileSymlinks := func(name, link string) { l, err := os.Readlink(path.Join(tempDir, name)) TestExpectSuccess(t, err) TestEqual(t, l, link) } fileExists("./pkg/foo") fileContents("./pkg/foo", "foo") fileExists("./pkg/usr") fileExists("./pkg/usr/bin") fileExists("./pkg/usr/bin/bash") fileContents("./pkg/usr/bin/bash", "bash") fileSymlinks("./pkg/usr/bin/sh", "bash") // now validate the symlink and file in the symlinked dir that was outside // the symlink should still be absolute to /realetc // but the file should be in ./realetc/zz within the tempDir and not the // system's root... so Untar follows how it knows it should resolve and not // follow the real symlink fileSymlinks("./pkg/etc", "/realetc") fileExists("./realetc/zz") fileContents("./realetc/zz", "zz") }