blob: e2e46ffd454e917389348a22a369cfac39b3cdc2 [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
22func (w *Writer) CopyFrom(orig *File, newName string) error {
23 if w.last != nil && !w.last.closed {
24 if err := w.last.close(); err != nil {
25 return err
26 }
27 w.last = nil
28 }
29
30 fileHeader := orig.FileHeader
31 fileHeader.Name = newName
32 fh := &fileHeader
33 fh.Flags |= 0x8
34
35 h := &header{
36 FileHeader: fh,
37 offset: uint64(w.cw.count),
38 }
39 w.dir = append(w.dir, h)
40
41 if err := writeHeader(w.cw, fh); err != nil {
42 return err
43 }
44
45 // Copy data
46 dataOffset, err := orig.DataOffset()
47 if err != nil {
48 return err
49 }
50 io.Copy(w.cw, io.NewSectionReader(orig.zipr, dataOffset, int64(orig.CompressedSize64)))
51
52 // Write data descriptor.
53 var buf []byte
54 if fh.isZip64() {
55 buf = make([]byte, dataDescriptor64Len)
56 } else {
57 buf = make([]byte, dataDescriptorLen)
58 }
59 b := writeBuf(buf)
60 b.uint32(dataDescriptorSignature)
61 b.uint32(fh.CRC32)
62 if fh.isZip64() {
63 b.uint64(fh.CompressedSize64)
64 b.uint64(fh.UncompressedSize64)
65 } else {
66 b.uint32(fh.CompressedSize)
67 b.uint32(fh.UncompressedSize)
68 }
69 _, err = w.cw.Write(buf)
70 return err
71}
Dan Willemsen017d8932016-08-04 15:43:03 -070072
73// CreateCompressedHeader adds a file to the zip file using the provied
74// FileHeader for the file metadata.
75// It returns a Writer to which the already compressed file contents
76// should be written.
77//
78// The UncompressedSize64 and CRC32 entries in the FileHeader must be filled
79// out already.
80//
81// The file's contents must be written to the io.Writer before the next
82// call to Create, CreateHeader, CreateCompressedHeader, or Close. The
83// provided FileHeader fh must not be modified after a call to
84// CreateCompressedHeader
85func (w *Writer) CreateCompressedHeader(fh *FileHeader) (io.WriteCloser, error) {
86 if w.last != nil && !w.last.closed {
87 if err := w.last.close(); err != nil {
88 return nil, err
89 }
90 }
91 if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
92 // See https://golang.org/issue/11144 confusion.
93 return nil, errors.New("archive/zip: invalid duplicate FileHeader")
94 }
95
96 fh.Flags |= 0x8 // we will write a data descriptor
97
98 fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
99 fh.ReaderVersion = zipVersion20
100
101 fw := &compressedFileWriter{
102 fileWriter{
103 zipw: w.cw,
104 compCount: &countWriter{w: w.cw},
105 },
106 }
107
108 h := &header{
109 FileHeader: fh,
110 offset: uint64(w.cw.count),
111 }
112 w.dir = append(w.dir, h)
113 fw.header = h
114
115 if err := writeHeader(w.cw, fh); err != nil {
116 return nil, err
117 }
118
119 w.last = &fw.fileWriter
120 return fw, nil
121}
122
123type compressedFileWriter struct {
124 fileWriter
125}
126
127func (w *compressedFileWriter) Write(p []byte) (int, error) {
128 if w.closed {
129 return 0, errors.New("zip: write to closed file")
130 }
131 return w.compCount.Write(p)
132}
133
134func (w *compressedFileWriter) Close() error {
135 if w.closed {
136 return errors.New("zip: file closed twice")
137 }
138 w.closed = true
139
140 // update FileHeader
141 fh := w.header.FileHeader
142 fh.CompressedSize64 = uint64(w.compCount.count)
143
144 if fh.isZip64() {
145 fh.CompressedSize = uint32max
146 fh.UncompressedSize = uint32max
147 fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
148 } else {
149 fh.CompressedSize = uint32(fh.CompressedSize64)
150 fh.UncompressedSize = uint32(fh.UncompressedSize64)
151 }
152
153 // Write data descriptor. This is more complicated than one would
154 // think, see e.g. comments in zipfile.c:putextended() and
155 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
156 // The approach here is to write 8 byte sizes if needed without
157 // adding a zip64 extra in the local header (too late anyway).
158 var buf []byte
159 if fh.isZip64() {
160 buf = make([]byte, dataDescriptor64Len)
161 } else {
162 buf = make([]byte, dataDescriptorLen)
163 }
164 b := writeBuf(buf)
165 b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
166 b.uint32(fh.CRC32)
167 if fh.isZip64() {
168 b.uint64(fh.CompressedSize64)
169 b.uint64(fh.UncompressedSize64)
170 } else {
171 b.uint32(fh.CompressedSize)
172 b.uint32(fh.UncompressedSize)
173 }
174 _, err := w.zipw.Write(buf)
175 return err
176}