summaryrefslogtreecommitdiff
path: root/src/pkg/archive
diff options
context:
space:
mode:
authorDavid Symonds <dsymonds@golang.org>2009-10-05 04:08:24 -0700
committerDavid Symonds <dsymonds@golang.org>2009-10-05 04:08:24 -0700
commita3ce9fff79da30bcc57fdddc4feda3060b8e6877 (patch)
treef6f2e2b5dcd6e0e1693c20978de3f35dc9ce68c0 /src/pkg/archive
parent75df1a1c6c07eed1a469e24c1e18c2deff801b6c (diff)
downloadgolang-a3ce9fff79da30bcc57fdddc4feda3060b8e6877.tar.gz
Add write support for the GNU tar binary numeric field extension.
R=rsc APPROVED=rsc DELTA=102 (89 added, 1 deleted, 12 changed) OCL=35321 CL=35327
Diffstat (limited to 'src/pkg/archive')
-rw-r--r--src/pkg/archive/tar/testdata/writer-big.tarbin0 -> 4096 bytes
-rw-r--r--src/pkg/archive/tar/writer.go42
-rw-r--r--src/pkg/archive/tar/writer_test.go32
3 files changed, 61 insertions, 13 deletions
diff --git a/src/pkg/archive/tar/testdata/writer-big.tar b/src/pkg/archive/tar/testdata/writer-big.tar
new file mode 100644
index 000000000..753e883ce
--- /dev/null
+++ b/src/pkg/archive/tar/testdata/writer-big.tar
Binary files differ
diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go
index 42e628f5c..745a7c43d 100644
--- a/src/pkg/archive/tar/writer.go
+++ b/src/pkg/archive/tar/writer.go
@@ -16,9 +16,8 @@ import (
)
var (
- ErrWriteTooLong os.Error = os.ErrorString("write too long");
- // TODO(dsymonds): remove ErrIntFieldTooBig after we implement binary extension.
- ErrIntFieldTooBig os.Error = os.ErrorString("an integer header field was too big");
+ ErrWriteTooLong = os.NewError("write too long");
+ ErrFieldTooLong = os.NewError("header field too long");
)
// A Writer provides sequential writing of a tar archive in POSIX.1 format.
@@ -42,6 +41,7 @@ type Writer struct {
nb int64; // number of unwritten bytes for current file entry
pad int64; // amount of padding to write after current file entry
closed bool;
+ usedBinary bool; // whether the binary numeric field extension was used
}
// NewWriter creates a new Writer writing to w.
@@ -70,7 +70,7 @@ func (tw *Writer) Flush() os.Error {
func (tw *Writer) cString(b []byte, s string) {
if len(s) > len(b) {
if tw.err == nil {
- tw.err = ErrIntFieldTooBig;
+ tw.err = ErrFieldTooLong;
}
return
}
@@ -92,6 +92,23 @@ func (tw *Writer) octal(b []byte, x int64) {
tw.cString(b, s);
}
+// Write x into b, either as octal or as binary (GNUtar/star extension).
+func (tw *Writer) numeric(b []byte, x int64) {
+ // Try octal first.
+ s := strconv.Itob64(x, 8);
+ if len(s) < len(b) {
+ tw.octal(b, x);
+ return
+ }
+ // Too big: use binary (big-endian).
+ tw.usedBinary = true;
+ for i := len(b)-1; x > 0 && i >= 0; i-- {
+ b[i] = byte(x);
+ x >>= 8;
+ }
+ b[0] |= 0x80; // highest bit indicates binary format
+}
+
// WriteHeader writes hdr and prepares to accept the file's contents.
// WriteHeader calls Flush if it is not the first header.
func (tw *Writer) WriteHeader(hdr *Header) os.Error {
@@ -112,18 +129,23 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error {
bytes.Copy(s.next(100), strings.Bytes(hdr.Name));
tw.octal(s.next(8), hdr.Mode); // 100:108
- tw.octal(s.next(8), hdr.Uid); // 108:116
- tw.octal(s.next(8), hdr.Gid); // 116:124
- tw.octal(s.next(12), hdr.Size); // 124:136
- tw.octal(s.next(12), hdr.Mtime); // 136:148
+ tw.numeric(s.next(8), hdr.Uid); // 108:116
+ tw.numeric(s.next(8), hdr.Gid); // 116:124
+ tw.numeric(s.next(12), hdr.Size); // 124:136
+ tw.numeric(s.next(12), hdr.Mtime); // 136:148
s.next(8); // chksum (148:156)
s.next(1)[0] = hdr.Typeflag; // 156:157
s.next(100); // linkname (157:257)
bytes.Copy(s.next(8), strings.Bytes("ustar\x0000")); // 257:265
tw.cString(s.next(32), hdr.Uname); // 265:297
tw.cString(s.next(32), hdr.Gname); // 297:329
- tw.octal(s.next(8), hdr.Devmajor); // 329:337
- tw.octal(s.next(8), hdr.Devminor); // 337:345
+ tw.numeric(s.next(8), hdr.Devmajor); // 329:337
+ tw.numeric(s.next(8), hdr.Devminor); // 337:345
+
+ // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
+ if tw.usedBinary {
+ bytes.Copy(header[257:265], strings.Bytes("ustar \x00"));
+ }
// The chksum field is terminated by a NUL and a space.
// This is different from the other octal fields.
diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go
index 69f069ff3..cd67fedf0 100644
--- a/src/pkg/archive/tar/writer_test.go
+++ b/src/pkg/archive/tar/writer_test.go
@@ -9,6 +9,7 @@ import (
"fmt";
"io";
"testing";
+ "testing/iotest";
)
type writerTestEntry struct {
@@ -37,7 +38,7 @@ var writerTests = []*writerTest{
Uname: "dsymonds",
Gname: "eng",
},
- contents: `Kilts`,
+ contents: "Kilts",
},
&writerTestEntry{
header: &Header{
@@ -55,6 +56,28 @@ var writerTests = []*writerTest{
},
}
},
+ // The truncated test file was produced using these commands:
+ // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
+ // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
+ &writerTest{
+ file: "testdata/writer-big.tar",
+ entries: []*writerTestEntry{
+ &writerTestEntry{
+ header: &Header{
+ Name: "tmp/16gig.txt",
+ Mode: 0640,
+ Uid: 73025,
+ Gid: 5000,
+ Size: 16 << 30,
+ Mtime: 1254699560,
+ Typeflag: '0',
+ Uname: "dsymonds",
+ Gname: "eng",
+ },
+ // no contents
+ },
+ },
+ },
}
// Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
@@ -105,7 +128,7 @@ testLoop:
}
buf := new(bytes.Buffer);
- tw := NewWriter(buf);
+ tw := NewWriter(iotest.TruncateWriter(buf, 4 << 10)); // only catch the first 4 KB
for j, entry := range test.entries {
if err := tw.WriteHeader(entry.header); err != nil {
t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err);
@@ -116,7 +139,10 @@ testLoop:
continue testLoop
}
}
- tw.Close();
+ if err := tw.Close(); err != nil {
+ t.Errorf("test %d: Failed closing archive: %v", err);
+ continue testLoop
+ }
actual := buf.Bytes();
if !bytes.Equal(expected, actual) {