Home | History | Annotate | Download | only in zip
      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.
      4 
      5 package zip
      6 
      7 import (
      8 	"bufio"
      9 	"encoding/binary"
     10 	"errors"
     11 	"hash"
     12 	"hash/crc32"
     13 	"io"
     14 )
     15 
     16 // TODO(adg): support zip file comments
     17 
     18 // Writer implements a zip file writer.
     19 type Writer struct {
     20 	cw          *countWriter
     21 	dir         []*header
     22 	last        *fileWriter
     23 	closed      bool
     24 	compressors map[uint16]Compressor
     25 }
     26 
     27 type header struct {
     28 	*FileHeader
     29 	offset uint64
     30 }
     31 
     32 // NewWriter returns a new Writer writing a zip file to w.
     33 func NewWriter(w io.Writer) *Writer {
     34 	return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}}
     35 }
     36 
     37 // SetOffset sets the offset of the beginning of the zip data within the
     38 // underlying writer. It should be used when the zip data is appended to an
     39 // existing file, such as a binary executable.
     40 // It must be called before any data is written.
     41 func (w *Writer) SetOffset(n int64) {
     42 	if w.cw.count != 0 {
     43 		panic("zip: SetOffset called after data was written")
     44 	}
     45 	w.cw.count = n
     46 }
     47 
     48 // Flush flushes any buffered data to the underlying writer.
     49 // Calling Flush is not normally necessary; calling Close is sufficient.
     50 func (w *Writer) Flush() error {
     51 	return w.cw.w.(*bufio.Writer).Flush()
     52 }
     53 
     54 // Close finishes writing the zip file by writing the central directory.
     55 // It does not (and cannot) close the underlying writer.
     56 func (w *Writer) Close() error {
     57 	if w.last != nil && !w.last.closed {
     58 		if err := w.last.close(); err != nil {
     59 			return err
     60 		}
     61 		w.last = nil
     62 	}
     63 	if w.closed {
     64 		return errors.New("zip: writer closed twice")
     65 	}
     66 	w.closed = true
     67 
     68 	// write central directory
     69 	start := w.cw.count
     70 	for _, h := range w.dir {
     71 		var buf [directoryHeaderLen]byte
     72 		b := writeBuf(buf[:])
     73 		b.uint32(uint32(directoryHeaderSignature))
     74 		b.uint16(h.CreatorVersion)
     75 		b.uint16(h.ReaderVersion)
     76 		b.uint16(h.Flags)
     77 		b.uint16(h.Method)
     78 		b.uint16(h.ModifiedTime)
     79 		b.uint16(h.ModifiedDate)
     80 		b.uint32(h.CRC32)
     81 		if h.isZip64() || h.offset >= uint32max {
     82 			// the file needs a zip64 header. store maxint in both
     83 			// 32 bit size fields (and offset later) to signal that the
     84 			// zip64 extra header should be used.
     85 			b.uint32(uint32max) // compressed size
     86 			b.uint32(uint32max) // uncompressed size
     87 
     88 			// append a zip64 extra block to Extra
     89 			var buf [28]byte // 2x uint16 + 3x uint64
     90 			eb := writeBuf(buf[:])
     91 			eb.uint16(zip64ExtraId)
     92 			eb.uint16(24) // size = 3x uint64
     93 			eb.uint64(h.UncompressedSize64)
     94 			eb.uint64(h.CompressedSize64)
     95 			eb.uint64(h.offset)
     96 			h.Extra = append(h.Extra, buf[:]...)
     97 		} else {
     98 			b.uint32(h.CompressedSize)
     99 			b.uint32(h.UncompressedSize)
    100 		}
    101 		b.uint16(uint16(len(h.Name)))
    102 		b.uint16(uint16(len(h.Extra)))
    103 		b.uint16(uint16(len(h.Comment)))
    104 		b = b[4:] // skip disk number start and internal file attr (2x uint16)
    105 		b.uint32(h.ExternalAttrs)
    106 		if h.offset > uint32max {
    107 			b.uint32(uint32max)
    108 		} else {
    109 			b.uint32(uint32(h.offset))
    110 		}
    111 		if _, err := w.cw.Write(buf[:]); err != nil {
    112 			return err
    113 		}
    114 		if _, err := io.WriteString(w.cw, h.Name); err != nil {
    115 			return err
    116 		}
    117 		if _, err := w.cw.Write(h.Extra); err != nil {
    118 			return err
    119 		}
    120 		if _, err := io.WriteString(w.cw, h.Comment); err != nil {
    121 			return err
    122 		}
    123 	}
    124 	end := w.cw.count
    125 
    126 	records := uint64(len(w.dir))
    127 	size := uint64(end - start)
    128 	offset := uint64(start)
    129 
    130 	if records > uint16max || size > uint32max || offset > uint32max {
    131 		var buf [directory64EndLen + directory64LocLen]byte
    132 		b := writeBuf(buf[:])
    133 
    134 		// zip64 end of central directory record
    135 		b.uint32(directory64EndSignature)
    136 		b.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64)
    137 		b.uint16(zipVersion45)           // version made by
    138 		b.uint16(zipVersion45)           // version needed to extract
    139 		b.uint32(0)                      // number of this disk
    140 		b.uint32(0)                      // number of the disk with the start of the central directory
    141 		b.uint64(records)                // total number of entries in the central directory on this disk
    142 		b.uint64(records)                // total number of entries in the central directory
    143 		b.uint64(size)                   // size of the central directory
    144 		b.uint64(offset)                 // offset of start of central directory with respect to the starting disk number
    145 
    146 		// zip64 end of central directory locator
    147 		b.uint32(directory64LocSignature)
    148 		b.uint32(0)           // number of the disk with the start of the zip64 end of central directory
    149 		b.uint64(uint64(end)) // relative offset of the zip64 end of central directory record
    150 		b.uint32(1)           // total number of disks
    151 
    152 		if _, err := w.cw.Write(buf[:]); err != nil {
    153 			return err
    154 		}
    155 
    156 		// store max values in the regular end record to signal that
    157 		// that the zip64 values should be used instead
    158 		records = uint16max
    159 		size = uint32max
    160 		offset = uint32max
    161 	}
    162 
    163 	// write end record
    164 	var buf [directoryEndLen]byte
    165 	b := writeBuf(buf[:])
    166 	b.uint32(uint32(directoryEndSignature))
    167 	b = b[4:]                 // skip over disk number and first disk number (2x uint16)
    168 	b.uint16(uint16(records)) // number of entries this disk
    169 	b.uint16(uint16(records)) // number of entries total
    170 	b.uint32(uint32(size))    // size of directory
    171 	b.uint32(uint32(offset))  // start of directory
    172 	// skipped size of comment (always zero)
    173 	if _, err := w.cw.Write(buf[:]); err != nil {
    174 		return err
    175 	}
    176 
    177 	return w.cw.w.(*bufio.Writer).Flush()
    178 }
    179 
    180 // Create adds a file to the zip file using the provided name.
    181 // It returns a Writer to which the file contents should be written.
    182 // The name must be a relative path: it must not start with a drive
    183 // letter (e.g. C:) or leading slash, and only forward slashes are
    184 // allowed.
    185 // The file's contents must be written to the io.Writer before the next
    186 // call to Create, CreateHeader, or Close.
    187 func (w *Writer) Create(name string) (io.Writer, error) {
    188 	header := &FileHeader{
    189 		Name:   name,
    190 		Method: Deflate,
    191 	}
    192 	return w.CreateHeader(header)
    193 }
    194 
    195 // BEGIN ANDROID CHANGE separate createHeaderImpl from CreateHeader
    196 // Legacy version of CreateHeader
    197 func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
    198 	fh.Flags |= DataDescriptorFlag // writing a data descriptor
    199 	return w.createHeaderImpl(fh)
    200 }
    201 
    202 // END ANDROID CHANGE
    203 
    204 // CreateHeader adds a file to the zip file using the provided FileHeader
    205 // for the file metadata.
    206 // It returns a Writer to which the file contents should be written.
    207 //
    208 // The file's contents must be written to the io.Writer before the next
    209 // call to Create, CreateHeader, or Close. The provided FileHeader fh
    210 // must not be modified after a call to CreateHeader.
    211 
    212 // BEGIN ANDROID CHANGE separate createHeaderImpl from CreateHeader
    213 func (w *Writer) createHeaderImpl(fh *FileHeader) (io.Writer, error) {
    214 	// END ANDROID CHANGE
    215 	if w.last != nil && !w.last.closed {
    216 		if err := w.last.close(); err != nil {
    217 			return nil, err
    218 		}
    219 	}
    220 	if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
    221 		// See https://golang.org/issue/11144 confusion.
    222 		return nil, errors.New("archive/zip: invalid duplicate FileHeader")
    223 	}
    224 	// BEGIN ANDROID CHANGE move the setting of DataDescriptorFlag into CreateHeader
    225 	// fh.Flags |= 0x8 // we will write a data descriptor
    226 	// END ANDROID CHANGE
    227 	fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
    228 	fh.ReaderVersion = zipVersion20
    229 
    230 	fw := &fileWriter{
    231 		zipw:      w.cw,
    232 		compCount: &countWriter{w: w.cw},
    233 		crc32:     crc32.NewIEEE(),
    234 	}
    235 	comp := w.compressor(fh.Method)
    236 	if comp == nil {
    237 		return nil, ErrAlgorithm
    238 	}
    239 	var err error
    240 	fw.comp, err = comp(fw.compCount)
    241 	if err != nil {
    242 		return nil, err
    243 	}
    244 	fw.rawCount = &countWriter{w: fw.comp}
    245 
    246 	h := &header{
    247 		FileHeader: fh,
    248 		offset:     uint64(w.cw.count),
    249 	}
    250 	w.dir = append(w.dir, h)
    251 	fw.header = h
    252 
    253 	if err := writeHeader(w.cw, fh); err != nil {
    254 		return nil, err
    255 	}
    256 
    257 	w.last = fw
    258 	return fw, nil
    259 }
    260 
    261 func writeHeader(w io.Writer, h *FileHeader) error {
    262 	var buf [fileHeaderLen]byte
    263 	b := writeBuf(buf[:])
    264 	b.uint32(uint32(fileHeaderSignature))
    265 	b.uint16(h.ReaderVersion)
    266 	b.uint16(h.Flags)
    267 	b.uint16(h.Method)
    268 	b.uint16(h.ModifiedTime)
    269 	b.uint16(h.ModifiedDate)
    270 	// BEGIN ANDROID CHANGE populate header size fields and crc field if not writing a data descriptor
    271 	if h.Flags&DataDescriptorFlag != 0 {
    272 		// since we are writing a data descriptor, these fields should be 0
    273 		b.uint32(0) // crc32,
    274 		b.uint32(0) // compressed size,
    275 		b.uint32(0) // uncompressed size
    276 	} else {
    277 		b.uint32(h.CRC32)
    278 
    279 		if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max {
    280 			panic("skipping writing the data descriptor for a 64-bit value is not yet supported")
    281 		}
    282 		compressedSize := uint32(h.CompressedSize64)
    283 		if compressedSize == 0 {
    284 			compressedSize = h.CompressedSize
    285 		}
    286 
    287 		uncompressedSize := uint32(h.UncompressedSize64)
    288 		if uncompressedSize == 0 {
    289 			uncompressedSize = h.UncompressedSize
    290 		}
    291 
    292 		b.uint32(compressedSize)
    293 		b.uint32(uncompressedSize)
    294 	}
    295 	// END ANDROID CHANGE
    296 	b.uint16(uint16(len(h.Name)))
    297 	b.uint16(uint16(len(h.Extra)))
    298 	if _, err := w.Write(buf[:]); err != nil {
    299 		return err
    300 	}
    301 	if _, err := io.WriteString(w, h.Name); err != nil {
    302 		return err
    303 	}
    304 	_, err := w.Write(h.Extra)
    305 	return err
    306 }
    307 
    308 // RegisterCompressor registers or overrides a custom compressor for a specific
    309 // method ID. If a compressor for a given method is not found, Writer will
    310 // default to looking up the compressor at the package level.
    311 func (w *Writer) RegisterCompressor(method uint16, comp Compressor) {
    312 	if w.compressors == nil {
    313 		w.compressors = make(map[uint16]Compressor)
    314 	}
    315 	w.compressors[method] = comp
    316 }
    317 
    318 func (w *Writer) compressor(method uint16) Compressor {
    319 	comp := w.compressors[method]
    320 	if comp == nil {
    321 		comp = compressor(method)
    322 	}
    323 	return comp
    324 }
    325 
    326 type fileWriter struct {
    327 	*header
    328 	zipw      io.Writer
    329 	rawCount  *countWriter
    330 	comp      io.WriteCloser
    331 	compCount *countWriter
    332 	crc32     hash.Hash32
    333 	closed    bool
    334 }
    335 
    336 func (w *fileWriter) Write(p []byte) (int, error) {
    337 	if w.closed {
    338 		return 0, errors.New("zip: write to closed file")
    339 	}
    340 	w.crc32.Write(p)
    341 	return w.rawCount.Write(p)
    342 }
    343 
    344 // BEGIN ANDROID CHANGE give the return value a name
    345 func (w *fileWriter) close() (err error) {
    346 	// END ANDROID CHANGE
    347 	if w.closed {
    348 		return errors.New("zip: file closed twice")
    349 	}
    350 	w.closed = true
    351 	if err := w.comp.Close(); err != nil {
    352 		return err
    353 	}
    354 
    355 	// update FileHeader
    356 	fh := w.header.FileHeader
    357 	fh.CRC32 = w.crc32.Sum32()
    358 	fh.CompressedSize64 = uint64(w.compCount.count)
    359 	fh.UncompressedSize64 = uint64(w.rawCount.count)
    360 
    361 	if fh.isZip64() {
    362 		fh.CompressedSize = uint32max
    363 		fh.UncompressedSize = uint32max
    364 		fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
    365 	} else {
    366 		fh.CompressedSize = uint32(fh.CompressedSize64)
    367 		fh.UncompressedSize = uint32(fh.UncompressedSize64)
    368 	}
    369 
    370 	// BEGIN ANDROID CHANGE only write data descriptor if the flag is set
    371 	if fh.Flags&DataDescriptorFlag != 0 {
    372 		// Write data descriptor. This is more complicated than one would
    373 		// think, see e.g. comments in zipfile.c:putextended() and
    374 		// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
    375 		// The approach here is to write 8 byte sizes if needed without
    376 		// adding a zip64 extra in the local header (too late anyway).
    377 		var buf []byte
    378 		if fh.isZip64() {
    379 			buf = make([]byte, dataDescriptor64Len)
    380 		} else {
    381 			buf = make([]byte, dataDescriptorLen)
    382 		}
    383 		b := writeBuf(buf)
    384 		b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
    385 		b.uint32(fh.CRC32)
    386 		if fh.isZip64() {
    387 			b.uint64(fh.CompressedSize64)
    388 			b.uint64(fh.UncompressedSize64)
    389 		} else {
    390 			b.uint32(fh.CompressedSize)
    391 			b.uint32(fh.UncompressedSize)
    392 		}
    393 		_, err = w.zipw.Write(buf)
    394 	}
    395 	// END ANDROID CHANGE
    396 	return err
    397 }
    398 
    399 type countWriter struct {
    400 	w     io.Writer
    401 	count int64
    402 }
    403 
    404 func (w *countWriter) Write(p []byte) (int, error) {
    405 	n, err := w.w.Write(p)
    406 	w.count += int64(n)
    407 	return n, err
    408 }
    409 
    410 type nopCloser struct {
    411 	io.Writer
    412 }
    413 
    414 func (w nopCloser) Close() error {
    415 	return nil
    416 }
    417 
    418 type writeBuf []byte
    419 
    420 func (b *writeBuf) uint16(v uint16) {
    421 	binary.LittleEndian.PutUint16(*b, v)
    422 	*b = (*b)[2:]
    423 }
    424 
    425 func (b *writeBuf) uint32(v uint32) {
    426 	binary.LittleEndian.PutUint32(*b, v)
    427 	*b = (*b)[4:]
    428 }
    429 
    430 func (b *writeBuf) uint64(v uint64) {
    431 	binary.LittleEndian.PutUint64(*b, v)
    432 	*b = (*b)[8:]
    433 }
    434