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