Skip to content

Instantly share code, notes, and snippets.

@vbatts
Last active August 29, 2015 14:14
Show Gist options
  • Save vbatts/d35e9f18fbfac74b36f4 to your computer and use it in GitHub Desktop.
Save vbatts/d35e9f18fbfac74b36f4 to your computer and use it in GitHub Desktop.
investigating archive permutations due to hardlinks

Issue

The difference in the sum, has to do with an arbitrary order that can occur
when tar'ing hardlinks. The first file encountered pointing to the inode wins
and has it's Size recorded. Then following files that may have the same inode, then have their LinkName point to the first file's Name, and their Size is 0.

WIP to fix ti https://github.com/vbatts/docker/compare/vbatts-archive_sort

usage

    sh repro.sh

Which will show the diff between the Tar Headers of two identical docker save outputs. This diff is more visually useful with an application like kompare, but I'll leave it to you to install that.

package main
import (
"archive/tar"
"encoding/json"
"fmt"
"io"
"log"
"os"
"sort"
)
// docker save ubuntu:utopic | tar -Ox ef3ba9f35b97ff1ba5c14a82ce512cf9df8736a3bed3ca44ea3a35dddc3c1019/layer.tar | ./ubuntu
func main() {
var (
headers []*tar.Header
err error
)
if len(os.Args[1:]) == 0 {
headers, err = WalkHeaders(os.Stdin)
if err != nil {
log.Fatalf("stdin - %s", err)
}
} else {
fh, err := os.Open(os.Args[1])
if err != nil {
log.Fatalf("%q - %s", os.Args[1], err)
}
headers, err = WalkHeaders(fh)
if err != nil {
log.Fatalf("stdin - %s", err)
}
}
fmt.Fprintf(os.Stderr, "%d headers\n", len(headers))
sort.Sort(TarHeaders(headers))
for i := range headers {
buf, err := json.Marshal(headers[i])
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", buf)
}
}
type TarHeaders []*tar.Header
func (th TarHeaders) Len() int { return len(th) }
func (th TarHeaders) Swap(i, j int) { th[j], th[i] = th[i], th[j] }
func (th TarHeaders) Less(i, j int) bool { return th[i].Name < th[j].Name }
func WalkHeaders(r io.Reader) ([]*tar.Header, error) {
t := tar.NewReader(r)
headers := []*tar.Header{}
for {
hdr, err := t.Next()
if err != nil {
if err == io.EOF {
break
}
return headers, err
}
headers = append(headers, hdr)
}
return headers, nil
}
#!/bin/sh
set -x
DOCKER=${DOCKER:-docker}
IMAGE=${IMAGE:-"ubuntu:utopic"}
ID=${ID:-ef3ba9f35b97ff1ba5c14a82ce512cf9df8736a3bed3ca44ea3a35dddc3c1019}
if ! $DOCKER images -a --no-trunc | grep -q ${ID} ; then
$DOCKER pull ${IMAGE}
fi
if ! $DOCKER images -a --no-trunc | grep -q ${ID} ; then
echo ERROR: failed to find image id ${ID}
exit 1
fi
go build hdr-json.go
${DOCKER} save ${IMAGE} | tar -Ox ${ID}/layer.tar | ./hdr-json > 1.list
${DOCKER} save ${IMAGE} | tar -Ox ${ID}/layer.tar | ./hdr-json > 2.list
diff -up ./1.list ./2.list
--- ./1.list 2015-02-05 15:59:40.528815200 +0100
+++ ./2.list 2015-02-05 15:59:42.450806106 +0100
@@ -1,14 +1,14 @@
{"Name":"bin/","Mode":16877,"Uid":0,"Gid":0,"Size":0,"ModTime":"2015-01-28T17:34:24+01:00","Typeflag":53,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bash","Mode":33261,"Uid":0,"Gid":0,"Size":1029720,"ModTime":"2014-10-07T17:15:38+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"bin/bunzip2","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2014-10-21T20:16:10+02:00","Typeflag":49,"Linkname":"bin/bzip2","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"bin/bzcat","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2014-10-21T20:16:10+02:00","Typeflag":49,"Linkname":"bin/bzip2","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"bin/bunzip2","Mode":33261,"Uid":0,"Gid":0,"Size":31288,"ModTime":"2014-10-21T20:16:10+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"bin/bzcat","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2014-10-21T20:16:10+02:00","Typeflag":49,"Linkname":"bin/bunzip2","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzcmp","Mode":41471,"Uid":0,"Gid":0,"Size":0,"ModTime":"2014-10-21T20:16:08+02:00","Typeflag":50,"Linkname":"bzdiff","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzdiff","Mode":33261,"Uid":0,"Gid":0,"Size":2140,"ModTime":"2014-10-21T20:16:05+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzegrep","Mode":41471,"Uid":0,"Gid":0,"Size":0,"ModTime":"2014-10-21T20:16:08+02:00","Typeflag":50,"Linkname":"bzgrep","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzexe","Mode":33261,"Uid":0,"Gid":0,"Size":4877,"ModTime":"2014-10-21T20:16:05+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzfgrep","Mode":41471,"Uid":0,"Gid":0,"Size":0,"ModTime":"2014-10-21T20:16:08+02:00","Typeflag":50,"Linkname":"bzgrep","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzgrep","Mode":33261,"Uid":0,"Gid":0,"Size":3642,"ModTime":"2014-10-21T20:16:05+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"bin/bzip2","Mode":33261,"Uid":0,"Gid":0,"Size":31288,"ModTime":"2014-10-21T20:16:10+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"bin/bzip2","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2014-10-21T20:16:10+02:00","Typeflag":49,"Linkname":"bin/bunzip2","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzip2recover","Mode":33261,"Uid":0,"Gid":0,"Size":14616,"ModTime":"2014-10-21T20:16:10+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzless","Mode":41471,"Uid":0,"Gid":0,"Size":0,"ModTime":"2014-10-21T20:16:08+02:00","Typeflag":50,"Linkname":"bzmore","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"bin/bzmore","Mode":33261,"Uid":0,"Gid":0,"Size":1297,"ModTime":"2014-10-21T20:16:05+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
@@ -1498,10 +1498,10 @@
{"Name":"usr/bin/loadunimap","Mode":33261,"Uid":0,"Gid":0,"Size":23144,"ModTime":"2013-02-18T14:55:53+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/locale","Mode":33261,"Uid":0,"Gid":0,"Size":38704,"ModTime":"2014-12-10T18:10:09+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/localedef","Mode":33261,"Uid":0,"Gid":0,"Size":319088,"ModTime":"2014-12-10T18:10:09+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"usr/bin/lockfile-check","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/lockfile-touch","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"usr/bin/lockfile-create","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/lockfile-touch","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"usr/bin/lockfile-remove","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/lockfile-touch","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"usr/bin/lockfile-touch","Mode":33261,"Uid":0,"Gid":0,"Size":14592,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"usr/bin/lockfile-check","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/lockfile-create","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"usr/bin/lockfile-create","Mode":33261,"Uid":0,"Gid":0,"Size":14592,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"usr/bin/lockfile-remove","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/lockfile-create","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"usr/bin/lockfile-touch","Mode":33261,"Uid":0,"Gid":0,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/lockfile-create","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/logger","Mode":33261,"Uid":0,"Gid":0,"Size":27848,"ModTime":"2014-10-15T17:28:24+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/logname","Mode":33261,"Uid":0,"Gid":0,"Size":27200,"ModTime":"2014-09-08T13:12:01+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/lsattr","Mode":33261,"Uid":0,"Gid":0,"Size":10512,"ModTime":"2014-07-07T17:02:30+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
@@ -1510,9 +1510,9 @@
{"Name":"usr/bin/lsinitramfs","Mode":33261,"Uid":0,"Gid":0,"Size":767,"ModTime":"2012-06-01T05:36:34+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/lslocks","Mode":33261,"Uid":0,"Gid":0,"Size":27640,"ModTime":"2014-10-15T17:28:24+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/lspgpot","Mode":33261,"Uid":0,"Gid":0,"Size":1081,"ModTime":"2013-03-15T20:23:50+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"usr/bin/mail-lock","Mode":34285,"Uid":0,"Gid":8,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/mail-unlock","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"usr/bin/mail-touchlock","Mode":34285,"Uid":0,"Gid":8,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/mail-unlock","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
-{"Name":"usr/bin/mail-unlock","Mode":34285,"Uid":0,"Gid":8,"Size":14592,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"usr/bin/mail-lock","Mode":34285,"Uid":0,"Gid":8,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/mail-touchlock","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"usr/bin/mail-touchlock","Mode":34285,"Uid":0,"Gid":8,"Size":14592,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
+{"Name":"usr/bin/mail-unlock","Mode":34285,"Uid":0,"Gid":8,"Size":0,"ModTime":"2012-12-04T00:37:50+01:00","Typeflag":49,"Linkname":"usr/bin/mail-touchlock","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/mapscrn","Mode":33261,"Uid":0,"Gid":0,"Size":19080,"ModTime":"2013-02-18T14:55:53+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/mawk","Mode":33261,"Uid":0,"Gid":0,"Size":117768,"ModTime":"2014-03-24T10:53:33+01:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
{"Name":"usr/bin/mcookie","Mode":33261,"Uid":0,"Gid":0,"Size":27320,"ModTime":"2014-10-15T17:28:24+02:00","Typeflag":48,"Linkname":"","Uname":"","Gname":"","Devmajor":0,"Devminor":0,"AccessTime":"0001-01-01T00:00:00Z","ChangeTime":"0001-01-01T00:00:00Z","Xattrs":null}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment