| Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 1 | // 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 | /* | 
|  | 6 | Package zip provides support for reading and writing ZIP archives. | 
|  | 7 |  | 
|  | 8 | See: https://www.pkware.com/documents/casestudies/APPNOTE.TXT | 
|  | 9 |  | 
|  | 10 | This package does not support disk spanning. | 
|  | 11 |  | 
|  | 12 | A note about ZIP64: | 
|  | 13 |  | 
|  | 14 | To be backwards compatible the FileHeader has both 32 and 64 bit Size | 
|  | 15 | fields. The 64 bit fields will always contain the correct value and | 
|  | 16 | for normal archives both fields will be the same. For files requiring | 
|  | 17 | the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit | 
|  | 18 | fields must be used instead. | 
|  | 19 | */ | 
|  | 20 | package zip | 
|  | 21 |  | 
|  | 22 | import ( | 
|  | 23 | "os" | 
|  | 24 | "path" | 
|  | 25 | "time" | 
|  | 26 | ) | 
|  | 27 |  | 
|  | 28 | // Compression methods. | 
|  | 29 | const ( | 
|  | 30 | Store   uint16 = 0 | 
|  | 31 | Deflate uint16 = 8 | 
|  | 32 | ) | 
|  | 33 |  | 
|  | 34 | const ( | 
|  | 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. | 
|  | 70 | type 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. | 
|  | 94 | func (h *FileHeader) FileInfo() os.FileInfo { | 
|  | 95 | return headerFileInfo{h} | 
|  | 96 | } | 
|  | 97 |  | 
|  | 98 | // headerFileInfo implements os.FileInfo. | 
|  | 99 | type headerFileInfo struct { | 
|  | 100 | fh *FileHeader | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) } | 
|  | 104 | func (fi headerFileInfo) Size() int64 { | 
|  | 105 | if fi.fh.UncompressedSize64 > 0 { | 
|  | 106 | return int64(fi.fh.UncompressedSize64) | 
|  | 107 | } | 
|  | 108 | return int64(fi.fh.UncompressedSize) | 
|  | 109 | } | 
|  | 110 | func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() } | 
|  | 111 | func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } | 
|  | 112 | func (fi headerFileInfo) Mode() os.FileMode  { return fi.fh.Mode() } | 
|  | 113 | func (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. | 
|  | 120 | func 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 |  | 
|  | 136 | type 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 | 
|  | 150 | func 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 | 
|  | 170 | func 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. | 
|  | 179 | func (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. | 
|  | 185 | func (h *FileHeader) SetModTime(t time.Time) { | 
|  | 186 | h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t) | 
|  | 187 | } | 
|  | 188 |  | 
|  | 189 | const ( | 
|  | 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. | 
|  | 209 | func (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. | 
|  | 223 | func (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 | 
|  | 237 | func (fh *FileHeader) isZip64() bool { | 
|  | 238 | return fh.CompressedSize64 >= uint32max || fh.UncompressedSize64 >= uint32max | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | func 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 |  | 
|  | 253 | func 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 |  | 
|  | 285 | func 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 | } |