From: Alexander Larsson Date: Thu, 13 Feb 2014 09:08:30 +0000 (+1100) Subject: archive/tar: support extended attributes X-Git-Tag: go1.3beta1~733 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=87d58f44a1e53a61c7bd4b11a7f7aa616f6e373d;p=gostls13.git archive/tar: support extended attributes This adds support for archives with the SCHILY.xattr field in the pax header. This is what gnu tar and star generate. Fixes #7154. LGTM=dsymonds R=golang-codereviews, gobot, dsymonds CC=golang-codereviews https://golang.org/cl/54570043 --- diff --git a/src/pkg/archive/tar/common.go b/src/pkg/archive/tar/common.go index 1b961e3ec6..e8b973c1fa 100644 --- a/src/pkg/archive/tar/common.go +++ b/src/pkg/archive/tar/common.go @@ -57,6 +57,7 @@ type Header struct { Devminor int64 // minor number of character or block device AccessTime time.Time // access time ChangeTime time.Time // status change time + Xattrs map[string]string } // File name constants from the tar spec. @@ -189,6 +190,7 @@ const ( paxSize = "size" paxUid = "uid" paxUname = "uname" + paxXattr = "SCHILY.xattr." paxNone = "" ) diff --git a/src/pkg/archive/tar/reader.go b/src/pkg/archive/tar/reader.go index b2d62f3c51..7cb6e649c7 100644 --- a/src/pkg/archive/tar/reader.go +++ b/src/pkg/archive/tar/reader.go @@ -139,8 +139,14 @@ func mergePAX(hdr *Header, headers map[string]string) error { return err } hdr.Size = int64(size) + default: + if strings.HasPrefix(k, paxXattr) { + if hdr.Xattrs == nil { + hdr.Xattrs = make(map[string]string) + } + hdr.Xattrs[k[len(paxXattr):]] = v + } } - } return nil } diff --git a/src/pkg/archive/tar/reader_test.go b/src/pkg/archive/tar/reader_test.go index acd45410e3..f84dbebe98 100644 --- a/src/pkg/archive/tar/reader_test.go +++ b/src/pkg/archive/tar/reader_test.go @@ -161,6 +161,46 @@ var untarTests = []*untarTest{ }, }, }, + { + file: "testdata/xattrs.tar", + headers: []*Header{ + { + Name: "small.txt", + Mode: 0644, + Uid: 1000, + Gid: 10, + Size: 5, + ModTime: time.Unix(1386065770, 448252320), + Typeflag: '0', + Uname: "alex", + Gname: "wheel", + AccessTime: time.Unix(1389782991, 419875220), + ChangeTime: time.Unix(1389782956, 794414986), + Xattrs: map[string]string{ + "user.key": "value", + "user.key2": "value2", + // Interestingly, selinux encodes the terminating null inside the xattr + "security.selinux": "unconfined_u:object_r:default_t:s0\x00", + }, + }, + { + Name: "small2.txt", + Mode: 0644, + Uid: 1000, + Gid: 10, + Size: 11, + ModTime: time.Unix(1386065770, 449252304), + Typeflag: '0', + Uname: "alex", + Gname: "wheel", + AccessTime: time.Unix(1389782991, 419875220), + ChangeTime: time.Unix(1386065770, 449252304), + Xattrs: map[string]string{ + "security.selinux": "unconfined_u:object_r:default_t:s0\x00", + }, + }, + }, + }, } func TestReader(t *testing.T) { @@ -180,7 +220,7 @@ testLoop: f.Close() continue testLoop } - if *hdr != *header { + if !reflect.DeepEqual(*hdr, *header) { t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v", i, j, *hdr, *header) } @@ -253,7 +293,7 @@ func TestIncrementalRead(t *testing.T) { } // check the header - if *hdr != *headers[nread] { + if !reflect.DeepEqual(*hdr, *headers[nread]) { t.Errorf("Incorrect header:\nhave %+v\nwant %+v", *hdr, headers[nread]) } diff --git a/src/pkg/archive/tar/testdata/xattrs.tar b/src/pkg/archive/tar/testdata/xattrs.tar new file mode 100644 index 0000000000..9701950edd Binary files /dev/null and b/src/pkg/archive/tar/testdata/xattrs.tar differ diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go index 549f1464c3..9ee9499297 100644 --- a/src/pkg/archive/tar/writer.go +++ b/src/pkg/archive/tar/writer.go @@ -236,6 +236,12 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { return tw.err } + if allowPax { + for k, v := range hdr.Xattrs { + paxHeaders[paxXattr+k] = v + } + } + if len(paxHeaders) > 0 { if !allowPax { return errInvalidHeader diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go index 30ebf977ac..2b9ea658db 100644 --- a/src/pkg/archive/tar/writer_test.go +++ b/src/pkg/archive/tar/writer_test.go @@ -10,6 +10,7 @@ import ( "io" "io/ioutil" "os" + "reflect" "strings" "testing" "testing/iotest" @@ -338,6 +339,45 @@ func TestPaxNonAscii(t *testing.T) { } } +func TestPaxXattrs(t *testing.T) { + xattrs := map[string]string{ + "user.key": "value", + } + + // Create an archive with an xattr + 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 := "Kilts" + hdr.Xattrs = xattrs + 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) + } + // Test that we can get the xattrs back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(hdr.Xattrs, xattrs) { + t.Fatalf("xattrs did not survive round trip: got %+v, want %+v", + hdr.Xattrs, xattrs) + } +} + func TestPAXHeader(t *testing.T) { medName := strings.Repeat("CD", 50) longName := strings.Repeat("AB", 100)