"errors"
"fmt"
"io"
- "os"
"path"
+ "sort"
"strconv"
"strings"
"time"
// succeed, and seems harmless enough.
ext.ModTime = hdr.ModTime
// The spec asks that we namespace our pseudo files
- // with the current pid.
- pid := os.Getpid()
+ // with the current pid. However, this results in differing outputs
+ // for identical inputs. As such, the constant 0 is now used instead.
+ // golang.org/issue/12358
dir, file := path.Split(hdr.Name)
- fullName := path.Join(dir,
- fmt.Sprintf("PaxHeaders.%d", pid), file)
+ fullName := path.Join(dir, "PaxHeaders.0", file)
ascii := toASCII(fullName)
if len(ascii) > 100 {
// Construct the body
var buf bytes.Buffer
- for k, v := range paxHeaders {
- fmt.Fprint(&buf, paxHeader(k+"="+v))
+ // Keys are sorted before writing to body to allow deterministic output.
+ var keys []string
+ for k := range paxHeaders {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ for _, k := range keys {
+ fmt.Fprint(&buf, paxHeader(k+"="+paxHeaders[k]))
}
ext.Size = int64(len(buf.Bytes()))
"io/ioutil"
"os"
"reflect"
+ "sort"
"strings"
"testing"
"testing/iotest"
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
- if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
+ if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// Test that we can get a long name back out of the archive.
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
- if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
+ if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// Test that we can get a long name back out of the archive.
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
- if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
+ if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// Test that we can get a long name back out of the archive.
}
}
+func TestPaxHeadersSorted(t *testing.T) {
+ fileinfo, err := os.Stat("testdata/small.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+ hdr, err := FileInfoHeader(fileinfo, "")
+ if err != nil {
+ t.Fatalf("os.Stat: %v", err)
+ }
+ contents := strings.Repeat(" ", int(hdr.Size))
+
+ hdr.Xattrs = map[string]string{
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz",
+ "qux": "qux",
+ }
+
+ var buf bytes.Buffer
+ writer := NewWriter(&buf)
+ if err := writer.WriteHeader(hdr); err != nil {
+ t.Fatal(err)
+ }
+ if _, err = writer.Write([]byte(contents)); err != nil {
+ t.Fatal(err)
+ }
+ if err := writer.Close(); err != nil {
+ t.Fatal(err)
+ }
+ // Simple test to make sure PAX extensions are in effect
+ if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
+ t.Fatal("Expected at least one PAX header to be written.")
+ }
+
+ // xattr bar should always appear before others
+ indices := []int{
+ bytes.Index(buf.Bytes(), []byte("bar=bar")),
+ bytes.Index(buf.Bytes(), []byte("baz=baz")),
+ bytes.Index(buf.Bytes(), []byte("foo=foo")),
+ bytes.Index(buf.Bytes(), []byte("qux=qux")),
+ }
+ if !sort.IntsAreSorted(indices) {
+ t.Fatal("PAX headers are not sorted")
+ }
+}
+
func TestPAXHeader(t *testing.T) {
medName := strings.Repeat("CD", 50)
longName := strings.Repeat("AB", 100)