blob: d35513fd730da62306e37c733655f5ba4792d160 [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
Dan Willemsen3bf1a082016-08-03 00:35:25 -070035
Dan Willemsena1354b32017-02-17 13:14:43 -080036 // The zip64 extras change between the Central Directory and Local File Header, while we use
37 // the same structure for both. The Local File Haeder is taken care of by us writing a data
38 // descriptor with the zip64 values. The Central Directory Entry is written by Close(), where
39 // the zip64 extra is automatically created and appended when necessary.
40 fh.Extra = stripZip64Extras(fh.Extra)
41
Dan Willemsen3bf1a082016-08-03 00:35:25 -070042 h := &header{
43 FileHeader: fh,
44 offset: uint64(w.cw.count),
45 }
46 w.dir = append(w.dir, h)
47
48 if err := writeHeader(w.cw, fh); err != nil {
49 return err
50 }
51
52 // Copy data
53 dataOffset, err := orig.DataOffset()
54 if err != nil {
55 return err
56 }
57 io.Copy(w.cw, io.NewSectionReader(orig.zipr, dataOffset, int64(orig.CompressedSize64)))
58
Nan Zhangd5998cc2017-09-13 13:17:43 -070059 if orig.hasDataDescriptor() {
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)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070078 }
Dan Willemsen3bf1a082016-08-03 00:35:25 -070079 return err
80}
Dan Willemsen017d8932016-08-04 15:43:03 -070081
Dan Willemsena1354b32017-02-17 13:14:43 -080082// Strip any Zip64 extra fields
83func stripZip64Extras(input []byte) []byte {
84 ret := []byte{}
85
86 for len(input) >= 4 {
87 r := readBuf(input)
88 tag := r.uint16()
89 size := r.uint16()
90 if int(size) > len(r) {
91 break
92 }
93 if tag != zip64ExtraId {
94 ret = append(ret, input[:4+size]...)
95 }
96 input = input[4+size:]
97 }
98
99 // Keep any trailing data
100 ret = append(ret, input...)
101
102 return ret
103}
104
Dan Willemsen017d8932016-08-04 15:43:03 -0700105// CreateCompressedHeader adds a file to the zip file using the provied
106// FileHeader for the file metadata.
107// It returns a Writer to which the already compressed file contents
108// should be written.
109//
110// The UncompressedSize64 and CRC32 entries in the FileHeader must be filled
111// out already.
112//
113// The file's contents must be written to the io.Writer before the next
114// call to Create, CreateHeader, CreateCompressedHeader, or Close. The
115// provided FileHeader fh must not be modified after a call to
116// CreateCompressedHeader
117func (w *Writer) CreateCompressedHeader(fh *FileHeader) (io.WriteCloser, error) {
118 if w.last != nil && !w.last.closed {
119 if err := w.last.close(); err != nil {
120 return nil, err
121 }
122 }
123 if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
124 // See https://golang.org/issue/11144 confusion.
125 return nil, errors.New("archive/zip: invalid duplicate FileHeader")
126 }
127
Jeff Gastonc5eb66d2017-08-24 14:11:27 -0700128 fh.Flags |= DataDescriptorFlag // we will write a data descriptor
Dan Willemsen017d8932016-08-04 15:43:03 -0700129
130 fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
131 fh.ReaderVersion = zipVersion20
132
133 fw := &compressedFileWriter{
134 fileWriter{
135 zipw: w.cw,
136 compCount: &countWriter{w: w.cw},
137 },
138 }
139
140 h := &header{
141 FileHeader: fh,
142 offset: uint64(w.cw.count),
143 }
144 w.dir = append(w.dir, h)
145 fw.header = h
146
147 if err := writeHeader(w.cw, fh); err != nil {
148 return nil, err
149 }
150
151 w.last = &fw.fileWriter
152 return fw, nil
153}
154
Jeff Gastonc5eb66d2017-08-24 14:11:27 -0700155// Updated version of CreateHeader that doesn't enforce writing a data descriptor
156func (w *Writer) CreateHeaderAndroid(fh *FileHeader) (io.Writer, error) {
157 writeDataDescriptor := fh.Method != Store
158 if writeDataDescriptor {
159 fh.Flags &= DataDescriptorFlag
160 } else {
161 fh.Flags &= ^uint16(DataDescriptorFlag)
162 }
163 return w.createHeaderImpl(fh)
164}
165
Dan Willemsen017d8932016-08-04 15:43:03 -0700166type compressedFileWriter struct {
167 fileWriter
168}
169
170func (w *compressedFileWriter) Write(p []byte) (int, error) {
171 if w.closed {
172 return 0, errors.New("zip: write to closed file")
173 }
174 return w.compCount.Write(p)
175}
176
177func (w *compressedFileWriter) Close() error {
178 if w.closed {
179 return errors.New("zip: file closed twice")
180 }
181 w.closed = true
182
183 // update FileHeader
184 fh := w.header.FileHeader
185 fh.CompressedSize64 = uint64(w.compCount.count)
186
187 if fh.isZip64() {
188 fh.CompressedSize = uint32max
189 fh.UncompressedSize = uint32max
190 fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
191 } else {
192 fh.CompressedSize = uint32(fh.CompressedSize64)
193 fh.UncompressedSize = uint32(fh.UncompressedSize64)
194 }
195
196 // Write data descriptor. This is more complicated than one would
197 // think, see e.g. comments in zipfile.c:putextended() and
198 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
199 // The approach here is to write 8 byte sizes if needed without
200 // adding a zip64 extra in the local header (too late anyway).
201 var buf []byte
202 if fh.isZip64() {
203 buf = make([]byte, dataDescriptor64Len)
204 } else {
205 buf = make([]byte, dataDescriptorLen)
206 }
207 b := writeBuf(buf)
208 b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
209 b.uint32(fh.CRC32)
210 if fh.isZip64() {
211 b.uint64(fh.CompressedSize64)
212 b.uint64(fh.UncompressedSize64)
213 } else {
214 b.uint32(fh.CompressedSize)
215 b.uint32(fh.UncompressedSize)
216 }
217 _, err := w.zipw.Write(buf)
218 return err
219}