1*333d2b36SAndroid Build Coastguard Worker// Copyright 2010 The Go Authors. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style 3*333d2b36SAndroid Build Coastguard Worker// license that can be found in the LICENSE file. 4*333d2b36SAndroid Build Coastguard Worker 5*333d2b36SAndroid Build Coastguard Workerpackage zip 6*333d2b36SAndroid Build Coastguard Worker 7*333d2b36SAndroid Build Coastguard Workerimport ( 8*333d2b36SAndroid Build Coastguard Worker "bufio" 9*333d2b36SAndroid Build Coastguard Worker "encoding/binary" 10*333d2b36SAndroid Build Coastguard Worker "errors" 11*333d2b36SAndroid Build Coastguard Worker "fmt" 12*333d2b36SAndroid Build Coastguard Worker "hash" 13*333d2b36SAndroid Build Coastguard Worker "hash/crc32" 14*333d2b36SAndroid Build Coastguard Worker "io" 15*333d2b36SAndroid Build Coastguard Worker "os" 16*333d2b36SAndroid Build Coastguard Worker) 17*333d2b36SAndroid Build Coastguard Worker 18*333d2b36SAndroid Build Coastguard Workervar ( 19*333d2b36SAndroid Build Coastguard Worker ErrFormat = errors.New("zip: not a valid zip file") 20*333d2b36SAndroid Build Coastguard Worker ErrAlgorithm = errors.New("zip: unsupported compression algorithm") 21*333d2b36SAndroid Build Coastguard Worker ErrChecksum = errors.New("zip: checksum error") 22*333d2b36SAndroid Build Coastguard Worker) 23*333d2b36SAndroid Build Coastguard Worker 24*333d2b36SAndroid Build Coastguard Workertype Reader struct { 25*333d2b36SAndroid Build Coastguard Worker r io.ReaderAt 26*333d2b36SAndroid Build Coastguard Worker File []*File 27*333d2b36SAndroid Build Coastguard Worker Comment string 28*333d2b36SAndroid Build Coastguard Worker decompressors map[uint16]Decompressor 29*333d2b36SAndroid Build Coastguard Worker} 30*333d2b36SAndroid Build Coastguard Worker 31*333d2b36SAndroid Build Coastguard Workertype ReadCloser struct { 32*333d2b36SAndroid Build Coastguard Worker f *os.File 33*333d2b36SAndroid Build Coastguard Worker Reader 34*333d2b36SAndroid Build Coastguard Worker} 35*333d2b36SAndroid Build Coastguard Worker 36*333d2b36SAndroid Build Coastguard Workertype File struct { 37*333d2b36SAndroid Build Coastguard Worker FileHeader 38*333d2b36SAndroid Build Coastguard Worker zip *Reader 39*333d2b36SAndroid Build Coastguard Worker zipr io.ReaderAt 40*333d2b36SAndroid Build Coastguard Worker zipsize int64 41*333d2b36SAndroid Build Coastguard Worker headerOffset int64 42*333d2b36SAndroid Build Coastguard Worker} 43*333d2b36SAndroid Build Coastguard Worker 44*333d2b36SAndroid Build Coastguard Workerfunc (f *File) hasDataDescriptor() bool { 45*333d2b36SAndroid Build Coastguard Worker return f.Flags&0x8 != 0 46*333d2b36SAndroid Build Coastguard Worker} 47*333d2b36SAndroid Build Coastguard Worker 48*333d2b36SAndroid Build Coastguard Worker// OpenReader will open the Zip file specified by name and return a ReadCloser. 49*333d2b36SAndroid Build Coastguard Workerfunc OpenReader(name string) (*ReadCloser, error) { 50*333d2b36SAndroid Build Coastguard Worker f, err := os.Open(name) 51*333d2b36SAndroid Build Coastguard Worker if err != nil { 52*333d2b36SAndroid Build Coastguard Worker return nil, err 53*333d2b36SAndroid Build Coastguard Worker } 54*333d2b36SAndroid Build Coastguard Worker fi, err := f.Stat() 55*333d2b36SAndroid Build Coastguard Worker if err != nil { 56*333d2b36SAndroid Build Coastguard Worker f.Close() 57*333d2b36SAndroid Build Coastguard Worker return nil, err 58*333d2b36SAndroid Build Coastguard Worker } 59*333d2b36SAndroid Build Coastguard Worker r := new(ReadCloser) 60*333d2b36SAndroid Build Coastguard Worker if err := r.init(f, fi.Size()); err != nil { 61*333d2b36SAndroid Build Coastguard Worker f.Close() 62*333d2b36SAndroid Build Coastguard Worker return nil, err 63*333d2b36SAndroid Build Coastguard Worker } 64*333d2b36SAndroid Build Coastguard Worker r.f = f 65*333d2b36SAndroid Build Coastguard Worker return r, nil 66*333d2b36SAndroid Build Coastguard Worker} 67*333d2b36SAndroid Build Coastguard Worker 68*333d2b36SAndroid Build Coastguard Worker// NewReader returns a new Reader reading from r, which is assumed to 69*333d2b36SAndroid Build Coastguard Worker// have the given size in bytes. 70*333d2b36SAndroid Build Coastguard Workerfunc NewReader(r io.ReaderAt, size int64) (*Reader, error) { 71*333d2b36SAndroid Build Coastguard Worker zr := new(Reader) 72*333d2b36SAndroid Build Coastguard Worker if err := zr.init(r, size); err != nil { 73*333d2b36SAndroid Build Coastguard Worker return nil, err 74*333d2b36SAndroid Build Coastguard Worker } 75*333d2b36SAndroid Build Coastguard Worker return zr, nil 76*333d2b36SAndroid Build Coastguard Worker} 77*333d2b36SAndroid Build Coastguard Worker 78*333d2b36SAndroid Build Coastguard Workerfunc (z *Reader) init(r io.ReaderAt, size int64) error { 79*333d2b36SAndroid Build Coastguard Worker end, err := readDirectoryEnd(r, size) 80*333d2b36SAndroid Build Coastguard Worker if err != nil { 81*333d2b36SAndroid Build Coastguard Worker return err 82*333d2b36SAndroid Build Coastguard Worker } 83*333d2b36SAndroid Build Coastguard Worker if end.directoryRecords > uint64(size)/fileHeaderLen { 84*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("archive/zip: TOC declares impossible %d files in %d byte zip", end.directoryRecords, size) 85*333d2b36SAndroid Build Coastguard Worker } 86*333d2b36SAndroid Build Coastguard Worker z.r = r 87*333d2b36SAndroid Build Coastguard Worker z.File = make([]*File, 0, end.directoryRecords) 88*333d2b36SAndroid Build Coastguard Worker z.Comment = end.comment 89*333d2b36SAndroid Build Coastguard Worker rs := io.NewSectionReader(r, 0, size) 90*333d2b36SAndroid Build Coastguard Worker if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil { 91*333d2b36SAndroid Build Coastguard Worker return err 92*333d2b36SAndroid Build Coastguard Worker } 93*333d2b36SAndroid Build Coastguard Worker buf := bufio.NewReader(rs) 94*333d2b36SAndroid Build Coastguard Worker 95*333d2b36SAndroid Build Coastguard Worker // The count of files inside a zip is truncated to fit in a uint16. 96*333d2b36SAndroid Build Coastguard Worker // Gloss over this by reading headers until we encounter 97*333d2b36SAndroid Build Coastguard Worker // a bad one, and then only report a ErrFormat or UnexpectedEOF if 98*333d2b36SAndroid Build Coastguard Worker // the file count modulo 65536 is incorrect. 99*333d2b36SAndroid Build Coastguard Worker for { 100*333d2b36SAndroid Build Coastguard Worker f := &File{zip: z, zipr: r, zipsize: size} 101*333d2b36SAndroid Build Coastguard Worker err = readDirectoryHeader(f, buf) 102*333d2b36SAndroid Build Coastguard Worker if err == ErrFormat || err == io.ErrUnexpectedEOF { 103*333d2b36SAndroid Build Coastguard Worker break 104*333d2b36SAndroid Build Coastguard Worker } 105*333d2b36SAndroid Build Coastguard Worker if err != nil { 106*333d2b36SAndroid Build Coastguard Worker return err 107*333d2b36SAndroid Build Coastguard Worker } 108*333d2b36SAndroid Build Coastguard Worker z.File = append(z.File, f) 109*333d2b36SAndroid Build Coastguard Worker } 110*333d2b36SAndroid Build Coastguard Worker if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here 111*333d2b36SAndroid Build Coastguard Worker // Return the readDirectoryHeader error if we read 112*333d2b36SAndroid Build Coastguard Worker // the wrong number of directory entries. 113*333d2b36SAndroid Build Coastguard Worker return err 114*333d2b36SAndroid Build Coastguard Worker } 115*333d2b36SAndroid Build Coastguard Worker return nil 116*333d2b36SAndroid Build Coastguard Worker} 117*333d2b36SAndroid Build Coastguard Worker 118*333d2b36SAndroid Build Coastguard Worker// RegisterDecompressor registers or overrides a custom decompressor for a 119*333d2b36SAndroid Build Coastguard Worker// specific method ID. If a decompressor for a given method is not found, 120*333d2b36SAndroid Build Coastguard Worker// Reader will default to looking up the decompressor at the package level. 121*333d2b36SAndroid Build Coastguard Workerfunc (z *Reader) RegisterDecompressor(method uint16, dcomp Decompressor) { 122*333d2b36SAndroid Build Coastguard Worker if z.decompressors == nil { 123*333d2b36SAndroid Build Coastguard Worker z.decompressors = make(map[uint16]Decompressor) 124*333d2b36SAndroid Build Coastguard Worker } 125*333d2b36SAndroid Build Coastguard Worker z.decompressors[method] = dcomp 126*333d2b36SAndroid Build Coastguard Worker} 127*333d2b36SAndroid Build Coastguard Worker 128*333d2b36SAndroid Build Coastguard Workerfunc (z *Reader) decompressor(method uint16) Decompressor { 129*333d2b36SAndroid Build Coastguard Worker dcomp := z.decompressors[method] 130*333d2b36SAndroid Build Coastguard Worker if dcomp == nil { 131*333d2b36SAndroid Build Coastguard Worker dcomp = decompressor(method) 132*333d2b36SAndroid Build Coastguard Worker } 133*333d2b36SAndroid Build Coastguard Worker return dcomp 134*333d2b36SAndroid Build Coastguard Worker} 135*333d2b36SAndroid Build Coastguard Worker 136*333d2b36SAndroid Build Coastguard Worker// Close closes the Zip file, rendering it unusable for I/O. 137*333d2b36SAndroid Build Coastguard Workerfunc (rc *ReadCloser) Close() error { 138*333d2b36SAndroid Build Coastguard Worker return rc.f.Close() 139*333d2b36SAndroid Build Coastguard Worker} 140*333d2b36SAndroid Build Coastguard Worker 141*333d2b36SAndroid Build Coastguard Worker// DataOffset returns the offset of the file's possibly-compressed 142*333d2b36SAndroid Build Coastguard Worker// data, relative to the beginning of the zip file. 143*333d2b36SAndroid Build Coastguard Worker// 144*333d2b36SAndroid Build Coastguard Worker// Most callers should instead use Open, which transparently 145*333d2b36SAndroid Build Coastguard Worker// decompresses data and verifies checksums. 146*333d2b36SAndroid Build Coastguard Workerfunc (f *File) DataOffset() (offset int64, err error) { 147*333d2b36SAndroid Build Coastguard Worker bodyOffset, err := f.findBodyOffset() 148*333d2b36SAndroid Build Coastguard Worker if err != nil { 149*333d2b36SAndroid Build Coastguard Worker return 150*333d2b36SAndroid Build Coastguard Worker } 151*333d2b36SAndroid Build Coastguard Worker return f.headerOffset + bodyOffset, nil 152*333d2b36SAndroid Build Coastguard Worker} 153*333d2b36SAndroid Build Coastguard Worker 154*333d2b36SAndroid Build Coastguard Worker// Open returns a ReadCloser that provides access to the File's contents. 155*333d2b36SAndroid Build Coastguard Worker// Multiple files may be read concurrently. 156*333d2b36SAndroid Build Coastguard Workerfunc (f *File) Open() (io.ReadCloser, error) { 157*333d2b36SAndroid Build Coastguard Worker bodyOffset, err := f.findBodyOffset() 158*333d2b36SAndroid Build Coastguard Worker if err != nil { 159*333d2b36SAndroid Build Coastguard Worker return nil, err 160*333d2b36SAndroid Build Coastguard Worker } 161*333d2b36SAndroid Build Coastguard Worker size := int64(f.CompressedSize64) 162*333d2b36SAndroid Build Coastguard Worker r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) 163*333d2b36SAndroid Build Coastguard Worker dcomp := f.zip.decompressor(f.Method) 164*333d2b36SAndroid Build Coastguard Worker if dcomp == nil { 165*333d2b36SAndroid Build Coastguard Worker return nil, ErrAlgorithm 166*333d2b36SAndroid Build Coastguard Worker } 167*333d2b36SAndroid Build Coastguard Worker var rc io.ReadCloser = dcomp(r) 168*333d2b36SAndroid Build Coastguard Worker var desr io.Reader 169*333d2b36SAndroid Build Coastguard Worker if f.hasDataDescriptor() { 170*333d2b36SAndroid Build Coastguard Worker desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) 171*333d2b36SAndroid Build Coastguard Worker } 172*333d2b36SAndroid Build Coastguard Worker rc = &checksumReader{ 173*333d2b36SAndroid Build Coastguard Worker rc: rc, 174*333d2b36SAndroid Build Coastguard Worker hash: crc32.NewIEEE(), 175*333d2b36SAndroid Build Coastguard Worker f: f, 176*333d2b36SAndroid Build Coastguard Worker desr: desr, 177*333d2b36SAndroid Build Coastguard Worker } 178*333d2b36SAndroid Build Coastguard Worker return rc, nil 179*333d2b36SAndroid Build Coastguard Worker} 180*333d2b36SAndroid Build Coastguard Worker 181*333d2b36SAndroid Build Coastguard Workertype checksumReader struct { 182*333d2b36SAndroid Build Coastguard Worker rc io.ReadCloser 183*333d2b36SAndroid Build Coastguard Worker hash hash.Hash32 184*333d2b36SAndroid Build Coastguard Worker nread uint64 // number of bytes read so far 185*333d2b36SAndroid Build Coastguard Worker f *File 186*333d2b36SAndroid Build Coastguard Worker desr io.Reader // if non-nil, where to read the data descriptor 187*333d2b36SAndroid Build Coastguard Worker err error // sticky error 188*333d2b36SAndroid Build Coastguard Worker} 189*333d2b36SAndroid Build Coastguard Worker 190*333d2b36SAndroid Build Coastguard Workerfunc (r *checksumReader) Read(b []byte) (n int, err error) { 191*333d2b36SAndroid Build Coastguard Worker if r.err != nil { 192*333d2b36SAndroid Build Coastguard Worker return 0, r.err 193*333d2b36SAndroid Build Coastguard Worker } 194*333d2b36SAndroid Build Coastguard Worker n, err = r.rc.Read(b) 195*333d2b36SAndroid Build Coastguard Worker r.hash.Write(b[:n]) 196*333d2b36SAndroid Build Coastguard Worker r.nread += uint64(n) 197*333d2b36SAndroid Build Coastguard Worker if err == nil { 198*333d2b36SAndroid Build Coastguard Worker return 199*333d2b36SAndroid Build Coastguard Worker } 200*333d2b36SAndroid Build Coastguard Worker if err == io.EOF { 201*333d2b36SAndroid Build Coastguard Worker if r.nread != r.f.UncompressedSize64 { 202*333d2b36SAndroid Build Coastguard Worker return 0, io.ErrUnexpectedEOF 203*333d2b36SAndroid Build Coastguard Worker } 204*333d2b36SAndroid Build Coastguard Worker if r.desr != nil { 205*333d2b36SAndroid Build Coastguard Worker if err1 := readDataDescriptor(r.desr, r.f); err1 != nil { 206*333d2b36SAndroid Build Coastguard Worker if err1 == io.EOF { 207*333d2b36SAndroid Build Coastguard Worker err = io.ErrUnexpectedEOF 208*333d2b36SAndroid Build Coastguard Worker } else { 209*333d2b36SAndroid Build Coastguard Worker err = err1 210*333d2b36SAndroid Build Coastguard Worker } 211*333d2b36SAndroid Build Coastguard Worker } else if r.hash.Sum32() != r.f.CRC32 { 212*333d2b36SAndroid Build Coastguard Worker err = ErrChecksum 213*333d2b36SAndroid Build Coastguard Worker } 214*333d2b36SAndroid Build Coastguard Worker } else { 215*333d2b36SAndroid Build Coastguard Worker // If there's not a data descriptor, we still compare 216*333d2b36SAndroid Build Coastguard Worker // the CRC32 of what we've read against the file header 217*333d2b36SAndroid Build Coastguard Worker // or TOC's CRC32, if it seems like it was set. 218*333d2b36SAndroid Build Coastguard Worker if r.f.CRC32 != 0 && r.hash.Sum32() != r.f.CRC32 { 219*333d2b36SAndroid Build Coastguard Worker err = ErrChecksum 220*333d2b36SAndroid Build Coastguard Worker } 221*333d2b36SAndroid Build Coastguard Worker } 222*333d2b36SAndroid Build Coastguard Worker } 223*333d2b36SAndroid Build Coastguard Worker r.err = err 224*333d2b36SAndroid Build Coastguard Worker return 225*333d2b36SAndroid Build Coastguard Worker} 226*333d2b36SAndroid Build Coastguard Worker 227*333d2b36SAndroid Build Coastguard Workerfunc (r *checksumReader) Close() error { return r.rc.Close() } 228*333d2b36SAndroid Build Coastguard Worker 229*333d2b36SAndroid Build Coastguard Worker// findBodyOffset does the minimum work to verify the file has a header 230*333d2b36SAndroid Build Coastguard Worker// and returns the file body offset. 231*333d2b36SAndroid Build Coastguard Workerfunc (f *File) findBodyOffset() (int64, error) { 232*333d2b36SAndroid Build Coastguard Worker var buf [fileHeaderLen]byte 233*333d2b36SAndroid Build Coastguard Worker if _, err := f.zipr.ReadAt(buf[:], f.headerOffset); err != nil { 234*333d2b36SAndroid Build Coastguard Worker return 0, err 235*333d2b36SAndroid Build Coastguard Worker } 236*333d2b36SAndroid Build Coastguard Worker b := readBuf(buf[:]) 237*333d2b36SAndroid Build Coastguard Worker if sig := b.uint32(); sig != fileHeaderSignature { 238*333d2b36SAndroid Build Coastguard Worker return 0, ErrFormat 239*333d2b36SAndroid Build Coastguard Worker } 240*333d2b36SAndroid Build Coastguard Worker b = b[22:] // skip over most of the header 241*333d2b36SAndroid Build Coastguard Worker filenameLen := int(b.uint16()) 242*333d2b36SAndroid Build Coastguard Worker extraLen := int(b.uint16()) 243*333d2b36SAndroid Build Coastguard Worker return int64(fileHeaderLen + filenameLen + extraLen), nil 244*333d2b36SAndroid Build Coastguard Worker} 245*333d2b36SAndroid Build Coastguard Worker 246*333d2b36SAndroid Build Coastguard Worker// readDirectoryHeader attempts to read a directory header from r. 247*333d2b36SAndroid Build Coastguard Worker// It returns io.ErrUnexpectedEOF if it cannot read a complete header, 248*333d2b36SAndroid Build Coastguard Worker// and ErrFormat if it doesn't find a valid header signature. 249*333d2b36SAndroid Build Coastguard Workerfunc readDirectoryHeader(f *File, r io.Reader) error { 250*333d2b36SAndroid Build Coastguard Worker var buf [directoryHeaderLen]byte 251*333d2b36SAndroid Build Coastguard Worker if _, err := io.ReadFull(r, buf[:]); err != nil { 252*333d2b36SAndroid Build Coastguard Worker return err 253*333d2b36SAndroid Build Coastguard Worker } 254*333d2b36SAndroid Build Coastguard Worker b := readBuf(buf[:]) 255*333d2b36SAndroid Build Coastguard Worker if sig := b.uint32(); sig != directoryHeaderSignature { 256*333d2b36SAndroid Build Coastguard Worker return ErrFormat 257*333d2b36SAndroid Build Coastguard Worker } 258*333d2b36SAndroid Build Coastguard Worker f.CreatorVersion = b.uint16() 259*333d2b36SAndroid Build Coastguard Worker f.ReaderVersion = b.uint16() 260*333d2b36SAndroid Build Coastguard Worker f.Flags = b.uint16() 261*333d2b36SAndroid Build Coastguard Worker f.Method = b.uint16() 262*333d2b36SAndroid Build Coastguard Worker f.ModifiedTime = b.uint16() 263*333d2b36SAndroid Build Coastguard Worker f.ModifiedDate = b.uint16() 264*333d2b36SAndroid Build Coastguard Worker f.CRC32 = b.uint32() 265*333d2b36SAndroid Build Coastguard Worker f.CompressedSize = b.uint32() 266*333d2b36SAndroid Build Coastguard Worker f.UncompressedSize = b.uint32() 267*333d2b36SAndroid Build Coastguard Worker f.CompressedSize64 = uint64(f.CompressedSize) 268*333d2b36SAndroid Build Coastguard Worker f.UncompressedSize64 = uint64(f.UncompressedSize) 269*333d2b36SAndroid Build Coastguard Worker filenameLen := int(b.uint16()) 270*333d2b36SAndroid Build Coastguard Worker extraLen := int(b.uint16()) 271*333d2b36SAndroid Build Coastguard Worker commentLen := int(b.uint16()) 272*333d2b36SAndroid Build Coastguard Worker b = b[4:] // skipped start disk number and internal attributes (2x uint16) 273*333d2b36SAndroid Build Coastguard Worker f.ExternalAttrs = b.uint32() 274*333d2b36SAndroid Build Coastguard Worker f.headerOffset = int64(b.uint32()) 275*333d2b36SAndroid Build Coastguard Worker d := make([]byte, filenameLen+extraLen+commentLen) 276*333d2b36SAndroid Build Coastguard Worker if _, err := io.ReadFull(r, d); err != nil { 277*333d2b36SAndroid Build Coastguard Worker return err 278*333d2b36SAndroid Build Coastguard Worker } 279*333d2b36SAndroid Build Coastguard Worker f.Name = string(d[:filenameLen]) 280*333d2b36SAndroid Build Coastguard Worker f.Extra = d[filenameLen : filenameLen+extraLen] 281*333d2b36SAndroid Build Coastguard Worker f.Comment = string(d[filenameLen+extraLen:]) 282*333d2b36SAndroid Build Coastguard Worker 283*333d2b36SAndroid Build Coastguard Worker needUSize := f.UncompressedSize == ^uint32(0) 284*333d2b36SAndroid Build Coastguard Worker needCSize := f.CompressedSize == ^uint32(0) 285*333d2b36SAndroid Build Coastguard Worker needHeaderOffset := f.headerOffset == int64(^uint32(0)) 286*333d2b36SAndroid Build Coastguard Worker 287*333d2b36SAndroid Build Coastguard Worker if len(f.Extra) > 0 { 288*333d2b36SAndroid Build Coastguard Worker // Best effort to find what we need. 289*333d2b36SAndroid Build Coastguard Worker // Other zip authors might not even follow the basic format, 290*333d2b36SAndroid Build Coastguard Worker // and we'll just ignore the Extra content in that case. 291*333d2b36SAndroid Build Coastguard Worker b := readBuf(f.Extra) 292*333d2b36SAndroid Build Coastguard Worker for len(b) >= 4 { // need at least tag and size 293*333d2b36SAndroid Build Coastguard Worker tag := b.uint16() 294*333d2b36SAndroid Build Coastguard Worker size := b.uint16() 295*333d2b36SAndroid Build Coastguard Worker if int(size) > len(b) { 296*333d2b36SAndroid Build Coastguard Worker break 297*333d2b36SAndroid Build Coastguard Worker } 298*333d2b36SAndroid Build Coastguard Worker if tag == zip64ExtraId { 299*333d2b36SAndroid Build Coastguard Worker // update directory values from the zip64 extra block. 300*333d2b36SAndroid Build Coastguard Worker // They should only be consulted if the sizes read earlier 301*333d2b36SAndroid Build Coastguard Worker // are maxed out. 302*333d2b36SAndroid Build Coastguard Worker // See golang.org/issue/13367. 303*333d2b36SAndroid Build Coastguard Worker eb := readBuf(b[:size]) 304*333d2b36SAndroid Build Coastguard Worker 305*333d2b36SAndroid Build Coastguard Worker if needUSize { 306*333d2b36SAndroid Build Coastguard Worker needUSize = false 307*333d2b36SAndroid Build Coastguard Worker if len(eb) < 8 { 308*333d2b36SAndroid Build Coastguard Worker return ErrFormat 309*333d2b36SAndroid Build Coastguard Worker } 310*333d2b36SAndroid Build Coastguard Worker f.UncompressedSize64 = eb.uint64() 311*333d2b36SAndroid Build Coastguard Worker } 312*333d2b36SAndroid Build Coastguard Worker if needCSize { 313*333d2b36SAndroid Build Coastguard Worker needCSize = false 314*333d2b36SAndroid Build Coastguard Worker if len(eb) < 8 { 315*333d2b36SAndroid Build Coastguard Worker return ErrFormat 316*333d2b36SAndroid Build Coastguard Worker } 317*333d2b36SAndroid Build Coastguard Worker f.CompressedSize64 = eb.uint64() 318*333d2b36SAndroid Build Coastguard Worker } 319*333d2b36SAndroid Build Coastguard Worker if needHeaderOffset { 320*333d2b36SAndroid Build Coastguard Worker needHeaderOffset = false 321*333d2b36SAndroid Build Coastguard Worker if len(eb) < 8 { 322*333d2b36SAndroid Build Coastguard Worker return ErrFormat 323*333d2b36SAndroid Build Coastguard Worker } 324*333d2b36SAndroid Build Coastguard Worker f.headerOffset = int64(eb.uint64()) 325*333d2b36SAndroid Build Coastguard Worker } 326*333d2b36SAndroid Build Coastguard Worker break 327*333d2b36SAndroid Build Coastguard Worker } 328*333d2b36SAndroid Build Coastguard Worker b = b[size:] 329*333d2b36SAndroid Build Coastguard Worker } 330*333d2b36SAndroid Build Coastguard Worker } 331*333d2b36SAndroid Build Coastguard Worker 332*333d2b36SAndroid Build Coastguard Worker // Assume that uncompressed size 2³²-1 could plausibly happen in 333*333d2b36SAndroid Build Coastguard Worker // an old zip32 file that was sharding inputs into the largest chunks 334*333d2b36SAndroid Build Coastguard Worker // possible (or is just malicious; search the web for 42.zip). 335*333d2b36SAndroid Build Coastguard Worker // If needUSize is true still, it means we didn't see a zip64 extension. 336*333d2b36SAndroid Build Coastguard Worker // As long as the compressed size is not also 2³²-1 (implausible) 337*333d2b36SAndroid Build Coastguard Worker // and the header is not also 2³²-1 (equally implausible), 338*333d2b36SAndroid Build Coastguard Worker // accept the uncompressed size 2³²-1 as valid. 339*333d2b36SAndroid Build Coastguard Worker // If nothing else, this keeps archive/zip working with 42.zip. 340*333d2b36SAndroid Build Coastguard Worker _ = needUSize 341*333d2b36SAndroid Build Coastguard Worker 342*333d2b36SAndroid Build Coastguard Worker if needCSize || needHeaderOffset { 343*333d2b36SAndroid Build Coastguard Worker return ErrFormat 344*333d2b36SAndroid Build Coastguard Worker } 345*333d2b36SAndroid Build Coastguard Worker 346*333d2b36SAndroid Build Coastguard Worker return nil 347*333d2b36SAndroid Build Coastguard Worker} 348*333d2b36SAndroid Build Coastguard Worker 349*333d2b36SAndroid Build Coastguard Workerfunc readDataDescriptor(r io.Reader, f *File) error { 350*333d2b36SAndroid Build Coastguard Worker var buf [dataDescriptorLen]byte 351*333d2b36SAndroid Build Coastguard Worker 352*333d2b36SAndroid Build Coastguard Worker // The spec says: "Although not originally assigned a 353*333d2b36SAndroid Build Coastguard Worker // signature, the value 0x08074b50 has commonly been adopted 354*333d2b36SAndroid Build Coastguard Worker // as a signature value for the data descriptor record. 355*333d2b36SAndroid Build Coastguard Worker // Implementers should be aware that ZIP files may be 356*333d2b36SAndroid Build Coastguard Worker // encountered with or without this signature marking data 357*333d2b36SAndroid Build Coastguard Worker // descriptors and should account for either case when reading 358*333d2b36SAndroid Build Coastguard Worker // ZIP files to ensure compatibility." 359*333d2b36SAndroid Build Coastguard Worker // 360*333d2b36SAndroid Build Coastguard Worker // dataDescriptorLen includes the size of the signature but 361*333d2b36SAndroid Build Coastguard Worker // first read just those 4 bytes to see if it exists. 362*333d2b36SAndroid Build Coastguard Worker if _, err := io.ReadFull(r, buf[:4]); err != nil { 363*333d2b36SAndroid Build Coastguard Worker return err 364*333d2b36SAndroid Build Coastguard Worker } 365*333d2b36SAndroid Build Coastguard Worker off := 0 366*333d2b36SAndroid Build Coastguard Worker maybeSig := readBuf(buf[:4]) 367*333d2b36SAndroid Build Coastguard Worker if maybeSig.uint32() != dataDescriptorSignature { 368*333d2b36SAndroid Build Coastguard Worker // No data descriptor signature. Keep these four 369*333d2b36SAndroid Build Coastguard Worker // bytes. 370*333d2b36SAndroid Build Coastguard Worker off += 4 371*333d2b36SAndroid Build Coastguard Worker } 372*333d2b36SAndroid Build Coastguard Worker if _, err := io.ReadFull(r, buf[off:12]); err != nil { 373*333d2b36SAndroid Build Coastguard Worker return err 374*333d2b36SAndroid Build Coastguard Worker } 375*333d2b36SAndroid Build Coastguard Worker b := readBuf(buf[:12]) 376*333d2b36SAndroid Build Coastguard Worker if b.uint32() != f.CRC32 { 377*333d2b36SAndroid Build Coastguard Worker return ErrChecksum 378*333d2b36SAndroid Build Coastguard Worker } 379*333d2b36SAndroid Build Coastguard Worker 380*333d2b36SAndroid Build Coastguard Worker // The two sizes that follow here can be either 32 bits or 64 bits 381*333d2b36SAndroid Build Coastguard Worker // but the spec is not very clear on this and different 382*333d2b36SAndroid Build Coastguard Worker // interpretations has been made causing incompatibilities. We 383*333d2b36SAndroid Build Coastguard Worker // already have the sizes from the central directory so we can 384*333d2b36SAndroid Build Coastguard Worker // just ignore these. 385*333d2b36SAndroid Build Coastguard Worker 386*333d2b36SAndroid Build Coastguard Worker return nil 387*333d2b36SAndroid Build Coastguard Worker} 388*333d2b36SAndroid Build Coastguard Worker 389*333d2b36SAndroid Build Coastguard Workerfunc readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { 390*333d2b36SAndroid Build Coastguard Worker // look for directoryEndSignature in the last 1k, then in the last 65k 391*333d2b36SAndroid Build Coastguard Worker var buf []byte 392*333d2b36SAndroid Build Coastguard Worker var directoryEndOffset int64 393*333d2b36SAndroid Build Coastguard Worker for i, bLen := range []int64{1024, 65 * 1024} { 394*333d2b36SAndroid Build Coastguard Worker if bLen > size { 395*333d2b36SAndroid Build Coastguard Worker bLen = size 396*333d2b36SAndroid Build Coastguard Worker } 397*333d2b36SAndroid Build Coastguard Worker buf = make([]byte, int(bLen)) 398*333d2b36SAndroid Build Coastguard Worker if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF { 399*333d2b36SAndroid Build Coastguard Worker return nil, err 400*333d2b36SAndroid Build Coastguard Worker } 401*333d2b36SAndroid Build Coastguard Worker if p := findSignatureInBlock(buf); p >= 0 { 402*333d2b36SAndroid Build Coastguard Worker buf = buf[p:] 403*333d2b36SAndroid Build Coastguard Worker directoryEndOffset = size - bLen + int64(p) 404*333d2b36SAndroid Build Coastguard Worker break 405*333d2b36SAndroid Build Coastguard Worker } 406*333d2b36SAndroid Build Coastguard Worker if i == 1 || bLen == size { 407*333d2b36SAndroid Build Coastguard Worker return nil, ErrFormat 408*333d2b36SAndroid Build Coastguard Worker } 409*333d2b36SAndroid Build Coastguard Worker } 410*333d2b36SAndroid Build Coastguard Worker 411*333d2b36SAndroid Build Coastguard Worker // read header into struct 412*333d2b36SAndroid Build Coastguard Worker b := readBuf(buf[4:]) // skip signature 413*333d2b36SAndroid Build Coastguard Worker d := &directoryEnd{ 414*333d2b36SAndroid Build Coastguard Worker diskNbr: uint32(b.uint16()), 415*333d2b36SAndroid Build Coastguard Worker dirDiskNbr: uint32(b.uint16()), 416*333d2b36SAndroid Build Coastguard Worker dirRecordsThisDisk: uint64(b.uint16()), 417*333d2b36SAndroid Build Coastguard Worker directoryRecords: uint64(b.uint16()), 418*333d2b36SAndroid Build Coastguard Worker directorySize: uint64(b.uint32()), 419*333d2b36SAndroid Build Coastguard Worker directoryOffset: uint64(b.uint32()), 420*333d2b36SAndroid Build Coastguard Worker commentLen: b.uint16(), 421*333d2b36SAndroid Build Coastguard Worker } 422*333d2b36SAndroid Build Coastguard Worker l := int(d.commentLen) 423*333d2b36SAndroid Build Coastguard Worker if l > len(b) { 424*333d2b36SAndroid Build Coastguard Worker return nil, errors.New("zip: invalid comment length") 425*333d2b36SAndroid Build Coastguard Worker } 426*333d2b36SAndroid Build Coastguard Worker d.comment = string(b[:l]) 427*333d2b36SAndroid Build Coastguard Worker 428*333d2b36SAndroid Build Coastguard Worker // These values mean that the file can be a zip64 file 429*333d2b36SAndroid Build Coastguard Worker if d.directoryRecords == 0xffff || d.directorySize == 0xffff || d.directoryOffset == 0xffffffff { 430*333d2b36SAndroid Build Coastguard Worker p, err := findDirectory64End(r, directoryEndOffset) 431*333d2b36SAndroid Build Coastguard Worker if err == nil && p >= 0 { 432*333d2b36SAndroid Build Coastguard Worker err = readDirectory64End(r, p, d) 433*333d2b36SAndroid Build Coastguard Worker } 434*333d2b36SAndroid Build Coastguard Worker if err != nil { 435*333d2b36SAndroid Build Coastguard Worker return nil, err 436*333d2b36SAndroid Build Coastguard Worker } 437*333d2b36SAndroid Build Coastguard Worker } 438*333d2b36SAndroid Build Coastguard Worker // Make sure directoryOffset points to somewhere in our file. 439*333d2b36SAndroid Build Coastguard Worker if o := int64(d.directoryOffset); o < 0 || o >= size { 440*333d2b36SAndroid Build Coastguard Worker return nil, ErrFormat 441*333d2b36SAndroid Build Coastguard Worker } 442*333d2b36SAndroid Build Coastguard Worker return d, nil 443*333d2b36SAndroid Build Coastguard Worker} 444*333d2b36SAndroid Build Coastguard Worker 445*333d2b36SAndroid Build Coastguard Worker// findDirectory64End tries to read the zip64 locator just before the 446*333d2b36SAndroid Build Coastguard Worker// directory end and returns the offset of the zip64 directory end if 447*333d2b36SAndroid Build Coastguard Worker// found. 448*333d2b36SAndroid Build Coastguard Workerfunc findDirectory64End(r io.ReaderAt, directoryEndOffset int64) (int64, error) { 449*333d2b36SAndroid Build Coastguard Worker locOffset := directoryEndOffset - directory64LocLen 450*333d2b36SAndroid Build Coastguard Worker if locOffset < 0 { 451*333d2b36SAndroid Build Coastguard Worker return -1, nil // no need to look for a header outside the file 452*333d2b36SAndroid Build Coastguard Worker } 453*333d2b36SAndroid Build Coastguard Worker buf := make([]byte, directory64LocLen) 454*333d2b36SAndroid Build Coastguard Worker if _, err := r.ReadAt(buf, locOffset); err != nil { 455*333d2b36SAndroid Build Coastguard Worker return -1, err 456*333d2b36SAndroid Build Coastguard Worker } 457*333d2b36SAndroid Build Coastguard Worker b := readBuf(buf) 458*333d2b36SAndroid Build Coastguard Worker if sig := b.uint32(); sig != directory64LocSignature { 459*333d2b36SAndroid Build Coastguard Worker return -1, nil 460*333d2b36SAndroid Build Coastguard Worker } 461*333d2b36SAndroid Build Coastguard Worker if b.uint32() != 0 { // number of the disk with the start of the zip64 end of central directory 462*333d2b36SAndroid Build Coastguard Worker return -1, nil // the file is not a valid zip64-file 463*333d2b36SAndroid Build Coastguard Worker } 464*333d2b36SAndroid Build Coastguard Worker p := b.uint64() // relative offset of the zip64 end of central directory record 465*333d2b36SAndroid Build Coastguard Worker if b.uint32() != 1 { // total number of disks 466*333d2b36SAndroid Build Coastguard Worker return -1, nil // the file is not a valid zip64-file 467*333d2b36SAndroid Build Coastguard Worker } 468*333d2b36SAndroid Build Coastguard Worker return int64(p), nil 469*333d2b36SAndroid Build Coastguard Worker} 470*333d2b36SAndroid Build Coastguard Worker 471*333d2b36SAndroid Build Coastguard Worker// readDirectory64End reads the zip64 directory end and updates the 472*333d2b36SAndroid Build Coastguard Worker// directory end with the zip64 directory end values. 473*333d2b36SAndroid Build Coastguard Workerfunc readDirectory64End(r io.ReaderAt, offset int64, d *directoryEnd) (err error) { 474*333d2b36SAndroid Build Coastguard Worker buf := make([]byte, directory64EndLen) 475*333d2b36SAndroid Build Coastguard Worker if _, err := r.ReadAt(buf, offset); err != nil { 476*333d2b36SAndroid Build Coastguard Worker return err 477*333d2b36SAndroid Build Coastguard Worker } 478*333d2b36SAndroid Build Coastguard Worker 479*333d2b36SAndroid Build Coastguard Worker b := readBuf(buf) 480*333d2b36SAndroid Build Coastguard Worker if sig := b.uint32(); sig != directory64EndSignature { 481*333d2b36SAndroid Build Coastguard Worker return ErrFormat 482*333d2b36SAndroid Build Coastguard Worker } 483*333d2b36SAndroid Build Coastguard Worker 484*333d2b36SAndroid Build Coastguard Worker b = b[12:] // skip dir size, version and version needed (uint64 + 2x uint16) 485*333d2b36SAndroid Build Coastguard Worker d.diskNbr = b.uint32() // number of this disk 486*333d2b36SAndroid Build Coastguard Worker d.dirDiskNbr = b.uint32() // number of the disk with the start of the central directory 487*333d2b36SAndroid Build Coastguard Worker d.dirRecordsThisDisk = b.uint64() // total number of entries in the central directory on this disk 488*333d2b36SAndroid Build Coastguard Worker d.directoryRecords = b.uint64() // total number of entries in the central directory 489*333d2b36SAndroid Build Coastguard Worker d.directorySize = b.uint64() // size of the central directory 490*333d2b36SAndroid Build Coastguard Worker d.directoryOffset = b.uint64() // offset of start of central directory with respect to the starting disk number 491*333d2b36SAndroid Build Coastguard Worker 492*333d2b36SAndroid Build Coastguard Worker return nil 493*333d2b36SAndroid Build Coastguard Worker} 494*333d2b36SAndroid Build Coastguard Worker 495*333d2b36SAndroid Build Coastguard Workerfunc findSignatureInBlock(b []byte) int { 496*333d2b36SAndroid Build Coastguard Worker for i := len(b) - directoryEndLen; i >= 0; i-- { 497*333d2b36SAndroid Build Coastguard Worker // defined from directoryEndSignature in struct.go 498*333d2b36SAndroid Build Coastguard Worker if b[i] == 'P' && b[i+1] == 'K' && b[i+2] == 0x05 && b[i+3] == 0x06 { 499*333d2b36SAndroid Build Coastguard Worker // n is length of comment 500*333d2b36SAndroid Build Coastguard Worker n := int(b[i+directoryEndLen-2]) | int(b[i+directoryEndLen-1])<<8 501*333d2b36SAndroid Build Coastguard Worker if n+directoryEndLen+i <= len(b) { 502*333d2b36SAndroid Build Coastguard Worker return i 503*333d2b36SAndroid Build Coastguard Worker } 504*333d2b36SAndroid Build Coastguard Worker } 505*333d2b36SAndroid Build Coastguard Worker } 506*333d2b36SAndroid Build Coastguard Worker return -1 507*333d2b36SAndroid Build Coastguard Worker} 508*333d2b36SAndroid Build Coastguard Worker 509*333d2b36SAndroid Build Coastguard Workertype readBuf []byte 510*333d2b36SAndroid Build Coastguard Worker 511*333d2b36SAndroid Build Coastguard Workerfunc (b *readBuf) uint16() uint16 { 512*333d2b36SAndroid Build Coastguard Worker v := binary.LittleEndian.Uint16(*b) 513*333d2b36SAndroid Build Coastguard Worker *b = (*b)[2:] 514*333d2b36SAndroid Build Coastguard Worker return v 515*333d2b36SAndroid Build Coastguard Worker} 516*333d2b36SAndroid Build Coastguard Worker 517*333d2b36SAndroid Build Coastguard Workerfunc (b *readBuf) uint32() uint32 { 518*333d2b36SAndroid Build Coastguard Worker v := binary.LittleEndian.Uint32(*b) 519*333d2b36SAndroid Build Coastguard Worker *b = (*b)[4:] 520*333d2b36SAndroid Build Coastguard Worker return v 521*333d2b36SAndroid Build Coastguard Worker} 522*333d2b36SAndroid Build Coastguard Worker 523*333d2b36SAndroid Build Coastguard Workerfunc (b *readBuf) uint64() uint64 { 524*333d2b36SAndroid Build Coastguard Worker v := binary.LittleEndian.Uint64(*b) 525*333d2b36SAndroid Build Coastguard Worker *b = (*b)[8:] 526*333d2b36SAndroid Build Coastguard Worker return v 527*333d2b36SAndroid Build Coastguard Worker} 528