]> Cypherpunks repositories - gostls13.git/commitdiff
Add support for the basic extension done by Schilling's star.
authorDavid Symonds <dsymonds@golang.org>
Thu, 11 Jun 2009 04:32:36 +0000 (21:32 -0700)
committerDavid Symonds <dsymonds@golang.org>
Thu, 11 Jun 2009 04:32:36 +0000 (21:32 -0700)
Compute checksums in both ways (unsigned and signed).

R=rsc
APPROVED=rsc
DELTA=188  (145 added, 21 deleted, 22 changed)
OCL=30126
CL=30179

src/pkg/archive/tar/testdata/gnu.tar [moved from src/pkg/archive/tar/testdata/test.tar with 100% similarity]
src/pkg/archive/tar/testdata/star.tar [new file with mode: 0644]
src/pkg/archive/tar/untar.go
src/pkg/archive/tar/untar_test.go

diff --git a/src/pkg/archive/tar/testdata/star.tar b/src/pkg/archive/tar/testdata/star.tar
new file mode 100644 (file)
index 0000000..59e2d4e
Binary files /dev/null and b/src/pkg/archive/tar/testdata/star.tar differ
index 300c0f932c6bb9fa909a1929e527c330da9647cb..8446cc2159ff788a120b159a52c8adb43336f4fd 100644 (file)
@@ -67,6 +67,8 @@ type Header struct {
        Gname string;
        Devmajor int64;
        Devminor int64;
+       Atime int64;
+       Ctime int64;
 }
 
 func (tr *Reader) skipUnread()
@@ -118,7 +120,10 @@ func cString(b []byte) string {
        return string(b[0:n])
 }
 
-func (tr *Reader) octalNumber(b []byte) int64 {
+func (tr *Reader) octal(b []byte) int64 {
+       if len(b) > 0 && b[len(b)-1] == ' ' {
+               b = b[0:len(b)-1];
+       }
        x, err := strconv.Btoui64(cString(b), 8);
        if err != nil {
                tr.err = err;
@@ -149,23 +154,27 @@ func (tr *Reader) skipUnread() {
 }
 
 func (tr *Reader) verifyChecksum(header []byte) bool {
-       given := tr.octalNumber(header[148:156]);
+       given := tr.octal(header[148:156]);
        if tr.err != nil {
                return false
        }
 
-       var computed int64;
+       // POSIX specifies a sum of the unsigned byte values,
+       // but the Sun tar uses signed byte values.  :-(
+       var unsigned, signed int64;
        for i := 0; i < len(header); i++ {
                if i == 148 {
                        // The chksum field is special: it should be treated as space bytes.
-                       computed += ' ' * 8;
+                       unsigned += ' ' * 8;
+                       signed += ' ' * 8;
                        i += 7;
                        continue
                }
-               computed += int64(header[i]);
+               unsigned += int64(header[i]);
+               signed += int64(int8(header[i]));
        }
 
-       return given == computed
+       return given == unsigned || given == signed
 }
 
 type slicer []byte
@@ -205,15 +214,52 @@ func (tr *Reader) readHeader() *Header {
        // so use that value to do the correct parsing below.
 
        hdr.Name = cString(s.next(100));
-       hdr.Mode = tr.octalNumber(s.next(8));
-       hdr.Uid = tr.octalNumber(s.next(8));
-       hdr.Gid = tr.octalNumber(s.next(8));
-       hdr.Size = tr.octalNumber(s.next(12));
-       hdr.Mtime = tr.octalNumber(s.next(12));
+       hdr.Mode = tr.octal(s.next(8));
+       hdr.Uid = tr.octal(s.next(8));
+       hdr.Gid = tr.octal(s.next(8));
+       hdr.Size = tr.octal(s.next(12));
+       hdr.Mtime = tr.octal(s.next(12));
        s.next(8);  // chksum
        hdr.Typeflag = s.next(1)[0];
        hdr.Linkname = cString(s.next(100));
-       s.next(8);  // magic, version
+
+       // The remainder of the header depends on the value of magic.
+       magic := string(s.next(8));  // contains version field as well.
+       var format string;
+       switch magic {
+       case "ustar\x0000":  // POSIX tar (1003.1-1988)
+               if string(header[508:512]) == "tar\x00" {
+                       format = "star";
+               } else {
+                       format = "posix";
+               }
+       case "ustar  \x00":  // old GNU tar
+               format = "gnu";
+       }
+
+       switch format {
+       case "posix", "gnu", "star":
+               hdr.Uname = cString(s.next(32));
+               hdr.Gname = cString(s.next(32));
+               devmajor := s.next(8);
+               devminor := s.next(8);
+               if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock {
+                       hdr.Devmajor = tr.octal(devmajor);
+                       hdr.Devminor = tr.octal(devminor);
+               }
+               var prefix string;
+               switch format {
+               case "posix", "gnu":
+                       prefix = cString(s.next(155));
+               case "star":
+                       prefix = cString(s.next(131));
+                       hdr.Atime = tr.octal(s.next(12));
+                       hdr.Ctime = tr.octal(s.next(12));
+               }
+               if len(prefix) > 0 {
+                       hdr.Name = prefix + "/" + hdr.Name;
+               }
+       }
 
        if tr.err != nil {
                tr.err = HeaderError;
index a9c92dbf05b77ed5aae8aa81193f1e30f20ebc18..d692e1ae268fb85d759cc0d9d6767bc03da3706f 100644 (file)
@@ -10,11 +10,107 @@ import (
        "fmt";
        "io";
        "os";
+       "reflect";
        "testing";
 )
 
-func TestUntar(t *testing.T) {
-       f, err := os.Open("testdata/test.tar", os.O_RDONLY, 0444);
+type untarTest struct {
+       file string;
+       headers []*Header;
+}
+
+var untarTests = []*untarTest{
+       &untarTest{
+               file: "testdata/gnu.tar",
+               headers: []*Header{
+                       &Header{
+                               Name: "small.txt",
+                               Mode: 0640,
+                               Uid: 73025,
+                               Gid: 5000,
+                               Size: 5,
+                               Mtime: 1244428340,
+                               Typeflag: '0',
+                               Uname: "dsymonds",
+                               Gname: "eng",
+                       },
+                       &Header{
+                               Name: "small2.txt",
+                               Mode: 0640,
+                               Uid: 73025,
+                               Gid: 5000,
+                               Size: 11,
+                               Mtime: 1244436044,
+                               Typeflag: '0',
+                               Uname: "dsymonds",
+                               Gname: "eng",
+                       },
+               },
+       },
+       &untarTest{
+               file: "testdata/star.tar",
+               headers: []*Header{
+                       &Header{
+                               Name: "small.txt",
+                               Mode: 0640,
+                               Uid: 73025,
+                               Gid: 5000,
+                               Size: 5,
+                               Mtime: 1244592783,
+                               Typeflag: '0',
+                               Uname: "dsymonds",
+                               Gname: "eng",
+                               Atime: 1244592783,
+                               Ctime: 1244592783,
+                       },
+                       &Header{
+                               Name: "small2.txt",
+                               Mode: 0640,
+                               Uid: 73025,
+                               Gid: 5000,
+                               Size: 11,
+                               Mtime: 1244592783,
+                               Typeflag: '0',
+                               Uname: "dsymonds",
+                               Gname: "eng",
+                               Atime: 1244592783,
+                               Ctime: 1244592783,
+                       },
+               },
+       },
+};
+
+func TestAll(t *testing.T) {
+testLoop:
+       for i, test := range untarTests {
+               f, err := os.Open(test.file, os.O_RDONLY, 0444);
+               if err != nil {
+                       t.Errorf("test %d: Unexpected error: %v", i, err);
+                       continue
+               }
+               tr := NewReader(f);
+               for j, header := range test.headers {
+                       hdr, err := tr.Next();
+                       if err != nil || hdr == nil {
+                               t.Errorf("test %d, entry %d: Didn't get entry: %v", i, j, err);
+                               f.Close();
+                               continue testLoop
+                       }
+                       if !reflect.DeepEqual(hdr, header) {
+                               t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v",
+                                        i, j, *hdr, *header);
+                       }
+               }
+               hdr, err := tr.Next();
+               if hdr != nil || err != nil {
+                       t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, err);
+               }
+               f.Close();
+       }
+}
+
+func TestPartialRead(t *testing.T) {
+       f, err := os.Open("testdata/gnu.tar", os.O_RDONLY, 0444);
        if err != nil {
                t.Fatalf("Unexpected error: %v", err);
        }
@@ -22,22 +118,11 @@ func TestUntar(t *testing.T) {
 
        tr := NewReader(f);
 
-       // First file
+       // Read the first four bytes; Next() should skip the last byte.
        hdr, err := tr.Next();
        if err != nil || hdr == nil {
                t.Fatalf("Didn't get first file: %v", err);
        }
-       if hdr.Name != "small.txt" {
-               t.Errorf(`hdr.Name = %q, want "small.txt"`, hdr.Name);
-       }
-       if hdr.Mode != 0640 {
-               t.Errorf("hdr.Mode = %v, want 0640", hdr.Mode);
-       }
-       if hdr.Size != 5 {
-               t.Errorf("hdr.Size = %v, want 5", hdr.Size);
-       }
-
-       // Read the first four bytes; Next() should skip the last one.
        buf := make([]byte, 4);
        if n, err := io.FullRead(tr, buf); err != nil {
                t.Fatalf("Unexpected error: %v", err);
@@ -48,22 +133,14 @@ func TestUntar(t *testing.T) {
 
        // Second file
        hdr, err = tr.Next();
-       if err != nil {
+       if err != nil || hdr == nil {
                t.Fatalf("Didn't get second file: %v", err);
        }
-       if hdr.Name != "small2.txt" {
-               t.Errorf(`hdr.Name = %q, want "small2.txt"`, hdr.Name);
-       }
-       if hdr.Mode != 0640 {
-               t.Errorf("hdr.Mode = %v, want 0640", hdr.Mode);
-       }
-       if hdr.Size != 11 {
-               t.Errorf("hdr.Size = %v, want 11", hdr.Size);
+       buf = make([]byte, 6);
+       if n, err := io.FullRead(tr, buf); err != nil {
+               t.Fatalf("Unexpected error: %v", err);
        }
-
-
-       hdr, err = tr.Next();
-       if hdr != nil || err != nil {
-               t.Fatalf("Unexpected third file or error: %v", err);
+       if expected := io.StringBytes("Google"); !bytes.Equal(buf, expected) {
+               t.Errorf("Contents = %v, want %v", buf, expected);
        }
 }