]> Cypherpunks repositories - gostls13.git/commitdiff
archive/tar: support extended attributes
authorAlexander Larsson <alexander.larsson@gmail.com>
Thu, 13 Feb 2014 09:08:30 +0000 (20:08 +1100)
committerDavid Symonds <dsymonds@golang.org>
Thu, 13 Feb 2014 09:08:30 +0000 (20:08 +1100)
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

src/pkg/archive/tar/common.go
src/pkg/archive/tar/reader.go
src/pkg/archive/tar/reader_test.go
src/pkg/archive/tar/testdata/xattrs.tar [new file with mode: 0644]
src/pkg/archive/tar/writer.go
src/pkg/archive/tar/writer_test.go

index 1b961e3ec637c4630018d17ac69131cc3b364277..e8b973c1faf0b5b8566b9bc0fc1f9fe4226ae3d4 100644 (file)
@@ -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     = ""
 )
 
index b2d62f3c51c171e10ff237fb25f5e6acbd03341a..7cb6e649c7bec17549063c890ad12f296e3e93a7 100644 (file)
@@ -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
 }
index acd45410e37171b2a6b41e04c5ea2e368a36ad76..f84dbebe98996d18d1cb6eafcf92340b87b03ef7 100644 (file)
@@ -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 (file)
index 0000000..9701950
Binary files /dev/null and b/src/pkg/archive/tar/testdata/xattrs.tar differ
index 549f1464c38b0b2667843f1ba3db9c967525d3f7..9ee94992970eca4bf7b8ec7770616e0ef4767d45 100644 (file)
@@ -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
index 30ebf977acfc09f9c0764065004526069f12cd2b..2b9ea658db479193aebd5e3f391ea361b2869e79 100644 (file)
@@ -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)