From 52c79a819e41e3655327eab5d90aa3867734cc38 Mon Sep 17 00:00:00 2001 From: Jonathan Boulle Date: Fri, 5 Dec 2014 17:40:46 -0800 Subject: [PATCH 1/2] pkg/tarheader: support hard links when building tar headers This introduces an inode "cache" that can be used when populating a tar header (pkg/tarheader.Populate), so that any inodes already seen can be linked to instead of inserting an entire copy of the file into the tar. --- app-container/actool/build.go | 9 ++++++++- pkg/tarheader/pop_darwin.go | 2 +- pkg/tarheader/pop_linux.go | 2 +- pkg/tarheader/pop_posix.go | 10 +++++++++- pkg/tarheader/tarheader.go | 6 +++--- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app-container/actool/build.go b/app-container/actool/build.go index a1730cc..5600639 100644 --- a/app-container/actool/build.go +++ b/app-container/actool/build.go @@ -37,6 +37,8 @@ func init() { } func buildWalker(root string, aw aci.ArchiveWriter, rootfs bool) filepath.WalkFunc { + // cache of inode -> filepath, used to leverage hard links in the archive + inos := map[uint64]string{} return func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -81,7 +83,12 @@ func buildWalker(root string, aw aci.ArchiveWriter, rootfs bool) filepath.WalkFu // modify the Name field of the returned header to provide the // full path name of the file. hdr.Name = relpath - tarheader.Populate(hdr, info) + tarheader.Populate(hdr, info, inos) + // If the file is a hard link we don't need the contents + if hdr.Typeflag == tar.TypeLink { + hdr.Size = 0 + file = nil + } aw.AddFile(relpath, hdr, file) return nil diff --git a/pkg/tarheader/pop_darwin.go b/pkg/tarheader/pop_darwin.go index 99144db..dd0176a 100644 --- a/pkg/tarheader/pop_darwin.go +++ b/pkg/tarheader/pop_darwin.go @@ -13,7 +13,7 @@ func init() { populateHeaderStat = append(populateHeaderStat, populateHeaderCtime) } -func populateHeaderCtime(h *tar.Header, fi os.FileInfo) { +func populateHeaderCtime(h *tar.Header, fi os.FileInfo, _ map[uint64]string) { st, ok := fi.Sys().(*syscall.Stat_t) if !ok { return diff --git a/pkg/tarheader/pop_linux.go b/pkg/tarheader/pop_linux.go index aec5c73..16c0c7e 100644 --- a/pkg/tarheader/pop_linux.go +++ b/pkg/tarheader/pop_linux.go @@ -11,7 +11,7 @@ func init() { populateHeaderStat = append(populateHeaderStat, populateHeaderCtime) } -func populateHeaderCtime(h *tar.Header, fi os.FileInfo) { +func populateHeaderCtime(h *tar.Header, fi os.FileInfo, _ map[uint64]string) { st, ok := fi.Sys().(*syscall.Stat_t) if !ok { return diff --git a/pkg/tarheader/pop_posix.go b/pkg/tarheader/pop_posix.go index 99dcc42..aadefa5 100644 --- a/pkg/tarheader/pop_posix.go +++ b/pkg/tarheader/pop_posix.go @@ -10,11 +10,19 @@ func init() { populateHeaderStat = append(populateHeaderStat, populateHeaderUnix) } -func populateHeaderUnix(h *tar.Header, fi os.FileInfo) { +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[st.Ino] + if ok { + h.Linkname = p + h.Typeflag = tar.TypeLink + } else { + seen[st.Ino] = h.Name + } } diff --git a/pkg/tarheader/tarheader.go b/pkg/tarheader/tarheader.go index f570ac0..d74ec90 100644 --- a/pkg/tarheader/tarheader.go +++ b/pkg/tarheader/tarheader.go @@ -5,10 +5,10 @@ import ( "os" ) -var populateHeaderStat []func(h *tar.Header, fi os.FileInfo) +var populateHeaderStat []func(h *tar.Header, fi os.FileInfo, seen map[uint64]string) -func Populate(h *tar.Header, fi os.FileInfo) { +func Populate(h *tar.Header, fi os.FileInfo, seen map[uint64]string) { for _, pop := range populateHeaderStat { - pop(h, fi) + pop(h, fi, seen) } } From 69e3f6a4fb57b58cc3d94882bcb3b37274d7c9e9 Mon Sep 17 00:00:00 2001 From: Maciej Pasternacki Date: Sat, 6 Dec 2014 19:11:08 +0000 Subject: [PATCH 2/2] pkg/tarheader: cast inode up to uint64 Some systems (FreeBSD included) have uint32 as syscall.Stat_t.Ino --- pkg/tarheader/pop_posix.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/tarheader/pop_posix.go b/pkg/tarheader/pop_posix.go index aadefa5..b454d7f 100644 --- a/pkg/tarheader/pop_posix.go +++ b/pkg/tarheader/pop_posix.go @@ -18,11 +18,11 @@ func populateHeaderUnix(h *tar.Header, fi os.FileInfo, seen map[uint64]string) { h.Uid = int(st.Uid) h.Gid = int(st.Gid) // If we have already seen this inode, generate a hardlink - p, ok := seen[st.Ino] + p, ok := seen[uint64(st.Ino)] if ok { h.Linkname = p h.Typeflag = tar.TypeLink } else { - seen[st.Ino] = h.Name + seen[uint64(st.Ino)] = h.Name } }