Source file src/archive/zip/writer.go
1
2
3
4
5 package zip
6
7 import (
8 "bufio"
9 "encoding/binary"
10 "errors"
11 "hash"
12 "hash/crc32"
13 "io"
14 "strings"
15 "unicode/utf8"
16 )
17
18 var (
19 errLongName = errors.New("zip: FileHeader.Name too long")
20 errLongExtra = errors.New("zip: FileHeader.Extra too long")
21 )
22
23
24 type Writer struct {
25 cw *countWriter
26 dir []*header
27 last *fileWriter
28 closed bool
29 compressors map[uint16]Compressor
30 comment string
31
32
33
34 testHookCloseSizeOffset func(size, offset uint64)
35 }
36
37 type header struct {
38 *FileHeader
39 offset uint64
40 }
41
42
43 func NewWriter(w io.Writer) *Writer {
44 return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}}
45 }
46
47
48
49
50
51 func (w *Writer) SetOffset(n int64) {
52 if w.cw.count != 0 {
53 panic("zip: SetOffset called after data was written")
54 }
55 w.cw.count = n
56 }
57
58
59
60 func (w *Writer) Flush() error {
61 return w.cw.w.(*bufio.Writer).Flush()
62 }
63
64
65
66 func (w *Writer) SetComment(comment string) error {
67 if len(comment) > uint16max {
68 return errors.New("zip: Writer.Comment too long")
69 }
70 w.comment = comment
71 return nil
72 }
73
74
75
76 func (w *Writer) Close() error {
77 if w.last != nil && !w.last.closed {
78 if err := w.last.close(); err != nil {
79 return err
80 }
81 w.last = nil
82 }
83 if w.closed {
84 return errors.New("zip: writer closed twice")
85 }
86 w.closed = true
87
88
89 start := w.cw.count
90 for _, h := range w.dir {
91 var buf [directoryHeaderLen]byte
92 b := writeBuf(buf[:])
93 b.uint32(uint32(directoryHeaderSignature))
94 b.uint16(h.CreatorVersion)
95 b.uint16(h.ReaderVersion)
96 b.uint16(h.Flags)
97 b.uint16(h.Method)
98 b.uint16(h.ModifiedTime)
99 b.uint16(h.ModifiedDate)
100 b.uint32(h.CRC32)
101 if h.isZip64() || h.offset >= uint32max {
102
103
104
105 b.uint32(uint32max)
106 b.uint32(uint32max)
107
108
109 var buf [28]byte
110 eb := writeBuf(buf[:])
111 eb.uint16(zip64ExtraID)
112 eb.uint16(24)
113 eb.uint64(h.UncompressedSize64)
114 eb.uint64(h.CompressedSize64)
115 eb.uint64(h.offset)
116 h.Extra = append(h.Extra, buf[:]...)
117 } else {
118 b.uint32(h.CompressedSize)
119 b.uint32(h.UncompressedSize)
120 }
121
122 b.uint16(uint16(len(h.Name)))
123 b.uint16(uint16(len(h.Extra)))
124 b.uint16(uint16(len(h.Comment)))
125 b = b[4:]
126 b.uint32(h.ExternalAttrs)
127 if h.offset > uint32max {
128 b.uint32(uint32max)
129 } else {
130 b.uint32(uint32(h.offset))
131 }
132 if _, err := w.cw.Write(buf[:]); err != nil {
133 return err
134 }
135 if _, err := io.WriteString(w.cw, h.Name); err != nil {
136 return err
137 }
138 if _, err := w.cw.Write(h.Extra); err != nil {
139 return err
140 }
141 if _, err := io.WriteString(w.cw, h.Comment); err != nil {
142 return err
143 }
144 }
145 end := w.cw.count
146
147 records := uint64(len(w.dir))
148 size := uint64(end - start)
149 offset := uint64(start)
150
151 if f := w.testHookCloseSizeOffset; f != nil {
152 f(size, offset)
153 }
154
155 if records >= uint16max || size >= uint32max || offset >= uint32max {
156 var buf [directory64EndLen + directory64LocLen]byte
157 b := writeBuf(buf[:])
158
159
160 b.uint32(directory64EndSignature)
161 b.uint64(directory64EndLen - 12)
162 b.uint16(zipVersion45)
163 b.uint16(zipVersion45)
164 b.uint32(0)
165 b.uint32(0)
166 b.uint64(records)
167 b.uint64(records)
168 b.uint64(size)
169 b.uint64(offset)
170
171
172 b.uint32(directory64LocSignature)
173 b.uint32(0)
174 b.uint64(uint64(end))
175 b.uint32(1)
176
177 if _, err := w.cw.Write(buf[:]); err != nil {
178 return err
179 }
180
181
182
183 records = uint16max
184 size = uint32max
185 offset = uint32max
186 }
187
188
189 var buf [directoryEndLen]byte
190 b := writeBuf(buf[:])
191 b.uint32(uint32(directoryEndSignature))
192 b = b[4:]
193 b.uint16(uint16(records))
194 b.uint16(uint16(records))
195 b.uint32(uint32(size))
196 b.uint32(uint32(offset))
197 b.uint16(uint16(len(w.comment)))
198 if _, err := w.cw.Write(buf[:]); err != nil {
199 return err
200 }
201 if _, err := io.WriteString(w.cw, w.comment); err != nil {
202 return err
203 }
204
205 return w.cw.w.(*bufio.Writer).Flush()
206 }
207
208
209
210
211
212
213
214
215
216
217 func (w *Writer) Create(name string) (io.Writer, error) {
218 header := &FileHeader{
219 Name: name,
220 Method: Deflate,
221 }
222 return w.CreateHeader(header)
223 }
224
225
226
227
228 func detectUTF8(s string) (valid, require bool) {
229 for i := 0; i < len(s); {
230 r, size := utf8.DecodeRuneInString(s[i:])
231 i += size
232
233
234
235
236
237
238 if r < 0x20 || r > 0x7d || r == 0x5c {
239 if !utf8.ValidRune(r) || (r == utf8.RuneError && size == 1) {
240 return false, false
241 }
242 require = true
243 }
244 }
245 return true, require
246 }
247
248
249
250
251
252
253
254
255 func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
256 if w.last != nil && !w.last.closed {
257 if err := w.last.close(); err != nil {
258 return nil, err
259 }
260 }
261 if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
262
263 return nil, errors.New("archive/zip: invalid duplicate FileHeader")
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282 utf8Valid1, utf8Require1 := detectUTF8(fh.Name)
283 utf8Valid2, utf8Require2 := detectUTF8(fh.Comment)
284 switch {
285 case fh.NonUTF8:
286 fh.Flags &^= 0x800
287 case (utf8Require1 || utf8Require2) && (utf8Valid1 && utf8Valid2):
288 fh.Flags |= 0x800
289 }
290
291 fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20
292 fh.ReaderVersion = zipVersion20
293
294
295 if !fh.Modified.IsZero() {
296
297
298
299
300
301
302
303
304 fh.ModifiedDate, fh.ModifiedTime = timeToMsDosTime(fh.Modified)
305
306
307
308
309
310
311
312 var mbuf [9]byte
313 mt := uint32(fh.Modified.Unix())
314 eb := writeBuf(mbuf[:])
315 eb.uint16(extTimeExtraID)
316 eb.uint16(5)
317 eb.uint8(1)
318 eb.uint32(mt)
319 fh.Extra = append(fh.Extra, mbuf[:]...)
320 }
321
322 var (
323 ow io.Writer
324 fw *fileWriter
325 )
326 h := &header{
327 FileHeader: fh,
328 offset: uint64(w.cw.count),
329 }
330
331 if strings.HasSuffix(fh.Name, "/") {
332
333
334
335
336 fh.Method = Store
337 fh.Flags &^= 0x8
338
339
340 fh.CompressedSize = 0
341 fh.CompressedSize64 = 0
342 fh.UncompressedSize = 0
343 fh.UncompressedSize64 = 0
344
345 ow = dirWriter{}
346 } else {
347 fh.Flags |= 0x8
348
349 fw = &fileWriter{
350 zipw: w.cw,
351 compCount: &countWriter{w: w.cw},
352 crc32: crc32.NewIEEE(),
353 }
354 comp := w.compressor(fh.Method)
355 if comp == nil {
356 return nil, ErrAlgorithm
357 }
358 var err error
359 fw.comp, err = comp(fw.compCount)
360 if err != nil {
361 return nil, err
362 }
363 fw.rawCount = &countWriter{w: fw.comp}
364 fw.header = h
365 ow = fw
366 }
367 w.dir = append(w.dir, h)
368 if err := writeHeader(w.cw, fh); err != nil {
369 return nil, err
370 }
371
372 w.last = fw
373 return ow, nil
374 }
375
376 func writeHeader(w io.Writer, h *FileHeader) error {
377 const maxUint16 = 1<<16 - 1
378 if len(h.Name) > maxUint16 {
379 return errLongName
380 }
381 if len(h.Extra) > maxUint16 {
382 return errLongExtra
383 }
384
385 var buf [fileHeaderLen]byte
386 b := writeBuf(buf[:])
387 b.uint32(uint32(fileHeaderSignature))
388 b.uint16(h.ReaderVersion)
389 b.uint16(h.Flags)
390 b.uint16(h.Method)
391 b.uint16(h.ModifiedTime)
392 b.uint16(h.ModifiedDate)
393 b.uint32(0)
394 b.uint32(0)
395 b.uint32(0)
396 b.uint16(uint16(len(h.Name)))
397 b.uint16(uint16(len(h.Extra)))
398 if _, err := w.Write(buf[:]); err != nil {
399 return err
400 }
401 if _, err := io.WriteString(w, h.Name); err != nil {
402 return err
403 }
404 _, err := w.Write(h.Extra)
405 return err
406 }
407
408
409
410
411 func (w *Writer) RegisterCompressor(method uint16, comp Compressor) {
412 if w.compressors == nil {
413 w.compressors = make(map[uint16]Compressor)
414 }
415 w.compressors[method] = comp
416 }
417
418 func (w *Writer) compressor(method uint16) Compressor {
419 comp := w.compressors[method]
420 if comp == nil {
421 comp = compressor(method)
422 }
423 return comp
424 }
425
426 type dirWriter struct{}
427
428 func (dirWriter) Write(b []byte) (int, error) {
429 if len(b) == 0 {
430 return 0, nil
431 }
432 return 0, errors.New("zip: write to directory")
433 }
434
435 type fileWriter struct {
436 *header
437 zipw io.Writer
438 rawCount *countWriter
439 comp io.WriteCloser
440 compCount *countWriter
441 crc32 hash.Hash32
442 closed bool
443 }
444
445 func (w *fileWriter) Write(p []byte) (int, error) {
446 if w.closed {
447 return 0, errors.New("zip: write to closed file")
448 }
449 w.crc32.Write(p)
450 return w.rawCount.Write(p)
451 }
452
453 func (w *fileWriter) close() error {
454 if w.closed {
455 return errors.New("zip: file closed twice")
456 }
457 w.closed = true
458 if err := w.comp.Close(); err != nil {
459 return err
460 }
461
462
463 fh := w.header.FileHeader
464 fh.CRC32 = w.crc32.Sum32()
465 fh.CompressedSize64 = uint64(w.compCount.count)
466 fh.UncompressedSize64 = uint64(w.rawCount.count)
467
468 if fh.isZip64() {
469 fh.CompressedSize = uint32max
470 fh.UncompressedSize = uint32max
471 fh.ReaderVersion = zipVersion45
472 } else {
473 fh.CompressedSize = uint32(fh.CompressedSize64)
474 fh.UncompressedSize = uint32(fh.UncompressedSize64)
475 }
476
477
478
479
480
481
482 var buf []byte
483 if fh.isZip64() {
484 buf = make([]byte, dataDescriptor64Len)
485 } else {
486 buf = make([]byte, dataDescriptorLen)
487 }
488 b := writeBuf(buf)
489 b.uint32(dataDescriptorSignature)
490 b.uint32(fh.CRC32)
491 if fh.isZip64() {
492 b.uint64(fh.CompressedSize64)
493 b.uint64(fh.UncompressedSize64)
494 } else {
495 b.uint32(fh.CompressedSize)
496 b.uint32(fh.UncompressedSize)
497 }
498 _, err := w.zipw.Write(buf)
499 return err
500 }
501
502 type countWriter struct {
503 w io.Writer
504 count int64
505 }
506
507 func (w *countWriter) Write(p []byte) (int, error) {
508 n, err := w.w.Write(p)
509 w.count += int64(n)
510 return n, err
511 }
512
513 type nopCloser struct {
514 io.Writer
515 }
516
517 func (w nopCloser) Close() error {
518 return nil
519 }
520
521 type writeBuf []byte
522
523 func (b *writeBuf) uint8(v uint8) {
524 (*b)[0] = v
525 *b = (*b)[1:]
526 }
527
528 func (b *writeBuf) uint16(v uint16) {
529 binary.LittleEndian.PutUint16(*b, v)
530 *b = (*b)[2:]
531 }
532
533 func (b *writeBuf) uint32(v uint32) {
534 binary.LittleEndian.PutUint32(*b, v)
535 *b = (*b)[4:]
536 }
537
538 func (b *writeBuf) uint64(v uint64) {
539 binary.LittleEndian.PutUint64(*b, v)
540 *b = (*b)[8:]
541 }
542
View as plain text