1
0
mirror of https://github.com/golang/go synced 2024-11-18 15:34:53 -07:00

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
This commit is contained in:
David Symonds 2009-06-10 21:32:36 -07:00
parent 7fd9cfd0cc
commit 61d6ad3178
4 changed files with 163 additions and 40 deletions

BIN
src/pkg/archive/tar/testdata/star.tar vendored Normal file

Binary file not shown.

View File

@ -67,6 +67,8 @@ type Header struct {
Gname string; Gname string;
Devmajor int64; Devmajor int64;
Devminor int64; Devminor int64;
Atime int64;
Ctime int64;
} }
func (tr *Reader) skipUnread() func (tr *Reader) skipUnread()
@ -118,7 +120,10 @@ func cString(b []byte) string {
return string(b[0:n]) 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); x, err := strconv.Btoui64(cString(b), 8);
if err != nil { if err != nil {
tr.err = err; tr.err = err;
@ -149,23 +154,27 @@ func (tr *Reader) skipUnread() {
} }
func (tr *Reader) verifyChecksum(header []byte) bool { func (tr *Reader) verifyChecksum(header []byte) bool {
given := tr.octalNumber(header[148:156]); given := tr.octal(header[148:156]);
if tr.err != nil { if tr.err != nil {
return false 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++ { for i := 0; i < len(header); i++ {
if i == 148 { if i == 148 {
// The chksum field is special: it should be treated as space bytes. // The chksum field is special: it should be treated as space bytes.
computed += ' ' * 8; unsigned += ' ' * 8;
signed += ' ' * 8;
i += 7; i += 7;
continue 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 type slicer []byte
@ -205,15 +214,52 @@ func (tr *Reader) readHeader() *Header {
// so use that value to do the correct parsing below. // so use that value to do the correct parsing below.
hdr.Name = cString(s.next(100)); hdr.Name = cString(s.next(100));
hdr.Mode = tr.octalNumber(s.next(8)); hdr.Mode = tr.octal(s.next(8));
hdr.Uid = tr.octalNumber(s.next(8)); hdr.Uid = tr.octal(s.next(8));
hdr.Gid = tr.octalNumber(s.next(8)); hdr.Gid = tr.octal(s.next(8));
hdr.Size = tr.octalNumber(s.next(12)); hdr.Size = tr.octal(s.next(12));
hdr.Mtime = tr.octalNumber(s.next(12)); hdr.Mtime = tr.octal(s.next(12));
s.next(8); // chksum s.next(8); // chksum
hdr.Typeflag = s.next(1)[0]; hdr.Typeflag = s.next(1)[0];
hdr.Linkname = cString(s.next(100)); 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 { if tr.err != nil {
tr.err = HeaderError; tr.err = HeaderError;

View File

@ -10,11 +10,107 @@ import (
"fmt"; "fmt";
"io"; "io";
"os"; "os";
"reflect";
"testing"; "testing";
) )
func TestUntar(t *testing.T) { type untarTest struct {
f, err := os.Open("testdata/test.tar", os.O_RDONLY, 0444); 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 { if err != nil {
t.Fatalf("Unexpected error: %v", err); t.Fatalf("Unexpected error: %v", err);
} }
@ -22,22 +118,11 @@ func TestUntar(t *testing.T) {
tr := NewReader(f); tr := NewReader(f);
// First file // Read the first four bytes; Next() should skip the last byte.
hdr, err := tr.Next(); hdr, err := tr.Next();
if err != nil || hdr == nil { if err != nil || hdr == nil {
t.Fatalf("Didn't get first file: %v", err); 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); buf := make([]byte, 4);
if n, err := io.FullRead(tr, buf); err != nil { if n, err := io.FullRead(tr, buf); err != nil {
t.Fatalf("Unexpected error: %v", err); t.Fatalf("Unexpected error: %v", err);
@ -48,22 +133,14 @@ func TestUntar(t *testing.T) {
// Second file // Second file
hdr, err = tr.Next(); hdr, err = tr.Next();
if err != nil { if err != nil || hdr == nil {
t.Fatalf("Didn't get second file: %v", err); t.Fatalf("Didn't get second file: %v", err);
} }
if hdr.Name != "small2.txt" { buf = make([]byte, 6);
t.Errorf(`hdr.Name = %q, want "small2.txt"`, hdr.Name); if n, err := io.FullRead(tr, buf); err != nil {
t.Fatalf("Unexpected error: %v", err);
} }
if hdr.Mode != 0640 { if expected := io.StringBytes("Google"); !bytes.Equal(buf, expected) {
t.Errorf("hdr.Mode = %v, want 0640", hdr.Mode); t.Errorf("Contents = %v, want %v", buf, expected);
}
if hdr.Size != 11 {
t.Errorf("hdr.Size = %v, want 11", hdr.Size);
}
hdr, err = tr.Next();
if hdr != nil || err != nil {
t.Fatalf("Unexpected third file or error: %v", err);
} }
} }