Commit 61d6ad31 authored by David Symonds's avatar David Symonds

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
parent 7fd9cfd0
......@@ -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;
......
......@@ -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);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment