1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package zipslicer
18
19 import (
20 "archive/zip"
21 "bufio"
22 "bytes"
23 "compress/flate"
24 "crypto"
25 "encoding/binary"
26 "errors"
27 "hash"
28 "hash/crc32"
29 "io"
30 "io/ioutil"
31 "time"
32 )
33
34 type File struct {
35 CreatorVersion uint16
36 ReaderVersion uint16
37 Flags uint16
38 Method uint16
39 ModifiedTime uint16
40 ModifiedDate uint16
41 CRC32 uint32
42 CompressedSize uint64
43 UncompressedSize uint64
44 Name string
45 Extra []byte
46 Comment []byte
47 InternalAttrs uint16
48 ExternalAttrs uint32
49 Offset uint64
50
51 r io.ReaderAt
52 rs int64
53 raw []byte
54 lfh zipLocalHeader
55 lfhName, lfhExtra []byte
56 ddb []byte
57 compd []byte
58 }
59
60 type Reader struct {
61 f *File
62 rc io.ReadCloser
63 crc hash.Hash32
64 err error
65 nread uint64
66 }
67
68 func (f *File) readLocalHeader() error {
69 if f.lfh.Signature != 0 {
70
71 return nil
72 }
73 sr := io.NewSectionReader(f.r, int64(f.Offset), f.rs)
74 var lfhb [fileHeaderLen]byte
75 if _, err := io.ReadFull(sr, lfhb[:]); err != nil {
76 return err
77 }
78 binary.Read(bytes.NewReader(lfhb[:]), binary.LittleEndian, &f.lfh)
79 if f.lfh.Signature != fileHeaderSignature {
80 return errors.New("local file header not found")
81 }
82 f.lfhName = make([]byte, f.lfh.FilenameLen)
83 if _, err := io.ReadFull(sr, f.lfhName); err != nil {
84 return err
85 }
86 f.lfhExtra = make([]byte, f.lfh.ExtraLen)
87 if _, err := io.ReadFull(sr, f.lfhExtra); err != nil {
88 return err
89 }
90 return nil
91 }
92
93 func (f *File) readDataDesc() error {
94 if err := f.readLocalHeader(); err != nil {
95 return err
96 }
97 if f.lfh.Flags&0x8 == 0 {
98
99 return nil
100 }
101 if len(f.ddb) != 0 {
102
103 return nil
104 }
105 lfhSize := fileHeaderLen + len(f.lfhName) + len(f.lfhExtra)
106 pos := int64(f.Offset) + int64(lfhSize) + int64(f.CompressedSize)
107 f.ddb = make([]byte, dataDescriptor64Len)
108 if _, err := f.r.ReadAt(f.ddb[:dataDescriptorLen], pos); err != nil {
109 return err
110 }
111
112
113 var desc zipDataDesc
114 binary.Read(bytes.NewReader(f.ddb[:dataDescriptorLen]), binary.LittleEndian, &desc)
115 if desc.Signature != dataDescriptorSignature {
116 return errors.New("data descriptor signature is missing")
117 }
118 if f.UncompressedSize >= uint32Max || desc.UncompressedSize != uint32(f.UncompressedSize) || desc.CompressedSize != uint32(f.CompressedSize) {
119
120 if _, err := f.r.ReadAt(f.ddb[dataDescriptorLen:], pos+dataDescriptorLen); err != nil {
121 return err
122 }
123 var desc64 zipDataDesc64
124 binary.Read(bytes.NewReader(f.ddb), binary.LittleEndian, &desc64)
125 if desc64.CompressedSize != f.CompressedSize || desc64.UncompressedSize != f.UncompressedSize {
126 return errors.New("data descriptor is invalid")
127 }
128 f.CRC32 = desc64.CRC32
129 } else {
130
131 f.ddb = f.ddb[:dataDescriptorLen]
132 f.CRC32 = desc.CRC32
133 }
134 return nil
135 }
136
137 func (f *File) GetDirectoryHeader() ([]byte, error) {
138 if len(f.raw) > 0 {
139 return f.raw, nil
140 }
141 hdr := zipCentralDir{
142 Signature: directoryHeaderSignature,
143 CreatorVersion: f.CreatorVersion,
144 ReaderVersion: f.ReaderVersion,
145 Flags: f.Flags,
146 Method: f.Method,
147 ModifiedTime: f.ModifiedTime,
148 ModifiedDate: f.ModifiedDate,
149 CRC32: f.CRC32,
150 CompressedSize: uint32(f.CompressedSize),
151 UncompressedSize: uint32(f.UncompressedSize),
152 InternalAttrs: f.InternalAttrs,
153 ExternalAttrs: f.ExternalAttrs,
154 Offset: uint32(f.Offset),
155 FilenameLen: uint16(len(f.Name)),
156 ExtraLen: uint16(len(f.Extra)),
157 CommentLen: uint16(len(f.Comment)),
158 }
159 if f.CompressedSize >= uint32Max || f.UncompressedSize >= uint32Max || f.Offset >= uint32Max {
160 hdr.CompressedSize = uint32Max
161 hdr.UncompressedSize = uint32Max
162 hdr.Offset = uint32Max
163 extra := zip64Extra{
164 Signature: zip64ExtraID,
165 RecordSize: zip64ExtraLen,
166 UncompressedSize: f.UncompressedSize,
167 CompressedSize: f.CompressedSize,
168 Offset: f.Offset,
169 }
170 b := bytes.NewBuffer(make([]byte, 0, zip64ExtraLen+4+len(f.Extra)))
171 binary.Write(b, binary.LittleEndian, extra)
172 b.Write(f.Extra)
173 f.Extra = b.Bytes()
174 hdr.ExtraLen = uint16(b.Len())
175 hdr.ReaderVersion = zip45
176 }
177 b := bytes.NewBuffer(make([]byte, 0, directoryHeaderLen+len(f.Name)+len(f.Extra)+len(f.Comment)))
178 binary.Write(b, binary.LittleEndian, hdr)
179 b.WriteString(f.Name)
180 b.Write(f.Extra)
181 b.Write(f.Comment)
182 return b.Bytes(), nil
183 }
184
185 func (f *File) GetLocalHeader() ([]byte, error) {
186 if err := f.readLocalHeader(); err != nil {
187 return nil, err
188 }
189 b := bytes.NewBuffer(make([]byte, 0, fileHeaderLen+len(f.lfhName)+len(f.lfhExtra)))
190 binary.Write(b, binary.LittleEndian, f.lfh)
191 b.Write(f.lfhName)
192 b.Write(f.lfhExtra)
193 return b.Bytes(), nil
194 }
195
196 func (f *File) GetDataDescriptor() ([]byte, error) {
197 if err := f.readDataDesc(); err != nil {
198 return nil, err
199 }
200 return f.ddb, nil
201 }
202
203 func (f *File) GetTotalSize() (int64, error) {
204 if err := f.readDataDesc(); err != nil {
205 return 0, err
206 }
207 return fileHeaderLen + int64(len(f.lfhName)+len(f.lfhExtra)+len(f.ddb)) + int64(f.CompressedSize), nil
208 }
209
210 func (d *Directory) NewFile(name string, extra, contents []byte, w io.Writer, mtime time.Time, deflate, useDesc bool) (*File, error) {
211 var zh zip.FileHeader
212 zh.SetModTime(mtime)
213 var fb bytes.Buffer
214 method := zip.Deflate
215 if deflate {
216 c, err := flate.NewWriter(&fb, 9)
217 if err != nil {
218 return nil, err
219 }
220 if _, err := c.Write(contents); err != nil {
221 return nil, err
222 }
223 if err := c.Close(); err != nil {
224 return nil, err
225 }
226 } else {
227 method = zip.Store
228 fb.Write(contents)
229 }
230 crc := crc32.NewIEEE()
231 crc.Write(contents)
232 sum := crc.Sum32()
233 buf := bufio.NewWriter(w)
234
235 f := &File{
236 CreatorVersion: zip45,
237 ReaderVersion: zip20,
238 Method: method,
239 ModifiedTime: zh.ModifiedTime,
240 ModifiedDate: zh.ModifiedDate,
241 CRC32: sum,
242 CompressedSize: uint64(fb.Len()),
243 UncompressedSize: uint64(len(contents)),
244 Name: name,
245 Extra: extra,
246
247 lfhName: []byte(name),
248 lfhExtra: extra,
249 compd: fb.Bytes(),
250 }
251 if useDesc {
252 f.Flags = 0x8
253 f.ReaderVersion = zip45
254 }
255 f.lfh = zipLocalHeader{
256 Signature: fileHeaderSignature,
257 ReaderVersion: f.ReaderVersion,
258 Flags: f.Flags,
259 Method: f.Method,
260 ModifiedTime: f.ModifiedTime,
261 ModifiedDate: f.ModifiedDate,
262 FilenameLen: uint16(len(name)),
263 ExtraLen: uint16(len(extra)),
264 }
265 if !useDesc {
266 f.lfh.CRC32 = f.CRC32
267 f.lfh.CompressedSize = uint32(f.CompressedSize)
268 f.lfh.UncompressedSize = uint32(f.UncompressedSize)
269 }
270 if err := binary.Write(buf, binary.LittleEndian, f.lfh); err != nil {
271 return nil, err
272 }
273 if _, err := buf.WriteString(name); err != nil {
274 return nil, err
275 }
276 if _, err := buf.Write(extra); err != nil {
277 return nil, err
278 }
279 if _, err := buf.Write(fb.Bytes()); err != nil {
280 return nil, err
281 }
282 if useDesc {
283 desc := zipDataDesc64{
284 Signature: dataDescriptorSignature,
285 CRC32: sum,
286 CompressedSize: uint64(fb.Len()),
287 UncompressedSize: uint64(len(contents)),
288 }
289 ddb := bytes.NewBuffer(make([]byte, 0, dataDescriptor64Len))
290 binary.Write(ddb, binary.LittleEndian, desc)
291 f.ddb = ddb.Bytes()
292 if _, err := buf.Write(f.ddb); err != nil {
293 return nil, err
294 }
295 }
296 if err := buf.Flush(); err != nil {
297 return nil, err
298 }
299 return d.AddFile(f)
300 }
301
302 func (f *File) ModTime() time.Time {
303 fh := zip.FileHeader{ModifiedDate: f.ModifiedDate, ModifiedTime: f.ModifiedTime}
304 return fh.ModTime()
305 }
306
307 func (f *File) Open() (io.ReadCloser, error) {
308 return f.OpenAndTeeRaw(nil)
309 }
310
311
312 func (f *File) OpenAndTeeRaw(sink io.Writer) (*Reader, error) {
313 if err := f.readLocalHeader(); err != nil {
314 return nil, err
315 }
316 var r io.Reader
317 if f.compd != nil {
318 r = bytes.NewReader(f.compd)
319 } else {
320 pos := int64(f.Offset) + fileHeaderLen + int64(f.lfh.FilenameLen) + int64(f.lfh.ExtraLen)
321 r = io.NewSectionReader(f.r, pos, int64(f.CompressedSize))
322 }
323 if sink != nil {
324 r = io.TeeReader(r, sink)
325 }
326 crc := crc32.NewIEEE()
327 var rc io.ReadCloser
328 switch f.Method {
329 case zip.Store:
330 rc = ioutil.NopCloser(r)
331 case zip.Deflate:
332 rc = flate.NewReader(r)
333 default:
334 return nil, errors.New("unsupported zip compression")
335 }
336 return &Reader{f: f, rc: rc, crc: crc}, nil
337 }
338
339 func (r *Reader) Read(d []byte) (int, error) {
340 if r.err != nil {
341 return 0, r.err
342 }
343 n, err := r.rc.Read(d)
344 r.crc.Write(d[:n])
345 r.nread += uint64(n)
346 if err == nil {
347 return n, nil
348 }
349 if err != io.EOF {
350 r.err = err
351 return n, err
352 }
353 if r.nread != r.f.UncompressedSize {
354 return 0, io.ErrUnexpectedEOF
355 }
356 if r.f.lfh.Flags&0x8 != 0 {
357 if err2 := r.f.readDataDesc(); err2 != nil {
358 if err2 == io.EOF {
359 err = io.ErrUnexpectedEOF
360 } else {
361 err = err2
362 }
363 }
364 }
365 if r.f.CRC32 != 0 && r.crc.Sum32() != r.f.CRC32 {
366 err = zip.ErrChecksum
367 }
368 r.err = err
369 return n, err
370 }
371
372 func (r *Reader) Close() error {
373 return r.rc.Close()
374 }
375
376 func (f *File) Digest(hash crypto.Hash) ([]byte, error) {
377 fc, err := f.Open()
378 if err != nil {
379 return nil, err
380 }
381 d := hash.New()
382 if _, err := io.Copy(d, fc); err != nil {
383 return nil, err
384 }
385 return d.Sum(nil), fc.Close()
386 }
387
388
389 func (f *File) Dump(w io.Writer) (int64, error) {
390 lfh, err := f.GetLocalHeader()
391 if err != nil {
392 return 0, err
393 }
394 if _, err := w.Write(lfh); err != nil {
395 return 0, err
396 }
397 if f.compd != nil {
398 if _, err := w.Write(f.compd); err != nil {
399 return 0, err
400 }
401 } else {
402 pos := int64(f.Offset) + fileHeaderLen + int64(f.lfh.FilenameLen) + int64(f.lfh.ExtraLen)
403 r := io.NewSectionReader(f.r, pos, int64(f.CompressedSize))
404 if _, err := io.Copy(w, r); err != nil {
405 return 0, err
406 }
407 }
408 ddb, err := f.GetDataDescriptor()
409 if err != nil {
410 return 0, err
411 }
412 if _, err := w.Write(ddb); err != nil {
413 return 0, err
414 }
415 return f.GetTotalSize()
416 }
417
View as plain text