1
// Copyright 2011 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.
16
// TODO(adg): support zip file comments
18
// Writer implements a zip file writer.
24
compressors map[uint16]Compressor
25
names map[string]int // filename -> index in dir slice.
33
// NewWriter returns a new Writer writing a zip file to w.
34
func NewWriter(w io.Writer) *Writer {
35
return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}}
38
// SetOffset sets the offset of the beginning of the zip data within the
39
// underlying writer. It should be used when the zip data is appended to an
40
// existing file, such as a binary executable.
41
// It must be called before any data is written.
42
func (w *Writer) SetOffset(n int64) {
44
panic("zip: SetOffset called after data was written")
49
// newAppendingWriter holds the implementation of Reader.Append.
50
func newAppendingWriter(r *Reader, fw io.Writer) *Writer {
53
w: bufio.NewWriter(fw),
56
dir: make([]*header, len(r.File), len(r.File)*3/2),
57
names: make(map[string]int),
59
for i, f := range r.File {
61
FileHeader: &f.FileHeader,
62
offset: uint64(f.headerOffset),
69
// Flush flushes any buffered data to the underlying writer.
70
// Calling Flush is not normally necessary; calling Close is sufficient.
71
func (w *Writer) Flush() error {
72
return w.cw.w.(*bufio.Writer).Flush()
75
// Close finishes writing the zip file by writing the central directory.
76
// It does not (and can not) close the underlying writer.
77
func (w *Writer) Close() error {
78
if w.last != nil && !w.last.closed {
79
if err := w.last.close(); err != nil {
85
return errors.New("zip: writer closed twice")
89
// write central directory
92
for _, h := range w.dir {
93
if h.FileHeader == nil {
94
// This entry has been superceded by a later
99
var buf [directoryHeaderLen]byte
100
b := writeBuf(buf[:])
101
b.uint32(uint32(directoryHeaderSignature))
102
b.uint16(h.CreatorVersion)
103
b.uint16(h.ReaderVersion)
106
b.uint16(h.ModifiedTime)
107
b.uint16(h.ModifiedDate)
109
if h.isZip64() || h.offset >= uint32max {
110
// the file needs a zip64 header. store maxint in both
111
// 32 bit size fields (and offset later) to signal that the
112
// zip64 extra header should be used.
113
b.uint32(uint32max) // compressed size
114
b.uint32(uint32max) // uncompressed size
116
// append a zip64 extra block to Extra
117
var buf [28]byte // 2x uint16 + 3x uint64
118
eb := writeBuf(buf[:])
119
eb.uint16(zip64ExtraId)
120
eb.uint16(24) // size = 3x uint64
121
eb.uint64(h.UncompressedSize64)
122
eb.uint64(h.CompressedSize64)
124
h.Extra = append(h.Extra, buf[:]...)
126
b.uint32(h.CompressedSize)
127
b.uint32(h.UncompressedSize)
129
b.uint16(uint16(len(h.Name)))
130
b.uint16(uint16(len(h.Extra)))
131
b.uint16(uint16(len(h.Comment)))
132
b = b[4:] // skip disk number start and internal file attr (2x uint16)
133
b.uint32(h.ExternalAttrs)
134
if h.offset > uint32max {
137
b.uint32(uint32(h.offset))
139
if _, err := w.cw.Write(buf[:]); err != nil {
142
if _, err := io.WriteString(w.cw, h.Name); err != nil {
145
if _, err := w.cw.Write(h.Extra); err != nil {
148
if _, err := io.WriteString(w.cw, h.Comment); err != nil {
154
size := uint64(end - start)
155
offset := uint64(start)
157
if records > uint16max || size > uint32max || offset > uint32max {
158
var buf [directory64EndLen + directory64LocLen]byte
159
b := writeBuf(buf[:])
161
// zip64 end of central directory record
162
b.uint32(directory64EndSignature)
163
b.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64)
164
b.uint16(zipVersion45) // version made by
165
b.uint16(zipVersion45) // version needed to extract
166
b.uint32(0) // number of this disk
167
b.uint32(0) // number of the disk with the start of the central directory
168
b.uint64(records) // total number of entries in the central directory on this disk
169
b.uint64(records) // total number of entries in the central directory
170
b.uint64(size) // size of the central directory
171
b.uint64(offset) // offset of start of central directory with respect to the starting disk number
173
// zip64 end of central directory locator
174
b.uint32(directory64LocSignature)
175
b.uint32(0) // number of the disk with the start of the zip64 end of central directory
176
b.uint64(uint64(end)) // relative offset of the zip64 end of central directory record
177
b.uint32(1) // total number of disks
179
if _, err := w.cw.Write(buf[:]); err != nil {
183
// store max values in the regular end record to signal that
184
// that the zip64 values should be used instead
191
var buf [directoryEndLen]byte
192
b := writeBuf(buf[:])
193
b.uint32(uint32(directoryEndSignature))
194
b = b[4:] // skip over disk number and first disk number (2x uint16)
195
b.uint16(uint16(records)) // number of entries this disk
196
b.uint16(uint16(records)) // number of entries total
197
b.uint32(uint32(size)) // size of directory
198
b.uint32(uint32(offset)) // start of directory
199
// skipped size of comment (always zero)
200
if _, err := w.cw.Write(buf[:]); err != nil {
204
return w.cw.w.(*bufio.Writer).Flush()
207
// Create adds a file to the zip file using the provided name.
208
// It returns a Writer to which the file contents should be written.
209
// The name must be a relative path: it must not start with a drive
210
// letter (e.g. C:) or leading slash, and only forward slashes are
212
// The file's contents must be written to the io.Writer before the next
213
// call to Create, CreateHeader, or Close.
214
func (w *Writer) Create(name string) (io.Writer, error) {
215
header := &FileHeader{
219
return w.CreateHeader(header)
222
// CreateHeader adds a file to the zip file using the provided FileHeader
223
// for the file metadata.
224
// It returns a Writer to which the file contents should be written.
226
// The file's contents must be written to the io.Writer before the next
227
// call to Create, CreateHeader, or Close. The provided FileHeader fh
228
// must not be modified after a call to CreateHeader.
229
func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
230
if w.last != nil && !w.last.closed {
231
if err := w.last.close(); err != nil {
235
if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
236
// See https://golang.org/issue/11144 confusion.
237
return nil, errors.New("archive/zip: invalid duplicate FileHeader")
239
if i, ok := w.names[fh.Name]; ok {
240
// We're appending a file that existed already,
241
// so clear out the old entry so that it won't
242
// be added to the index.
243
w.dir[i].FileHeader = nil
244
delete(w.names, fh.Name)
247
fh.Flags |= 0x8 // we will write a data descriptor
249
fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
250
fh.ReaderVersion = zipVersion20
254
compCount: &countWriter{w: w.cw},
255
crc32: crc32.NewIEEE(),
257
comp := w.compressor(fh.Method)
259
return nil, ErrAlgorithm
262
fw.comp, err = comp(fw.compCount)
266
fw.rawCount = &countWriter{w: fw.comp}
270
offset: uint64(w.cw.count),
272
w.dir = append(w.dir, h)
275
if err := writeHeader(w.cw, fh); err != nil {
283
func writeHeader(w io.Writer, h *FileHeader) error {
284
var buf [fileHeaderLen]byte
285
b := writeBuf(buf[:])
286
b.uint32(uint32(fileHeaderSignature))
287
b.uint16(h.ReaderVersion)
290
b.uint16(h.ModifiedTime)
291
b.uint16(h.ModifiedDate)
292
b.uint32(0) // since we are writing a data descriptor crc32,
293
b.uint32(0) // compressed size,
294
b.uint32(0) // and uncompressed size should be zero
295
b.uint16(uint16(len(h.Name)))
296
b.uint16(uint16(len(h.Extra)))
297
if _, err := w.Write(buf[:]); err != nil {
300
if _, err := io.WriteString(w, h.Name); err != nil {
303
_, err := w.Write(h.Extra)
307
// RegisterCompressor registers or overrides a custom compressor for a specific
308
// method ID. If a compressor for a given method is not found, Writer will
309
// default to looking up the compressor at the package level.
310
func (w *Writer) RegisterCompressor(method uint16, comp Compressor) {
311
if w.compressors == nil {
312
w.compressors = make(map[uint16]Compressor)
314
w.compressors[method] = comp
317
func (w *Writer) compressor(method uint16) Compressor {
318
comp := w.compressors[method]
320
comp = compressor(method)
325
type fileWriter struct {
328
rawCount *countWriter
330
compCount *countWriter
335
func (w *fileWriter) Write(p []byte) (int, error) {
337
return 0, errors.New("zip: write to closed file")
340
return w.rawCount.Write(p)
343
func (w *fileWriter) close() error {
345
return errors.New("zip: file closed twice")
348
if err := w.comp.Close(); err != nil {
353
fh := w.header.FileHeader
354
fh.CRC32 = w.crc32.Sum32()
355
fh.CompressedSize64 = uint64(w.compCount.count)
356
fh.UncompressedSize64 = uint64(w.rawCount.count)
359
fh.CompressedSize = uint32max
360
fh.UncompressedSize = uint32max
361
fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
363
fh.CompressedSize = uint32(fh.CompressedSize64)
364
fh.UncompressedSize = uint32(fh.UncompressedSize64)
367
// Write data descriptor. This is more complicated than one would
368
// think, see e.g. comments in zipfile.c:putextended() and
369
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
370
// The approach here is to write 8 byte sizes if needed without
371
// adding a zip64 extra in the local header (too late anyway).
374
buf = make([]byte, dataDescriptor64Len)
376
buf = make([]byte, dataDescriptorLen)
379
b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
382
b.uint64(fh.CompressedSize64)
383
b.uint64(fh.UncompressedSize64)
385
b.uint32(fh.CompressedSize)
386
b.uint32(fh.UncompressedSize)
388
_, err := w.zipw.Write(buf)
392
type countWriter struct {
397
func (w *countWriter) Write(p []byte) (int, error) {
398
n, err := w.w.Write(p)
403
type nopCloser struct {
407
func (w nopCloser) Close() error {
413
func (b *writeBuf) uint16(v uint16) {
414
binary.LittleEndian.PutUint16(*b, v)
418
func (b *writeBuf) uint32(v uint32) {
419
binary.LittleEndian.PutUint32(*b, v)
423
func (b *writeBuf) uint64(v uint64) {
424
binary.LittleEndian.PutUint64(*b, v)