From: David Symonds Date: Thu, 11 Jun 2009 04:32:36 +0000 (-0700) Subject: Add support for the basic extension done by Schilling's star. X-Git-Tag: weekly.2009-11-06~1409 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=61d6ad317856c85987b146ef6f65d0dd913f7bfa;p=gostls13.git Add support for the basic extension done by Schilling's star. Compute checksums in both ways (unsigned and signed). R=rsc APPROVED=rsc DELTA=188 (145 added, 21 deleted, 22 changed) OCL=30126 CL=30179 --- diff --git a/src/pkg/archive/tar/testdata/test.tar b/src/pkg/archive/tar/testdata/gnu.tar similarity index 100% rename from src/pkg/archive/tar/testdata/test.tar rename to src/pkg/archive/tar/testdata/gnu.tar diff --git a/src/pkg/archive/tar/testdata/star.tar b/src/pkg/archive/tar/testdata/star.tar new file mode 100644 index 0000000000..59e2d4e604 Binary files /dev/null and b/src/pkg/archive/tar/testdata/star.tar differ diff --git a/src/pkg/archive/tar/untar.go b/src/pkg/archive/tar/untar.go index 300c0f932c..8446cc2159 100644 --- a/src/pkg/archive/tar/untar.go +++ b/src/pkg/archive/tar/untar.go @@ -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; diff --git a/src/pkg/archive/tar/untar_test.go b/src/pkg/archive/tar/untar_test.go index a9c92dbf05..d692e1ae26 100644 --- a/src/pkg/archive/tar/untar_test.go +++ b/src/pkg/archive/tar/untar_test.go @@ -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); } }