func (decoderData *decoderData) addHardlink(header *tar.Header, parent *filesystem.DirectoryInode, name string) error { header.Linkname = normaliseFilename(header.Linkname) if inum, ok := decoderData.inodeTable[header.Linkname]; ok { var newEntry filesystem.DirectoryEntry newEntry.Name = name newEntry.InodeNumber = inum parent.EntryList = append(parent.EntryList, &newEntry) } else { return errors.New(fmt.Sprintf("missing hardlink target: %s", header.Linkname)) } return nil }
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 } }
// 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 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 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) }
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") }
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 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 }