1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
6 The zip package provides support for reading ZIP archives.
8 See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
10 This package does not support ZIP64 or disk spanning.
26 FormatError
= os
.NewError("not a valid zip file")
27 UnsupportedMethod
= os
.NewError("unsupported compression algorithm")
28 ChecksumError
= os
.NewError("checksum error")
45 // OpenReader will open the Zip file specified by name and return a Reader.
46 func OpenReader(name
string) (*Reader
, os
.Error
) {
47 f
, err
:= os
.Open(name
, os
.O_RDONLY
, 0644)
55 return NewReader(f
, fi
.Size
)
58 // NewReader returns a new Reader reading from r, which is assumed to
59 // have the given size in bytes.
60 func NewReader(r io
.ReaderAt
, size
int64) (*Reader
, os
.Error
) {
61 end
, err
:= readDirectoryEnd(r
, size
)
67 File
: make([]*File
, end
.directoryRecords
),
70 rs
:= io
.NewSectionReader(r
, 0, size
)
71 if _
, err
= rs
.Seek(int64(end
.directoryOffset
), 0); err
!= nil {
74 buf
:= bufio
.NewReader(rs
)
75 for i
:= range z
.File
{
76 z
.File
[i
] = &File
{zipr
: r
, zipsize
: size
}
77 if err
:= readDirectoryHeader(z
.File
[i
], buf
); err
!= nil {
84 // Open returns a ReadCloser that provides access to the File's contents.
85 func (f
*File
) Open() (rc io
.ReadCloser
, err os
.Error
) {
86 off
:= int64(f
.headerOffset
)
87 if f
.bodyOffset
== 0 {
88 r
:= io
.NewSectionReader(f
.zipr
, off
, f
.zipsize
-off
)
89 if err
= readFileHeader(f
, r
); err
!= nil {
92 if f
.bodyOffset
, err
= r
.Seek(0, 1); err
!= nil {
96 r
:= io
.NewSectionReader(f
.zipr
, off
+f
.bodyOffset
, int64(f
.CompressedSize
))
98 case 0: // store (no compression)
101 rc
= flate
.NewReader(r
)
103 err
= UnsupportedMethod
106 rc
= &checksumReader
{rc
, crc32
.NewIEEE(), f
.CRC32
}
111 type checksumReader
struct {
117 func (r
*checksumReader
) Read(b
[]byte) (n
int, err os
.Error
) {
118 n
, err
= r
.rc
.Read(b
)
123 if r
.hash
.Sum32() != r
.sum
{
129 func (r
*checksumReader
) Close() os
.Error
{ return r
.rc
.Close() }
131 type nopCloser
struct {
135 func (f nopCloser
) Close() os
.Error
{ return nil }
137 func readFileHeader(f
*File
, r io
.Reader
) (err os
.Error
) {
139 if rerr
, ok
:= recover().(os
.Error
); ok
{
145 filenameLength
uint16
149 if signature
!= fileHeaderSignature
{
152 read(r
, &f
.ReaderVersion
)
155 read(r
, &f
.ModifiedTime
)
156 read(r
, &f
.ModifiedDate
)
158 read(r
, &f
.CompressedSize
)
159 read(r
, &f
.UncompressedSize
)
160 read(r
, &filenameLength
)
161 read(r
, &extraLength
)
162 f
.Name
= string(readByteSlice(r
, filenameLength
))
163 f
.Extra
= readByteSlice(r
, extraLength
)
167 func readDirectoryHeader(f
*File
, r io
.Reader
) (err os
.Error
) {
169 if rerr
, ok
:= recover().(os
.Error
); ok
{
175 filenameLength
uint16
178 startDiskNumber
uint16 // unused
179 internalAttributes
uint16 // unused
180 externalAttributes
uint32 // unused
183 if signature
!= directoryHeaderSignature
{
186 read(r
, &f
.CreatorVersion
)
187 read(r
, &f
.ReaderVersion
)
190 read(r
, &f
.ModifiedTime
)
191 read(r
, &f
.ModifiedDate
)
193 read(r
, &f
.CompressedSize
)
194 read(r
, &f
.UncompressedSize
)
195 read(r
, &filenameLength
)
196 read(r
, &extraLength
)
197 read(r
, &commentLength
)
198 read(r
, &startDiskNumber
)
199 read(r
, &internalAttributes
)
200 read(r
, &externalAttributes
)
201 read(r
, &f
.headerOffset
)
202 f
.Name
= string(readByteSlice(r
, filenameLength
))
203 f
.Extra
= readByteSlice(r
, extraLength
)
204 f
.Comment
= string(readByteSlice(r
, commentLength
))
208 func readDirectoryEnd(r io
.ReaderAt
, size
int64) (d
*directoryEnd
, err os
.Error
) {
209 // look for directoryEndSignature in the last 1k, then in the last 65k
211 for i
, bLen
:= range []int64{1024, 65 * 1024} {
215 b
= make([]byte, int(bLen
))
216 if _
, err
:= r
.ReadAt(b
, size
-bLen
); err
!= nil && err
!= os
.EOF
{
219 if p
:= findSignatureInBlock(b
); p
>= 0 {
223 if i
== 1 || bLen
== size
{
224 return nil, FormatError
228 // read header into struct
230 if rerr
, ok
:= recover().(os
.Error
); ok
{
235 br
:= bytes
.NewBuffer(b
[4:]) // skip over signature
236 d
= new(directoryEnd
)
238 read(br
, &d
.dirDiskNbr
)
239 read(br
, &d
.dirRecordsThisDisk
)
240 read(br
, &d
.directoryRecords
)
241 read(br
, &d
.directorySize
)
242 read(br
, &d
.directoryOffset
)
243 read(br
, &d
.commentLen
)
244 d
.comment
= string(readByteSlice(br
, d
.commentLen
))
248 func findSignatureInBlock(b
[]byte) int {
249 const minSize
= 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2 // fixed part of header
250 for i
:= len(b
) - minSize
; i
>= 0; i
-- {
251 // defined from directoryEndSignature in struct.go
252 if b
[i
] == 'P' && b
[i
+1] == 'K' && b
[i
+2] == 0x05 && b
[i
+3] == 0x06 {
253 // n is length of comment
254 n
:= int(b
[i
+minSize
-2]) |
int(b
[i
+minSize
-1])<<8
255 if n
+minSize
+i
== len(b
) {
263 func read(r io
.Reader
, data
interface{}) {
264 if err
:= binary
.Read(r
, binary
.LittleEndian
, data
); err
!= nil {
269 func readByteSlice(r io
.Reader
, l
uint16) []byte {
274 if _
, err
:= io
.ReadFull(r
, b
); err
!= nil {