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