github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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 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 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 Method uint16 106 107 // Modified is the modified time of the file. 108 // 109 // When reading, an extended timestamp is preferred over the legacy MS-DOS 110 // date field, and the offset between the times is used as the timezone. 111 // If only the MS-DOS date is present, the timezone is assumed to be UTC. 112 // 113 // When writing, an extended timestamp (which is timezone-agnostic) is 114 // always emitted. The legacy MS-DOS date field is encoded according to the 115 // location of the Modified time. 116 Modified time.Time 117 ModifiedTime uint16 // Deprecated: Legacy MS-DOS date; use Modified instead. 118 ModifiedDate uint16 // Deprecated: Legacy MS-DOS time; use Modified instead. 119 120 CRC32 uint32 121 CompressedSize uint32 // Deprecated: Use CompressedSize64 instead. 122 UncompressedSize uint32 // Deprecated: Use UncompressedSize64 instead. 123 CompressedSize64 uint64 124 UncompressedSize64 uint64 125 Extra []byte 126 ExternalAttrs uint32 // Meaning depends on CreatorVersion 127 } 128 129 // FileInfo returns an os.FileInfo for the FileHeader. 130 func (h *FileHeader) FileInfo() os.FileInfo { 131 return headerFileInfo{h} 132 } 133 134 // headerFileInfo implements os.FileInfo. 135 type headerFileInfo struct { 136 fh *FileHeader 137 } 138 139 func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) } 140 func (fi headerFileInfo) Size() int64 { 141 if fi.fh.UncompressedSize64 > 0 { 142 return int64(fi.fh.UncompressedSize64) 143 } 144 return int64(fi.fh.UncompressedSize) 145 } 146 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } 147 func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } 148 func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } 149 func (fi headerFileInfo) Sys() interface{} { return fi.fh } 150 151 // FileInfoHeader creates a partially-populated FileHeader from an 152 // os.FileInfo. 153 // Because os.FileInfo's Name method returns only the base name of 154 // the file it describes, it may be necessary to modify the Name field 155 // of the returned header to provide the full path name of the file. 156 func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { 157 size := fi.Size() 158 fh := &FileHeader{ 159 Name: fi.Name(), 160 UncompressedSize64: uint64(size), 161 } 162 fh.SetModTime(fi.ModTime()) 163 fh.SetMode(fi.Mode()) 164 if fh.UncompressedSize64 > uint32max { 165 fh.UncompressedSize = uint32max 166 } else { 167 fh.UncompressedSize = uint32(fh.UncompressedSize64) 168 } 169 return fh, nil 170 } 171 172 type directoryEnd struct { 173 diskNbr uint32 // unused 174 dirDiskNbr uint32 // unused 175 dirRecordsThisDisk uint64 // unused 176 directoryRecords uint64 177 directorySize uint64 178 directoryOffset uint64 // relative to file 179 commentLen uint16 180 comment string 181 } 182 183 // timeZone returns a *time.Location based on the provided offset. 184 // If the offset is non-sensible, then this uses an offset of zero. 185 func timeZone(offset time.Duration) *time.Location { 186 const ( 187 minOffset = -12 * time.Hour // E.g., Baker island at -12:00 188 maxOffset = +14 * time.Hour // E.g., Line island at +14:00 189 offsetAlias = 15 * time.Minute // E.g., Nepal at +5:45 190 ) 191 offset = offset.Round(offsetAlias) 192 if offset < minOffset || maxOffset < offset { 193 offset = 0 194 } 195 return time.FixedZone("", int(offset/time.Second)) 196 } 197 198 // msDosTimeToTime converts an MS-DOS date and time into a time.Time. 199 // The resolution is 2s. 200 // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx 201 func msDosTimeToTime(dosDate, dosTime uint16) time.Time { 202 return time.Date( 203 // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 204 int(dosDate>>9+1980), 205 time.Month(dosDate>>5&0xf), 206 int(dosDate&0x1f), 207 208 // time bits 0-4: second/2; 5-10: minute; 11-15: hour 209 int(dosTime>>11), 210 int(dosTime>>5&0x3f), 211 int(dosTime&0x1f*2), 212 0, // nanoseconds 213 214 time.UTC, 215 ) 216 } 217 218 // timeToMsDosTime converts a time.Time to an MS-DOS date and time. 219 // The resolution is 2s. 220 // See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx 221 func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) { 222 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9) 223 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11) 224 return 225 } 226 227 // ModTime returns the modification time in UTC. 228 // This returns Modified if non-zero, otherwise it computes the timestamp 229 // from the legacy ModifiedDate and ModifiedTime fields. 230 // 231 // Deprecated: Use Modified instead. 232 func (h *FileHeader) ModTime() time.Time { 233 if !h.Modified.IsZero() { 234 return h.Modified.UTC() // Convert to UTC for compatibility 235 } 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 }