OSDN Git Service

b0a559936bd2a2dcdf23fa34fed252bd949ba1b5
[pf3gnuchains/gcc-fork.git] / libgo / go / archive / zip / reader.go
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.
4
5 package zip
6
7 import (
8         "bufio"
9         "compress/flate"
10         "hash"
11         "hash/crc32"
12         "encoding/binary"
13         "io"
14         "io/ioutil"
15         "os"
16 )
17
18 var (
19         FormatError       = os.NewError("zip: not a valid zip file")
20         UnsupportedMethod = os.NewError("zip: unsupported compression algorithm")
21         ChecksumError     = os.NewError("zip: checksum error")
22 )
23
24 type Reader struct {
25         r       io.ReaderAt
26         File    []*File
27         Comment string
28 }
29
30 type ReadCloser struct {
31         f *os.File
32         Reader
33 }
34
35 type File struct {
36         FileHeader
37         zipr         io.ReaderAt
38         zipsize      int64
39         headerOffset int64
40 }
41
42 func (f *File) hasDataDescriptor() bool {
43         return f.Flags&0x8 != 0
44 }
45
46 // OpenReader will open the Zip file specified by name and return a ReadCloser.
47 func OpenReader(name string) (*ReadCloser, os.Error) {
48         f, err := os.Open(name)
49         if err != nil {
50                 return nil, err
51         }
52         fi, err := f.Stat()
53         if err != nil {
54                 f.Close()
55                 return nil, err
56         }
57         r := new(ReadCloser)
58         if err := r.init(f, fi.Size); err != nil {
59                 f.Close()
60                 return nil, err
61         }
62         return r, nil
63 }
64
65 // NewReader returns a new Reader reading from r, which is assumed to
66 // have the given size in bytes.
67 func NewReader(r io.ReaderAt, size int64) (*Reader, os.Error) {
68         zr := new(Reader)
69         if err := zr.init(r, size); err != nil {
70                 return nil, err
71         }
72         return zr, nil
73 }
74
75 func (z *Reader) init(r io.ReaderAt, size int64) os.Error {
76         end, err := readDirectoryEnd(r, size)
77         if err != nil {
78                 return err
79         }
80         z.r = r
81         z.File = make([]*File, 0, end.directoryRecords)
82         z.Comment = end.comment
83         rs := io.NewSectionReader(r, 0, size)
84         if _, err = rs.Seek(int64(end.directoryOffset), os.SEEK_SET); err != nil {
85                 return err
86         }
87         buf := bufio.NewReader(rs)
88
89         // The count of files inside a zip is truncated to fit in a uint16.
90         // Gloss over this by reading headers until we encounter
91         // a bad one, and then only report a FormatError or UnexpectedEOF if
92         // the file count modulo 65536 is incorrect.
93         for {
94                 f := &File{zipr: r, zipsize: size}
95                 err = readDirectoryHeader(f, buf)
96                 if err == FormatError || err == io.ErrUnexpectedEOF {
97                         break
98                 }
99                 if err != nil {
100                         return err
101                 }
102                 z.File = append(z.File, f)
103         }
104         if uint16(len(z.File)) != end.directoryRecords {
105                 // Return the readDirectoryHeader error if we read
106                 // the wrong number of directory entries.
107                 return err
108         }
109         return nil
110 }
111
112 // Close closes the Zip file, rendering it unusable for I/O.
113 func (rc *ReadCloser) Close() os.Error {
114         return rc.f.Close()
115 }
116
117 // Open returns a ReadCloser that provides access to the File's contents.
118 // It is safe to Open and Read from files concurrently.
119 func (f *File) Open() (rc io.ReadCloser, err os.Error) {
120         bodyOffset, err := f.findBodyOffset()
121         if err != nil {
122                 return
123         }
124         size := int64(f.CompressedSize)
125         if size == 0 && f.hasDataDescriptor() {
126                 // permit SectionReader to see the rest of the file
127                 size = f.zipsize - (f.headerOffset + bodyOffset)
128         }
129         r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
130         switch f.Method {
131         case Store: // (no compression)
132                 rc = ioutil.NopCloser(r)
133         case Deflate:
134                 rc = flate.NewReader(r)
135         default:
136                 err = UnsupportedMethod
137         }
138         if rc != nil {
139                 rc = &checksumReader{rc, crc32.NewIEEE(), f, r}
140         }
141         return
142 }
143
144 type checksumReader struct {
145         rc   io.ReadCloser
146         hash hash.Hash32
147         f    *File
148         zipr io.Reader // for reading the data descriptor
149 }
150
151 func (r *checksumReader) Read(b []byte) (n int, err os.Error) {
152         n, err = r.rc.Read(b)
153         r.hash.Write(b[:n])
154         if err != os.EOF {
155                 return
156         }
157         if r.f.hasDataDescriptor() {
158                 if err = readDataDescriptor(r.zipr, r.f); err != nil {
159                         return
160                 }
161         }
162         if r.hash.Sum32() != r.f.CRC32 {
163                 err = ChecksumError
164         }
165         return
166 }
167
168 func (r *checksumReader) Close() os.Error { return r.rc.Close() }
169
170 func readFileHeader(f *File, r io.Reader) os.Error {
171         var b [fileHeaderLen]byte
172         if _, err := io.ReadFull(r, b[:]); err != nil {
173                 return err
174         }
175         c := binary.LittleEndian
176         if sig := c.Uint32(b[:4]); sig != fileHeaderSignature {
177                 return FormatError
178         }
179         f.ReaderVersion = c.Uint16(b[4:6])
180         f.Flags = c.Uint16(b[6:8])
181         f.Method = c.Uint16(b[8:10])
182         f.ModifiedTime = c.Uint16(b[10:12])
183         f.ModifiedDate = c.Uint16(b[12:14])
184         f.CRC32 = c.Uint32(b[14:18])
185         f.CompressedSize = c.Uint32(b[18:22])
186         f.UncompressedSize = c.Uint32(b[22:26])
187         filenameLen := int(c.Uint16(b[26:28]))
188         extraLen := int(c.Uint16(b[28:30]))
189         d := make([]byte, filenameLen+extraLen)
190         if _, err := io.ReadFull(r, d); err != nil {
191                 return err
192         }
193         f.Name = string(d[:filenameLen])
194         f.Extra = d[filenameLen:]
195         return nil
196 }
197
198 // findBodyOffset does the minimum work to verify the file has a header
199 // and returns the file body offset.
200 func (f *File) findBodyOffset() (int64, os.Error) {
201         r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset)
202         var b [fileHeaderLen]byte
203         if _, err := io.ReadFull(r, b[:]); err != nil {
204                 return 0, err
205         }
206         c := binary.LittleEndian
207         if sig := c.Uint32(b[:4]); sig != fileHeaderSignature {
208                 return 0, FormatError
209         }
210         filenameLen := int(c.Uint16(b[26:28]))
211         extraLen := int(c.Uint16(b[28:30]))
212         return int64(fileHeaderLen + filenameLen + extraLen), nil
213 }
214
215 // readDirectoryHeader attempts to read a directory header from r.
216 // It returns io.ErrUnexpectedEOF if it cannot read a complete header,
217 // and FormatError if it doesn't find a valid header signature.
218 func readDirectoryHeader(f *File, r io.Reader) os.Error {
219         var b [directoryHeaderLen]byte
220         if _, err := io.ReadFull(r, b[:]); err != nil {
221                 return err
222         }
223         c := binary.LittleEndian
224         if sig := c.Uint32(b[:4]); sig != directoryHeaderSignature {
225                 return FormatError
226         }
227         f.CreatorVersion = c.Uint16(b[4:6])
228         f.ReaderVersion = c.Uint16(b[6:8])
229         f.Flags = c.Uint16(b[8:10])
230         f.Method = c.Uint16(b[10:12])
231         f.ModifiedTime = c.Uint16(b[12:14])
232         f.ModifiedDate = c.Uint16(b[14:16])
233         f.CRC32 = c.Uint32(b[16:20])
234         f.CompressedSize = c.Uint32(b[20:24])
235         f.UncompressedSize = c.Uint32(b[24:28])
236         filenameLen := int(c.Uint16(b[28:30]))
237         extraLen := int(c.Uint16(b[30:32]))
238         commentLen := int(c.Uint16(b[32:34]))
239         // startDiskNumber := c.Uint16(b[34:36])    // Unused
240         // internalAttributes := c.Uint16(b[36:38]) // Unused
241         f.ExternalAttrs = c.Uint32(b[38:42])
242         f.headerOffset = int64(c.Uint32(b[42:46]))
243         d := make([]byte, filenameLen+extraLen+commentLen)
244         if _, err := io.ReadFull(r, d); err != nil {
245                 return err
246         }
247         f.Name = string(d[:filenameLen])
248         f.Extra = d[filenameLen : filenameLen+extraLen]
249         f.Comment = string(d[filenameLen+extraLen:])
250         return nil
251 }
252
253 func readDataDescriptor(r io.Reader, f *File) os.Error {
254         var b [dataDescriptorLen]byte
255         if _, err := io.ReadFull(r, b[:]); err != nil {
256                 return err
257         }
258         c := binary.LittleEndian
259         f.CRC32 = c.Uint32(b[:4])
260         f.CompressedSize = c.Uint32(b[4:8])
261         f.UncompressedSize = c.Uint32(b[8:12])
262         return nil
263 }
264
265 func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err os.Error) {
266         // look for directoryEndSignature in the last 1k, then in the last 65k
267         var b []byte
268         for i, bLen := range []int64{1024, 65 * 1024} {
269                 if bLen > size {
270                         bLen = size
271                 }
272                 b = make([]byte, int(bLen))
273                 if _, err := r.ReadAt(b, size-bLen); err != nil && err != os.EOF {
274                         return nil, err
275                 }
276                 if p := findSignatureInBlock(b); p >= 0 {
277                         b = b[p:]
278                         break
279                 }
280                 if i == 1 || bLen == size {
281                         return nil, FormatError
282                 }
283         }
284
285         // read header into struct
286         c := binary.LittleEndian
287         d := new(directoryEnd)
288         d.diskNbr = c.Uint16(b[4:6])
289         d.dirDiskNbr = c.Uint16(b[6:8])
290         d.dirRecordsThisDisk = c.Uint16(b[8:10])
291         d.directoryRecords = c.Uint16(b[10:12])
292         d.directorySize = c.Uint32(b[12:16])
293         d.directoryOffset = c.Uint32(b[16:20])
294         d.commentLen = c.Uint16(b[20:22])
295         d.comment = string(b[22 : 22+int(d.commentLen)])
296         return d, nil
297 }
298
299 func findSignatureInBlock(b []byte) int {
300         for i := len(b) - directoryEndLen; i >= 0; i-- {
301                 // defined from directoryEndSignature in struct.go
302                 if b[i] == 'P' && b[i+1] == 'K' && b[i+2] == 0x05 && b[i+3] == 0x06 {
303                         // n is length of comment
304                         n := int(b[i+directoryEndLen-2]) | int(b[i+directoryEndLen-1])<<8
305                         if n+directoryEndLen+i == len(b) {
306                                 return i
307                         }
308                 }
309         }
310         return -1
311 }