Source file src/pkg/archive/zip/writer.go
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 "compress/flate"
10 "encoding/binary"
11 "errors"
12 "hash"
13 "hash/crc32"
14 "io"
15 )
16
17 // TODO(adg): support zip file comments
18 // TODO(adg): support specifying deflate level
19
20 // Writer implements a zip file writer.
21 type Writer struct {
22 cw *countWriter
23 dir []*header
24 last *fileWriter
25 closed bool
26 }
27
28 type header struct {
29 *FileHeader
30 offset uint32
31 }
32
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)}}
36 }
37
38 // Close finishes writing the zip file by writing the central directory.
39 // It does not (and can not) close the underlying writer.
40 func (w *Writer) Close() error {
41 if w.last != nil && !w.last.closed {
42 if err := w.last.close(); err != nil {
43 return err
44 }
45 w.last = nil
46 }
47 if w.closed {
48 return errors.New("zip: writer closed twice")
49 }
50 w.closed = true
51
52 // write central directory
53 start := w.cw.count
54 for _, h := range w.dir {
55 var buf [directoryHeaderLen]byte
56 b := writeBuf(buf[:])
57 b.uint32(uint32(directoryHeaderSignature))
58 b.uint16(h.CreatorVersion)
59 b.uint16(h.ReaderVersion)
60 b.uint16(h.Flags)
61 b.uint16(h.Method)
62 b.uint16(h.ModifiedTime)
63 b.uint16(h.ModifiedDate)
64 b.uint32(h.CRC32)
65 b.uint32(h.CompressedSize)
66 b.uint32(h.UncompressedSize)
67 b.uint16(uint16(len(h.Name)))
68 b.uint16(uint16(len(h.Extra)))
69 b.uint16(uint16(len(h.Comment)))
70 b = b[4:] // skip disk number start and internal file attr (2x uint16)
71 b.uint32(h.ExternalAttrs)
72 b.uint32(h.offset)
73 if _, err := w.cw.Write(buf[:]); err != nil {
74 return err
75 }
76 if _, err := io.WriteString(w.cw, h.Name); err != nil {
77 return err
78 }
79 if _, err := w.cw.Write(h.Extra); err != nil {
80 return err
81 }
82 if _, err := io.WriteString(w.cw, h.Comment); err != nil {
83 return err
84 }
85 }
86 end := w.cw.count
87
88 // write end record
89 var buf [directoryEndLen]byte
90 b := writeBuf(buf[:])
91 b.uint32(uint32(directoryEndSignature))
92 b = b[4:] // skip over disk number and first disk number (2x uint16)
93 b.uint16(uint16(len(w.dir))) // number of entries this disk
94 b.uint16(uint16(len(w.dir))) // number of entries total
95 b.uint32(uint32(end - start)) // size of directory
96 b.uint32(uint32(start)) // start of directory
97 // skipped size of comment (always zero)
98 if _, err := w.cw.Write(buf[:]); err != nil {
99 return err
100 }
101
102 return w.cw.w.(*bufio.Writer).Flush()
103 }
104
105 // Create adds a file to the zip file using the provided name.
106 // It returns a Writer to which the file contents should be written.
107 // The file's contents must be written to the io.Writer before the next
108 // call to Create, CreateHeader, or Close.
109 func (w *Writer) Create(name string) (io.Writer, error) {
110 header := &FileHeader{
111 Name: name,
112 Method: Deflate,
113 }
114 return w.CreateHeader(header)
115 }
116
117 // CreateHeader adds a file to the zip file using the provided FileHeader
118 // for the file metadata.
119 // It returns a Writer to which the file contents should be written.
120 // The file's contents must be written to the io.Writer before the next
121 // call to Create, CreateHeader, or Close.
122 func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
123 if w.last != nil && !w.last.closed {
124 if err := w.last.close(); err != nil {
125 return nil, err
126 }
127 }
128
129 fh.Flags |= 0x8 // we will write a data descriptor
130 fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14
131 fh.ReaderVersion = 0x14
132
133 fw := &fileWriter{
134 zipw: w.cw,
135 compCount: &countWriter{w: w.cw},
136 crc32: crc32.NewIEEE(),
137 }
138 switch fh.Method {
139 case Store:
140 fw.comp = nopCloser{fw.compCount}
141 case Deflate:
142 var err error
143 fw.comp, err = flate.NewWriter(fw.compCount, 5)
144 if err != nil {
145 return nil, err
146 }
147 default:
148 return nil, ErrAlgorithm
149 }
150 fw.rawCount = &countWriter{w: fw.comp}
151
152 h := &header{
153 FileHeader: fh,
154 offset: uint32(w.cw.count),
155 }
156 w.dir = append(w.dir, h)
157 fw.header = h
158
159 if err := writeHeader(w.cw, fh); err != nil {
160 return nil, err
161 }
162
163 w.last = fw
164 return fw, nil
165 }
166
167 func writeHeader(w io.Writer, h *FileHeader) error {
168 var buf [fileHeaderLen]byte
169 b := writeBuf(buf[:])
170 b.uint32(uint32(fileHeaderSignature))
171 b.uint16(h.ReaderVersion)
172 b.uint16(h.Flags)
173 b.uint16(h.Method)
174 b.uint16(h.ModifiedTime)
175 b.uint16(h.ModifiedDate)
176 b.uint32(h.CRC32)
177 b.uint32(h.CompressedSize)
178 b.uint32(h.UncompressedSize)
179 b.uint16(uint16(len(h.Name)))
180 b.uint16(uint16(len(h.Extra)))
181 if _, err := w.Write(buf[:]); err != nil {
182 return err
183 }
184 if _, err := io.WriteString(w, h.Name); err != nil {
185 return err
186 }
187 _, err := w.Write(h.Extra)
188 return err
189 }
190
191 type fileWriter struct {
192 *header
193 zipw io.Writer
194 rawCount *countWriter
195 comp io.WriteCloser
196 compCount *countWriter
197 crc32 hash.Hash32
198 closed bool
199 }
200
201 func (w *fileWriter) Write(p []byte) (int, error) {
202 if w.closed {
203 return 0, errors.New("zip: write to closed file")
204 }
205 w.crc32.Write(p)
206 return w.rawCount.Write(p)
207 }
208
209 func (w *fileWriter) close() error {
210 if w.closed {
211 return errors.New("zip: file closed twice")
212 }
213 w.closed = true
214 if err := w.comp.Close(); err != nil {
215 return err
216 }
217
218 // update FileHeader
219 fh := w.header.FileHeader
220 fh.CRC32 = w.crc32.Sum32()
221 fh.CompressedSize = uint32(w.compCount.count)
222 fh.UncompressedSize = uint32(w.rawCount.count)
223
224 // write data descriptor
225 var buf [dataDescriptorLen]byte
226 b := writeBuf(buf[:])
227 b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
228 b.uint32(fh.CRC32)
229 b.uint32(fh.CompressedSize)
230 b.uint32(fh.UncompressedSize)
231 _, err := w.zipw.Write(buf[:])
232 return err
233 }
234
235 type countWriter struct {
236 w io.Writer
237 count int64
238 }
239
240 func (w *countWriter) Write(p []byte) (int, error) {
241 n, err := w.w.Write(p)
242 w.count += int64(n)
243 return n, err
244 }
245
246 type nopCloser struct {
247 io.Writer
248 }
249
250 func (w nopCloser) Close() error {
251 return nil
252 }
253
254 type writeBuf []byte
255
256 func (b *writeBuf) uint16(v uint16) {
257 binary.LittleEndian.PutUint16(*b, v)
258 *b = (*b)[2:]
259 }
260
261 func (b *writeBuf) uint32(v uint32) {
262 binary.LittleEndian.PutUint32(*b, v)
263 *b = (*b)[4:]
264 }