Commit ce51e107 authored by Andrew Gerrand's avatar Andrew Gerrand

archive/zip: use encoding/binary again, add readBuf helper

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5699097
parent 4fae9f79
...@@ -7,6 +7,7 @@ package zip ...@@ -7,6 +7,7 @@ package zip
import ( import (
"bufio" "bufio"
"compress/flate" "compress/flate"
"encoding/binary"
"errors" "errors"
"hash" "hash"
"hash/crc32" "hash/crc32"
...@@ -169,23 +170,24 @@ func (r *checksumReader) Read(b []byte) (n int, err error) { ...@@ -169,23 +170,24 @@ func (r *checksumReader) Read(b []byte) (n int, err error) {
func (r *checksumReader) Close() error { return r.rc.Close() } func (r *checksumReader) Close() error { return r.rc.Close() }
func readFileHeader(f *File, r io.Reader) error { func readFileHeader(f *File, r io.Reader) error {
var b [fileHeaderLen]byte var buf [fileHeaderLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, buf[:]); err != nil {
return err return err
} }
if sig := toUint32(b[:]); sig != fileHeaderSignature { b := readBuf(buf[:])
if sig := b.uint32(); sig != fileHeaderSignature {
return ErrFormat return ErrFormat
} }
f.ReaderVersion = toUint16(b[4:]) f.ReaderVersion = b.uint16()
f.Flags = toUint16(b[6:]) f.Flags = b.uint16()
f.Method = toUint16(b[8:]) f.Method = b.uint16()
f.ModifiedTime = toUint16(b[10:]) f.ModifiedTime = b.uint16()
f.ModifiedDate = toUint16(b[12:]) f.ModifiedDate = b.uint16()
f.CRC32 = toUint32(b[14:]) f.CRC32 = b.uint32()
f.CompressedSize = toUint32(b[18:]) f.CompressedSize = b.uint32()
f.UncompressedSize = toUint32(b[22:]) f.UncompressedSize = b.uint32()
filenameLen := int(toUint16(b[26:])) filenameLen := int(b.uint16())
extraLen := int(toUint16(b[28:])) extraLen := int(b.uint16())
d := make([]byte, filenameLen+extraLen) d := make([]byte, filenameLen+extraLen)
if _, err := io.ReadFull(r, d); err != nil { if _, err := io.ReadFull(r, d); err != nil {
return err return err
...@@ -199,15 +201,17 @@ func readFileHeader(f *File, r io.Reader) error { ...@@ -199,15 +201,17 @@ func readFileHeader(f *File, r io.Reader) error {
// and returns the file body offset. // and returns the file body offset.
func (f *File) findBodyOffset() (int64, error) { func (f *File) findBodyOffset() (int64, error) {
r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset) r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset)
var b [fileHeaderLen]byte var buf [fileHeaderLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, buf[:]); err != nil {
return 0, err return 0, err
} }
if sig := toUint32(b[:4]); sig != fileHeaderSignature { b := readBuf(buf[:])
if sig := b.uint32(); sig != fileHeaderSignature {
return 0, ErrFormat return 0, ErrFormat
} }
filenameLen := int(toUint16(b[26:28])) b = b[22:] // skip over most of the header
extraLen := int(toUint16(b[28:30])) filenameLen := int(b.uint16())
extraLen := int(b.uint16())
return int64(fileHeaderLen + filenameLen + extraLen), nil return int64(fileHeaderLen + filenameLen + extraLen), nil
} }
...@@ -215,28 +219,29 @@ func (f *File) findBodyOffset() (int64, error) { ...@@ -215,28 +219,29 @@ func (f *File) findBodyOffset() (int64, error) {
// It returns io.ErrUnexpectedEOF if it cannot read a complete header, // It returns io.ErrUnexpectedEOF if it cannot read a complete header,
// and ErrFormat if it doesn't find a valid header signature. // and ErrFormat if it doesn't find a valid header signature.
func readDirectoryHeader(f *File, r io.Reader) error { func readDirectoryHeader(f *File, r io.Reader) error {
var b [directoryHeaderLen]byte var buf [directoryHeaderLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, buf[:]); err != nil {
return err return err
} }
if sig := toUint32(b[:]); sig != directoryHeaderSignature { b := readBuf(buf[:])
if sig := b.uint32(); sig != directoryHeaderSignature {
return ErrFormat return ErrFormat
} }
f.CreatorVersion = toUint16(b[4:]) f.CreatorVersion = b.uint16()
f.ReaderVersion = toUint16(b[6:]) f.ReaderVersion = b.uint16()
f.Flags = toUint16(b[8:]) f.Flags = b.uint16()
f.Method = toUint16(b[10:]) f.Method = b.uint16()
f.ModifiedTime = toUint16(b[12:]) f.ModifiedTime = b.uint16()
f.ModifiedDate = toUint16(b[14:]) f.ModifiedDate = b.uint16()
f.CRC32 = toUint32(b[16:]) f.CRC32 = b.uint32()
f.CompressedSize = toUint32(b[20:]) f.CompressedSize = b.uint32()
f.UncompressedSize = toUint32(b[24:]) f.UncompressedSize = b.uint32()
filenameLen := int(toUint16(b[28:])) filenameLen := int(b.uint16())
extraLen := int(toUint16(b[30:32])) extraLen := int(b.uint16())
commentLen := int(toUint16(b[32:])) commentLen := int(b.uint16())
// skipped start disk number and internal attributes (2x uint16) b = b[4:] // skipped start disk number and internal attributes (2x uint16)
f.ExternalAttrs = toUint32(b[38:]) f.ExternalAttrs = b.uint32()
f.headerOffset = int64(toUint32(b[42:])) f.headerOffset = int64(b.uint32())
d := make([]byte, filenameLen+extraLen+commentLen) d := make([]byte, filenameLen+extraLen+commentLen)
if _, err := io.ReadFull(r, d); err != nil { if _, err := io.ReadFull(r, d); err != nil {
return err return err
...@@ -248,29 +253,30 @@ func readDirectoryHeader(f *File, r io.Reader) error { ...@@ -248,29 +253,30 @@ func readDirectoryHeader(f *File, r io.Reader) error {
} }
func readDataDescriptor(r io.Reader, f *File) error { func readDataDescriptor(r io.Reader, f *File) error {
var b [dataDescriptorLen]byte var buf [dataDescriptorLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, buf[:]); err != nil {
return err return err
} }
f.CRC32 = toUint32(b[:4]) b := readBuf(buf[:])
f.CompressedSize = toUint32(b[4:8]) f.CRC32 = b.uint32()
f.UncompressedSize = toUint32(b[8:12]) f.CompressedSize = b.uint32()
f.UncompressedSize = b.uint32()
return nil return nil
} }
func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
// look for directoryEndSignature in the last 1k, then in the last 65k // look for directoryEndSignature in the last 1k, then in the last 65k
var b []byte var buf []byte
for i, bLen := range []int64{1024, 65 * 1024} { for i, bLen := range []int64{1024, 65 * 1024} {
if bLen > size { if bLen > size {
bLen = size bLen = size
} }
b = make([]byte, int(bLen)) buf = make([]byte, int(bLen))
if _, err := r.ReadAt(b, size-bLen); err != nil && err != io.EOF { if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF {
return nil, err return nil, err
} }
if p := findSignatureInBlock(b); p >= 0 { if p := findSignatureInBlock(buf); p >= 0 {
b = b[p:] buf = buf[p:]
break break
} }
if i == 1 || bLen == size { if i == 1 || bLen == size {
...@@ -279,15 +285,21 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) ...@@ -279,15 +285,21 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
} }
// read header into struct // read header into struct
d := new(directoryEnd) b := readBuf(buf[4:]) // skip signature
d.diskNbr = toUint16(b[4:]) d := &directoryEnd{
d.dirDiskNbr = toUint16(b[6:]) diskNbr: b.uint16(),
d.dirRecordsThisDisk = toUint16(b[8:]) dirDiskNbr: b.uint16(),
d.directoryRecords = toUint16(b[10:]) dirRecordsThisDisk: b.uint16(),
d.directorySize = toUint32(b[12:]) directoryRecords: b.uint16(),
d.directoryOffset = toUint32(b[16:]) directorySize: b.uint32(),
d.commentLen = toUint16(b[20:]) directoryOffset: b.uint32(),
d.comment = string(b[22 : 22+int(d.commentLen)]) commentLen: b.uint16(),
}
l := int(d.commentLen)
if l > len(b) {
return nil, errors.New("zip: invalid comment length")
}
d.comment = string(b[:l])
return d, nil return d, nil
} }
...@@ -305,8 +317,16 @@ func findSignatureInBlock(b []byte) int { ...@@ -305,8 +317,16 @@ func findSignatureInBlock(b []byte) int {
return -1 return -1
} }
func toUint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } type readBuf []byte
func toUint32(b []byte) uint32 { func (b *readBuf) uint16() uint16 {
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 v := binary.LittleEndian.Uint16(*b)
*b = (*b)[2:]
return v
}
func (b *readBuf) uint32() uint32 {
v := binary.LittleEndian.Uint32(*b)
*b = (*b)[4:]
return v
} }
...@@ -7,6 +7,7 @@ package zip ...@@ -7,6 +7,7 @@ package zip
import ( import (
"bufio" "bufio"
"compress/flate" "compress/flate"
"encoding/binary"
"errors" "errors"
"hash" "hash"
"hash/crc32" "hash/crc32"
...@@ -249,21 +250,14 @@ func (w nopCloser) Close() error { ...@@ -249,21 +250,14 @@ func (w nopCloser) Close() error {
return nil return nil
} }
// We use this helper instead of encoding/binary's Write to avoid reflection.
// It's easy enough, anyway.
type writeBuf []byte type writeBuf []byte
func (b *writeBuf) uint16(v uint16) { func (b *writeBuf) uint16(v uint16) {
(*b)[0] = byte(v) binary.LittleEndian.PutUint16(*b, v)
(*b)[1] = byte(v >> 8)
*b = (*b)[2:] *b = (*b)[2:]
} }
func (b *writeBuf) uint32(v uint32) { func (b *writeBuf) uint32(v uint32) {
(*b)[0] = byte(v) binary.LittleEndian.PutUint32(*b, v)
(*b)[1] = byte(v >> 8)
(*b)[2] = byte(v >> 16)
(*b)[3] = byte(v >> 24)
*b = (*b)[4:] *b = (*b)[4:]
} }
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