github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/archive/zip/struct.go (about) 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/appnote 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 // no compression 31 Deflate uint16 = 8 // DEFLATE compressed 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 IDs. 65 // 66 // IDs 0..31 are reserved for official use by PKWARE. 67 // IDs above that range are defined by third-party vendors. 68 // Since ZIP lacked high precision timestamps (nor a official specification 69 // of the timezone used for the date fields), many competing extra fields 70 // have been invented. Pervasive use effectively makes them "official". 71 // 72 // See http://mdfs.net/Docs/Comp/Archiving/Zip/ExtraField 73 zip64ExtraID = 0x0001 // Zip64 extended information 74 ntfsExtraID = 0x000a // NTFS 75 unixExtraID = 0x000d // UNIX 76 extTimeExtraID = 0x5455 // Extended timestamp 77 infoZipUnixExtraID = 0x5855 // Info-ZIP Unix extension 78 ) 79 80 // FileHeader describes a file within a zip file. 81 // See the zip spec for details. 82 type FileHeader struct { 83 // Name is the name of the file. 84 // It must be a relative path, not start with a drive letter (e.g. C:), 85 // and must use forward slashes instead of back slashes. 86 Name string 87 88 // Comment is any arbitrary user-defined string shorter than 64KiB. 89 Comment string 90 91 // NonUTF8 indicates that Name and Comment are not encoded in UTF-8. 92 // 93 // By specification, the only other encoding permitted should be CP-437, 94 // but historically many ZIP readers interpret Name and Comment as whatever 95 // the system's local character encoding happens to be. 96 // 97 // This flag should only be set if the user intends to encode a non-portable 98 // ZIP file for a specific localized region. Otherwise, the Writer 99 // automatically sets the ZIP format's UTF-8 flag for valid UTF-8 strings. 100 NonUTF8 bool 101 102 CreatorVersion uint16 103 ReaderVersion uint16 104 Flags uint16 105 106 // Method is the compression method. If zero, Store is used. 107 Method uint16 108 109 // Modified is the modified time of the file. 110 // 111 // When reading, an extended timestamp is preferred over the legacy MS-DOS 112 // date field, and the offset between the times is used as the timezone. 113 // If only the MS-DOS date is present, the timezone is assumed to be UTC. 114 // 115 // When writing, an extended timestamp (which is timezone-agnostic) is 116 // always emitted. The legacy MS-DOS date field is encoded according to the 117 // location of the Modified time. 118 Modified time.Time 119 ModifiedTime uint16 // Deprecated: Legacy MS-DOS date; use Modified instead. 120 ModifiedDate uint16 // Deprecated: Legacy MS-DOS time; use Modified instead. 121 122 CRC32 uint32 123 CompressedSize uint32 // Deprecated: Use CompressedSize64 instead. 124 UncompressedSize uint32 // Deprecated: Use UncompressedSize64 instead. 125 CompressedSize64 uint64 126 UncompressedSize64 uint64 127 Extra []byte 128 ExternalAttrs uint32 // Meaning depends on CreatorVersion 129 } 130 131 // FileInfo returns an os.FileInfo for the FileHeader. 132 func (h *FileHeader) FileInfo() os.FileInfo { 133 return headerFileInfo{h} 134 } 135 136 // headerFileInfo implements os.FileInfo. 137 type headerFileInfo struct { 138 fh *FileHeader 139 } 140 141 func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) } 142 func (fi headerFileInfo) Size() int64 { 143 if fi.fh.UncompressedSize64 > 0 { 144 return int64(fi.fh.UncompressedSize64) 145 } 146 return int64(fi.fh.UncompressedSize) 147 } 148 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } 149 func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } 150 func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } 151 func (fi headerFileInfo) Sys() interface{} { return fi.fh } 152 153 // FileInfoHeader creates a partially-populated FileHeader from an 154 // os.FileInfo. 155 // Because os.FileInfo's Name method returns only the base name of 156 // the file it describes, it may be necessary to modify the Name field 157 // of the returned header to provide the full path name of the file. 158 // If compression is desired, callers should set the FileHeader.Method 159 // field; it is unset by default. 160 func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { 161 size := fi.Size() 162 fh := &FileHeader{ 163 Name: fi.Name(), 164 UncompressedSize64: uint64(size), 165 } 166 fh.SetModTime(fi.ModTime()) 167 fh.SetMode(fi.Mode()) 168 if fh.UncompressedSize64 > uint32max { 169 fh.UncompressedSize = uint32max 170 } else { 171 fh.UncompressedSize = uint32(fh.UncompressedSize64) 172 } 173 return fh, nil 174 } 175 176 type directoryEnd struct { 177 diskNbr uint32 // unused 178 dirDiskNbr uint32 // unused 179 dirRecordsThisDisk uint64 // unused 180 directoryRecords uint64 181 directorySize uint64 182 directoryOffset uint64 // relative to file 183 commentLen uint16 184 comment string 185 } 186 187 // timeZone returns a *time.Location based on the provided offset. 188 // If the offset is non-sensible, then this uses an offset of zero. 189 func timeZone(offset time.Duration) *time.Location { 190 const ( 191 minOffset = -12 * time.Hour // E.g., Baker island at -12:00 192 maxOffset = +14 * time.Hour // E.g., Line island at +14:00 193 offsetAlias = 15 * time.Minute // E.g., Nepal at +5:45 194 ) 195 offset = offset.Round(offsetAlias) 196 if offset < minOffset || maxOffset < offset { 197 offset = 0 198 } 199 return time.FixedZone("", int(offset/time.Second)) 200 } 201 202 // msDosTimeToTime converts an MS-DOS date and time into a time.Time. 203 // The resolution is 2s. 204 // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx 205 func msDosTimeToTime(dosDate, dosTime uint16) time.Time { 206 return time.Date( 207 // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 208 int(dosDate>>9+1980), 209 time.Month(dosDate>>5&0xf), 210 int(dosDate&0x1f), 211 212 // time bits 0-4: second/2; 5-10: minute; 11-15: hour 213 int(dosTime>>11), 214 int(dosTime>>5&0x3f), 215 int(dosTime&0x1f*2), 216 0, // nanoseconds 217 218 time.UTC, 219 ) 220 } 221 222 // timeToMsDosTime converts a time.Time to an MS-DOS date and time. 223 // The resolution is 2s. 224 // See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx 225 func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) { 226 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9) 227 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11) 228 return 229 } 230 231 // ModTime returns the modification time in UTC using the legacy 232 // ModifiedDate and ModifiedTime fields. 233 // 234 // Deprecated: Use Modified instead. 235 func (h *FileHeader) ModTime() time.Time { 236 return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) 237 } 238 239 // SetModTime sets the Modified, ModifiedTime, and ModifiedDate fields 240 // to the given time in UTC. 241 // 242 // Deprecated: Use Modified instead. 243 func (h *FileHeader) SetModTime(t time.Time) { 244 t = t.UTC() // Convert to UTC for compatibility 245 h.Modified = t 246 h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t) 247 } 248 249 const ( 250 // Unix constants. The specification doesn't mention them, 251 // but these seem to be the values agreed on by tools. 252 s_IFMT = 0xf000 253 s_IFSOCK = 0xc000 254 s_IFLNK = 0xa000 255 s_IFREG = 0x8000 256 s_IFBLK = 0x6000 257 s_IFDIR = 0x4000 258 s_IFCHR = 0x2000 259 s_IFIFO = 0x1000 260 s_ISUID = 0x800 261 s_ISGID = 0x400 262 s_ISVTX = 0x200 263 264 msdosDir = 0x10 265 msdosReadOnly = 0x01 266 ) 267 268 // Mode returns the permission and mode bits for the FileHeader. 269 func (h *FileHeader) Mode() (mode os.FileMode) { 270 switch h.CreatorVersion >> 8 { 271 case creatorUnix, creatorMacOSX: 272 mode = unixModeToFileMode(h.ExternalAttrs >> 16) 273 case creatorNTFS, creatorVFAT, creatorFAT: 274 mode = msdosModeToFileMode(h.ExternalAttrs) 275 } 276 if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' { 277 mode |= os.ModeDir 278 } 279 return mode 280 } 281 282 // SetMode changes the permission and mode bits for the FileHeader. 283 func (h *FileHeader) SetMode(mode os.FileMode) { 284 h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8 285 h.ExternalAttrs = fileModeToUnixMode(mode) << 16 286 287 // set MSDOS attributes too, as the original zip does. 288 if mode&os.ModeDir != 0 { 289 h.ExternalAttrs |= msdosDir 290 } 291 if mode&0200 == 0 { 292 h.ExternalAttrs |= msdosReadOnly 293 } 294 } 295 296 // isZip64 reports whether the file size exceeds the 32 bit limit 297 func (fh *FileHeader) isZip64() bool { 298 return fh.CompressedSize64 >= uint32max || fh.UncompressedSize64 >= uint32max 299 } 300 301 func msdosModeToFileMode(m uint32) (mode os.FileMode) { 302 if m&msdosDir != 0 { 303 mode = os.ModeDir | 0777 304 } else { 305 mode = 0666 306 } 307 if m&msdosReadOnly != 0 { 308 mode &^= 0222 309 } 310 return mode 311 } 312 313 func fileModeToUnixMode(mode os.FileMode) uint32 { 314 var m uint32 315 switch mode & os.ModeType { 316 default: 317 m = s_IFREG 318 case os.ModeDir: 319 m = s_IFDIR 320 case os.ModeSymlink: 321 m = s_IFLNK 322 case os.ModeNamedPipe: 323 m = s_IFIFO 324 case os.ModeSocket: 325 m = s_IFSOCK 326 case os.ModeDevice: 327 if mode&os.ModeCharDevice != 0 { 328 m = s_IFCHR 329 } else { 330 m = s_IFBLK 331 } 332 } 333 if mode&os.ModeSetuid != 0 { 334 m |= s_ISUID 335 } 336 if mode&os.ModeSetgid != 0 { 337 m |= s_ISGID 338 } 339 if mode&os.ModeSticky != 0 { 340 m |= s_ISVTX 341 } 342 return m | uint32(mode&0777) 343 } 344 345 func unixModeToFileMode(m uint32) os.FileMode { 346 mode := os.FileMode(m & 0777) 347 switch m & s_IFMT { 348 case s_IFBLK: 349 mode |= os.ModeDevice 350 case s_IFCHR: 351 mode |= os.ModeDevice | os.ModeCharDevice 352 case s_IFDIR: 353 mode |= os.ModeDir 354 case s_IFIFO: 355 mode |= os.ModeNamedPipe 356 case s_IFLNK: 357 mode |= os.ModeSymlink 358 case s_IFREG: 359 // nothing to do 360 case s_IFSOCK: 361 mode |= os.ModeSocket 362 } 363 if m&s_ISGID != 0 { 364 mode |= os.ModeSetgid 365 } 366 if m&s_ISUID != 0 { 367 mode |= os.ModeSetuid 368 } 369 if m&s_ISVTX != 0 { 370 mode |= os.ModeSticky 371 } 372 return mode 373 }