func(r *Reader)init(rdr io.ReaderAt, size int64)error { end, baseOffset, err := readDirectoryEnd(rdr, size) if err != nil { return err } r.r = rdr r.baseOffset = baseOffset // Since the number of directory records is not validated, it is not // safe to preallocate r.File without first checking that the specified // number of files is reasonable, since a malformed archive may // indicate it contains up to 1 << 128 - 1 files. Since each file has a // header which will be _at least_ 30 bytes we can safely preallocate // if (data size / 30) >= end.directoryRecords. if end.directorySize < uint64(size) && (uint64(size)-end.directorySize)/30 >= end.directoryRecords { r.File = make([]*File, 0, end.directoryRecords) } r.Comment = end.comment rs := io.NewSectionReader(rdr, 0, size) if _, err = rs.Seek(r.baseOffset+int64(end.directoryOffset), io.SeekStart); err != nil { return err } buf := bufio.NewReader(rs)
// The count of files inside a zip is truncated to fit in a uint16. // Gloss over this by reading headers until we encounter // a bad one, and then only report an ErrFormat or UnexpectedEOF if // the file count modulo 65536 is incorrect. for { f := &File{zip: r, zipr: rdr} err = readDirectoryHeader(f, buf) if err == ErrFormat || err == io.ErrUnexpectedEOF { break } if err != nil { return err } f.headerOffset += r.baseOffset r.File = append(r.File, f) } ifuint16(len(r.File)) != uint16(end.directoryRecords) { // only compare 16 bits here // Return the readDirectoryHeader error if we read // the wrong number of directory entries. return err } if zipinsecurepath.Value() == "0" { for _, f := range r.File { if f.Name == "" { // Zip permits an empty file name field. continue } // The zip specification states that names must use forward slashes, // so consider any backslashes in the name insecure. if !filepath.IsLocal(f.Name) || strings.Contains(f.Name, `\`) { zipinsecurepath.IncNonDefault() return ErrInsecurePath } } } returnnil }
// The count of files inside a zip is truncated to fit in a uint16. // Gloss over this by reading headers until we encounter // a bad one, and then only report an ErrFormat or UnexpectedEOF if // the file count modulo 65536 is incorrect. for { f := &File{zip: z, zipr: r} err = readDirectoryHeader(f, buf)
// For compatibility with other zip programs, // if we have a non-zero base offset and can't read // the first directory header, try again with a zero // base offset. if err == ErrFormat && z.baseOffset != 0 && len(z.File) == 0 { z.baseOffset = 0 if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil { return err } buf.Reset(rs) continue }