blob: bde3afa4f0eccad15a4e5e77007680c31c67472d [file] [log] [blame]
Dan Willemsen3bf1a082016-08-03 00:35:25 -07001// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package zip
16
17import (
Dan Willemsen017d8932016-08-04 15:43:03 -070018 "errors"
Dan Willemsen3bf1a082016-08-03 00:35:25 -070019 "io"
20)
21
Jeff Gastonc5eb66d2017-08-24 14:11:27 -070022const DataDescriptorFlag = 0x8
23
Dan Willemsen3bf1a082016-08-03 00:35:25 -070024func (w *Writer) CopyFrom(orig *File, newName string) error {
25 if w.last != nil && !w.last.closed {
26 if err := w.last.close(); err != nil {
27 return err
28 }
29 w.last = nil
30 }
31
32 fileHeader := orig.FileHeader
33 fileHeader.Name = newName
34 fh := &fileHeader
Jeff Gastonc5eb66d2017-08-24 14:11:27 -070035 fh.Flags |= DataDescriptorFlag
Dan Willemsen3bf1a082016-08-03 00:35:25 -070036
Dan Willemsena1354b32017-02-17 13:14:43 -080037 // The zip64 extras change between the Central Directory and Local File Header, while we use
38 // the same structure for both. The Local File Haeder is taken care of by us writing a data
39 // descriptor with the zip64 values. The Central Directory Entry is written by Close(), where
40 // the zip64 extra is automatically created and appended when necessary.
41 fh.Extra = stripZip64Extras(fh.Extra)
42
Dan Willemsen3bf1a082016-08-03 00:35:25 -070043 h := &header{
44 FileHeader: fh,
45 offset: uint64(w.cw.count),
46 }
47 w.dir = append(w.dir, h)
48
49 if err := writeHeader(w.cw, fh); err != nil {
50 return err
51 }
52
53 // Copy data
54 dataOffset, err := orig.DataOffset()
55 if err != nil {
56 return err
57 }
58 io.Copy(w.cw, io.NewSectionReader(orig.zipr, dataOffset, int64(orig.CompressedSize64)))
59
60 // Write data descriptor.
61 var buf []byte
62 if fh.isZip64() {
63 buf = make([]byte, dataDescriptor64Len)
64 } else {
65 buf = make([]byte, dataDescriptorLen)
66 }
67 b := writeBuf(buf)
68 b.uint32(dataDescriptorSignature)
69 b.uint32(fh.CRC32)
70 if fh.isZip64() {
71 b.uint64(fh.CompressedSize64)
72 b.uint64(fh.UncompressedSize64)
73 } else {
74 b.uint32(fh.CompressedSize)
75 b.uint32(fh.UncompressedSize)
76 }
77 _, err = w.cw.Write(buf)
78 return err
79}
Dan Willemsen017d8932016-08-04 15:43:03 -070080
Dan Willemsena1354b32017-02-17 13:14:43 -080081// Strip any Zip64 extra fields
82func stripZip64Extras(input []byte) []byte {
83 ret := []byte{}
84
85 for len(input) >= 4 {
86 r := readBuf(input)
87 tag := r.uint16()
88 size := r.uint16()
89 if int(size) > len(r) {
90 break
91 }
92 if tag != zip64ExtraId {
93 ret = append(ret, input[:4+size]...)
94 }
95 input = input[4+size:]
96 }
97
98 // Keep any trailing data
99 ret = append(ret, input...)
100
101 return ret
102}
103
Dan Willemsen017d8932016-08-04 15:43:03 -0700104// CreateCompressedHeader adds a file to the zip file using the provied
105// FileHeader for the file metadata.
106// It returns a Writer to which the already compressed file contents
107// should be written.
108//
109// The UncompressedSize64 and CRC32 entries in the FileHeader must be filled
110// out already.
111//
112// The file's contents must be written to the io.Writer before the next
113// call to Create, CreateHeader, CreateCompressedHeader, or Close. The
114// provided FileHeader fh must not be modified after a call to
115// CreateCompressedHeader
116func (w *Writer) CreateCompressedHeader(fh *FileHeader) (io.WriteCloser, error) {
117 if w.last != nil && !w.last.closed {
118 if err := w.last.close(); err != nil {
119 return nil, err
120 }
121 }
122 if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
123 // See https://golang.org/issue/11144 confusion.
124 return nil, errors.New("archive/zip: invalid duplicate FileHeader")
125 }
126
Jeff Gastonc5eb66d2017-08-24 14:11:27 -0700127 fh.Flags |= DataDescriptorFlag // we will write a data descriptor
Dan Willemsen017d8932016-08-04 15:43:03 -0700128
129 fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
130 fh.ReaderVersion = zipVersion20
131
132 fw := &compressedFileWriter{
133 fileWriter{
134 zipw: w.cw,
135 compCount: &countWriter{w: w.cw},
136 },
137 }
138
139 h := &header{
140 FileHeader: fh,
141 offset: uint64(w.cw.count),
142 }
143 w.dir = append(w.dir, h)
144 fw.header = h
145
146 if err := writeHeader(w.cw, fh); err != nil {
147 return nil, err
148 }
149
150 w.last = &fw.fileWriter
151 return fw, nil
152}
153
Jeff Gastonc5eb66d2017-08-24 14:11:27 -0700154// Updated version of CreateHeader that doesn't enforce writing a data descriptor
155func (w *Writer) CreateHeaderAndroid(fh *FileHeader) (io.Writer, error) {
156 writeDataDescriptor := fh.Method != Store
157 if writeDataDescriptor {
158 fh.Flags &= DataDescriptorFlag
159 } else {
160 fh.Flags &= ^uint16(DataDescriptorFlag)
161 }
162 return w.createHeaderImpl(fh)
163}
164
Dan Willemsen017d8932016-08-04 15:43:03 -0700165type compressedFileWriter struct {
166 fileWriter
167}
168
169func (w *compressedFileWriter) Write(p []byte) (int, error) {
170 if w.closed {
171 return 0, errors.New("zip: write to closed file")
172 }
173 return w.compCount.Write(p)
174}
175
176func (w *compressedFileWriter) Close() error {
177 if w.closed {
178 return errors.New("zip: file closed twice")
179 }
180 w.closed = true
181
182 // update FileHeader
183 fh := w.header.FileHeader
184 fh.CompressedSize64 = uint64(w.compCount.count)
185
186 if fh.isZip64() {
187 fh.CompressedSize = uint32max
188 fh.UncompressedSize = uint32max
189 fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
190 } else {
191 fh.CompressedSize = uint32(fh.CompressedSize64)
192 fh.UncompressedSize = uint32(fh.UncompressedSize64)
193 }
194
195 // Write data descriptor. This is more complicated than one would
196 // think, see e.g. comments in zipfile.c:putextended() and
197 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
198 // The approach here is to write 8 byte sizes if needed without
199 // adding a zip64 extra in the local header (too late anyway).
200 var buf []byte
201 if fh.isZip64() {
202 buf = make([]byte, dataDescriptor64Len)
203 } else {
204 buf = make([]byte, dataDescriptorLen)
205 }
206 b := writeBuf(buf)
207 b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
208 b.uint32(fh.CRC32)
209 if fh.isZip64() {
210 b.uint64(fh.CompressedSize64)
211 b.uint64(fh.UncompressedSize64)
212 } else {
213 b.uint32(fh.CompressedSize)
214 b.uint32(fh.UncompressedSize)
215 }
216 _, err := w.zipw.Write(buf)
217 return err
218}