blob: e92d02f8a2e872ddab84503a6ea0cd4e238faa78 [file] [log] [blame]
Dan Willemsen25a4e072016-08-05 16:34:03 -07001// Copyright 2010 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/*
6Package zip provides support for reading and writing ZIP archives.
7
8See: https://www.pkware.com/documents/casestudies/APPNOTE.TXT
9
10This package does not support disk spanning.
11
12A note about ZIP64:
13
14To be backwards compatible the FileHeader has both 32 and 64 bit Size
15fields. The 64 bit fields will always contain the correct value and
16for normal archives both fields will be the same. For files requiring
17the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit
18fields must be used instead.
19*/
20package zip
21
22import (
23 "os"
24 "path"
25 "time"
26)
27
28// Compression methods.
29const (
30 Store uint16 = 0
31 Deflate uint16 = 8
32)
33
34const (
35 fileHeaderSignature = 0x04034b50
36 directoryHeaderSignature = 0x02014b50
37 directoryEndSignature = 0x06054b50
38 directory64LocSignature = 0x07064b50
39 directory64EndSignature = 0x06064b50
40 dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder
41 fileHeaderLen = 30 // + filename + extra
42 directoryHeaderLen = 46 // + filename + extra + comment
43 directoryEndLen = 22 // + comment
44 dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size
45 dataDescriptor64Len = 24 // descriptor with 8 byte sizes
46 directory64LocLen = 20 //
47 directory64EndLen = 56 // + extra
48
49 // Constants for the first byte in CreatorVersion
50 creatorFAT = 0
51 creatorUnix = 3
52 creatorNTFS = 11
53 creatorVFAT = 14
54 creatorMacOSX = 19
55
56 // version numbers
57 zipVersion20 = 20 // 2.0
58 zipVersion45 = 45 // 4.5 (reads and writes zip64 archives)
59
60 // limits for non zip64 files
61 uint16max = (1 << 16) - 1
62 uint32max = (1 << 32) - 1
63
64 // extra header id's
65 zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field
66)
67
68// FileHeader describes a file within a zip file.
69// See the zip spec for details.
70type FileHeader struct {
71 // Name is the name of the file.
72 // It must be a relative path: it must not start with a drive
73 // letter (e.g. C:) or leading slash, and only forward slashes
74 // are allowed.
75 Name string
76
77 CreatorVersion uint16
78 ReaderVersion uint16
79 Flags uint16
80 Method uint16
81 ModifiedTime uint16 // MS-DOS time
82 ModifiedDate uint16 // MS-DOS date
83 CRC32 uint32
84 CompressedSize uint32 // Deprecated: Use CompressedSize64 instead.
85 UncompressedSize uint32 // Deprecated: Use UncompressedSize64 instead.
86 CompressedSize64 uint64
87 UncompressedSize64 uint64
88 Extra []byte
89 ExternalAttrs uint32 // Meaning depends on CreatorVersion
90 Comment string
91}
92
93// FileInfo returns an os.FileInfo for the FileHeader.
94func (h *FileHeader) FileInfo() os.FileInfo {
95 return headerFileInfo{h}
96}
97
98// headerFileInfo implements os.FileInfo.
99type headerFileInfo struct {
100 fh *FileHeader
101}
102
103func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
104func (fi headerFileInfo) Size() int64 {
105 if fi.fh.UncompressedSize64 > 0 {
106 return int64(fi.fh.UncompressedSize64)
107 }
108 return int64(fi.fh.UncompressedSize)
109}
110func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
111func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
112func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
113func (fi headerFileInfo) Sys() interface{} { return fi.fh }
114
115// FileInfoHeader creates a partially-populated FileHeader from an
116// os.FileInfo.
117// Because os.FileInfo's Name method returns only the base name of
118// the file it describes, it may be necessary to modify the Name field
119// of the returned header to provide the full path name of the file.
120func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
121 size := fi.Size()
122 fh := &FileHeader{
123 Name: fi.Name(),
124 UncompressedSize64: uint64(size),
125 }
126 fh.SetModTime(fi.ModTime())
127 fh.SetMode(fi.Mode())
128 if fh.UncompressedSize64 > uint32max {
129 fh.UncompressedSize = uint32max
130 } else {
131 fh.UncompressedSize = uint32(fh.UncompressedSize64)
132 }
133 return fh, nil
134}
135
136type directoryEnd struct {
137 diskNbr uint32 // unused
138 dirDiskNbr uint32 // unused
139 dirRecordsThisDisk uint64 // unused
140 directoryRecords uint64
141 directorySize uint64
142 directoryOffset uint64 // relative to file
143 commentLen uint16
144 comment string
145}
146
147// msDosTimeToTime converts an MS-DOS date and time into a time.Time.
148// The resolution is 2s.
149// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
150func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
151 return time.Date(
152 // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
153 int(dosDate>>9+1980),
154 time.Month(dosDate>>5&0xf),
155 int(dosDate&0x1f),
156
157 // time bits 0-4: second/2; 5-10: minute; 11-15: hour
158 int(dosTime>>11),
159 int(dosTime>>5&0x3f),
160 int(dosTime&0x1f*2),
161 0, // nanoseconds
162
163 time.UTC,
164 )
165}
166
167// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
168// The resolution is 2s.
169// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
170func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
171 t = t.In(time.UTC)
172 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
173 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
174 return
175}
176
177// ModTime returns the modification time in UTC.
178// The resolution is 2s.
179func (h *FileHeader) ModTime() time.Time {
180 return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
181}
182
183// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time in UTC.
184// The resolution is 2s.
185func (h *FileHeader) SetModTime(t time.Time) {
186 h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
187}
188
189const (
190 // Unix constants. The specification doesn't mention them,
191 // but these seem to be the values agreed on by tools.
192 s_IFMT = 0xf000
193 s_IFSOCK = 0xc000
194 s_IFLNK = 0xa000
195 s_IFREG = 0x8000
196 s_IFBLK = 0x6000
197 s_IFDIR = 0x4000
198 s_IFCHR = 0x2000
199 s_IFIFO = 0x1000
200 s_ISUID = 0x800
201 s_ISGID = 0x400
202 s_ISVTX = 0x200
203
204 msdosDir = 0x10
205 msdosReadOnly = 0x01
206)
207
208// Mode returns the permission and mode bits for the FileHeader.
209func (h *FileHeader) Mode() (mode os.FileMode) {
210 switch h.CreatorVersion >> 8 {
211 case creatorUnix, creatorMacOSX:
212 mode = unixModeToFileMode(h.ExternalAttrs >> 16)
213 case creatorNTFS, creatorVFAT, creatorFAT:
214 mode = msdosModeToFileMode(h.ExternalAttrs)
215 }
216 if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
217 mode |= os.ModeDir
218 }
219 return mode
220}
221
222// SetMode changes the permission and mode bits for the FileHeader.
223func (h *FileHeader) SetMode(mode os.FileMode) {
224 h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
225 h.ExternalAttrs = fileModeToUnixMode(mode) << 16
226
227 // set MSDOS attributes too, as the original zip does.
228 if mode&os.ModeDir != 0 {
229 h.ExternalAttrs |= msdosDir
230 }
231 if mode&0200 == 0 {
232 h.ExternalAttrs |= msdosReadOnly
233 }
234}
235
236// isZip64 reports whether the file size exceeds the 32 bit limit
237func (fh *FileHeader) isZip64() bool {
238 return fh.CompressedSize64 >= uint32max || fh.UncompressedSize64 >= uint32max
239}
240
241func msdosModeToFileMode(m uint32) (mode os.FileMode) {
242 if m&msdosDir != 0 {
243 mode = os.ModeDir | 0777
244 } else {
245 mode = 0666
246 }
247 if m&msdosReadOnly != 0 {
248 mode &^= 0222
249 }
250 return mode
251}
252
253func fileModeToUnixMode(mode os.FileMode) uint32 {
254 var m uint32
255 switch mode & os.ModeType {
256 default:
257 m = s_IFREG
258 case os.ModeDir:
259 m = s_IFDIR
260 case os.ModeSymlink:
261 m = s_IFLNK
262 case os.ModeNamedPipe:
263 m = s_IFIFO
264 case os.ModeSocket:
265 m = s_IFSOCK
266 case os.ModeDevice:
267 if mode&os.ModeCharDevice != 0 {
268 m = s_IFCHR
269 } else {
270 m = s_IFBLK
271 }
272 }
273 if mode&os.ModeSetuid != 0 {
274 m |= s_ISUID
275 }
276 if mode&os.ModeSetgid != 0 {
277 m |= s_ISGID
278 }
279 if mode&os.ModeSticky != 0 {
280 m |= s_ISVTX
281 }
282 return m | uint32(mode&0777)
283}
284
285func unixModeToFileMode(m uint32) os.FileMode {
286 mode := os.FileMode(m & 0777)
287 switch m & s_IFMT {
288 case s_IFBLK:
289 mode |= os.ModeDevice
290 case s_IFCHR:
291 mode |= os.ModeDevice | os.ModeCharDevice
292 case s_IFDIR:
293 mode |= os.ModeDir
294 case s_IFIFO:
295 mode |= os.ModeNamedPipe
296 case s_IFLNK:
297 mode |= os.ModeSymlink
298 case s_IFREG:
299 // nothing to do
300 case s_IFSOCK:
301 mode |= os.ModeSocket
302 }
303 if m&s_ISGID != 0 {
304 mode |= os.ModeSetgid
305 }
306 if m&s_ISUID != 0 {
307 mode |= os.ModeSetuid
308 }
309 if m&s_ISVTX != 0 {
310 mode |= os.ModeSticky
311 }
312 return mode
313}