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
diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go
index 42e628f..745a7c4 100644
--- a/src/pkg/archive/tar/writer.go
+++ b/src/pkg/archive/tar/writer.go
@@ -16,9 +16,8 @@
)
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 @@
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) 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 @@
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 @@
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.