summaryrefslogtreecommitdiff
path: root/src/pkg/archive
diff options
context:
space:
mode:
authorDavid Symonds <dsymonds@golang.org>2009-06-10 21:32:36 -0700
committerDavid Symonds <dsymonds@golang.org>2009-06-10 21:32:36 -0700
commit67fe3450942aa494a508ac17b9391f67edb59037 (patch)
tree128839b99f8011e2b07a278e307d4ceff34d86f0 /src/pkg/archive
parent71f190b1f173e37884be4038e264390df6d1b6e6 (diff)
downloadgolang-67fe3450942aa494a508ac17b9391f67edb59037.tar.gz
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
Diffstat (limited to 'src/pkg/archive')
-rw-r--r--src/pkg/archive/tar/testdata/gnu.tar (renamed from src/pkg/archive/tar/testdata/test.tar)bin3072 -> 3072 bytes
-rw-r--r--src/pkg/archive/tar/testdata/star.tarbin0 -> 3072 bytes
-rw-r--r--src/pkg/archive/tar/untar.go70
-rw-r--r--src/pkg/archive/tar/untar_test.go133
4 files changed, 163 insertions, 40 deletions
diff --git a/src/pkg/archive/tar/testdata/test.tar b/src/pkg/archive/tar/testdata/gnu.tar
index fc899dc8d..fc899dc8d 100644
--- a/src/pkg/archive/tar/testdata/test.tar
+++ b/src/pkg/archive/tar/testdata/gnu.tar
Binary files differ
diff --git a/src/pkg/archive/tar/testdata/star.tar b/src/pkg/archive/tar/testdata/star.tar
new file mode 100644
index 000000000..59e2d4e60
--- /dev/null
+++ b/src/pkg/archive/tar/testdata/star.tar
Binary files differ
diff --git a/src/pkg/archive/tar/untar.go b/src/pkg/archive/tar/untar.go
index 300c0f932..8446cc215 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 a9c92dbf0..d692e1ae2 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);
}
}