github.com/panjjo/go@v0.0.0-20161104043856-d62b31386338/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/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 ntfsExtraId = 0x000a // NTFS Extra Field 67 unixExtraId = 0x000d // UNIX Extra Field 68 exttsExtraId = 0x5455 // Extended Timestamp Extra Field 69 ) 70 71 // FileHeader describes a file within a zip file. 72 // See the zip spec for details. 73 type FileHeader struct { 74 // Name is the name of the file. 75 // It must be a relative path: it must not start with a drive 76 // letter (e.g. C:) or leading slash, and only forward slashes 77 // are allowed. 78 Name string 79 80 CreatorVersion uint16 81 ReaderVersion uint16 82 Flags uint16 83 Method uint16 84 ModifiedTime uint16 // MS-DOS time 85 ModifiedDate uint16 // MS-DOS date 86 CRC32 uint32 87 CompressedSize uint32 // Deprecated: Use CompressedSize64 instead. 88 UncompressedSize uint32 // Deprecated: Use UncompressedSize64 instead. 89 CompressedSize64 uint64 90 UncompressedSize64 uint64 91 Extra []byte 92 ExternalAttrs uint32 // Meaning depends on CreatorVersion 93 Comment string 94 } 95 96 // FileInfo returns an os.FileInfo for the FileHeader. 97 func (h *FileHeader) FileInfo() os.FileInfo { 98 return headerFileInfo{h} 99 } 100 101 // headerFileInfo implements os.FileInfo. 102 type headerFileInfo struct { 103 fh *FileHeader 104 } 105 106 func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) } 107 func (fi headerFileInfo) Size() int64 { 108 if fi.fh.UncompressedSize64 > 0 { 109 return int64(fi.fh.UncompressedSize64) 110 } 111 return int64(fi.fh.UncompressedSize) 112 } 113 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } 114 func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } 115 func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } 116 func (fi headerFileInfo) Sys() interface{} { return fi.fh } 117 118 // FileInfoHeader creates a partially-populated FileHeader from an 119 // os.FileInfo. 120 // Because os.FileInfo's Name method returns only the base name of 121 // the file it describes, it may be necessary to modify the Name field 122 // of the returned header to provide the full path name of the file. 123 func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { 124 size := fi.Size() 125 fh := &FileHeader{ 126 Name: fi.Name(), 127 UncompressedSize64: uint64(size), 128 } 129 fh.SetModTime(fi.ModTime()) 130 fh.SetMode(fi.Mode()) 131 if fh.UncompressedSize64 > uint32max { 132 fh.UncompressedSize = uint32max 133 } else { 134 fh.UncompressedSize = uint32(fh.UncompressedSize64) 135 } 136 return fh, nil 137 } 138 139 type directoryEnd struct { 140 diskNbr uint32 // unused 141 dirDiskNbr uint32 // unused 142 dirRecordsThisDisk uint64 // unused 143 directoryRecords uint64 144 directorySize uint64 145 directoryOffset uint64 // relative to file 146 commentLen uint16 147 comment string 148 } 149 150 // msDosTimeToTime converts an MS-DOS date and time into a time.Time. 151 // The resolution is 2s. 152 // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx 153 func msDosTimeToTime(dosDate, dosTime uint16) time.Time { 154 return time.Date( 155 // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 156 int(dosDate>>9+1980), 157 time.Month(dosDate>>5&0xf), 158 int(dosDate&0x1f), 159 160 // time bits 0-4: second/2; 5-10: minute; 11-15: hour 161 int(dosTime>>11), 162 int(dosTime>>5&0x3f), 163 int(dosTime&0x1f*2), 164 0, // nanoseconds 165 166 time.UTC, 167 ) 168 } 169 170 // timeToMsDosTime converts a time.Time to an MS-DOS date and time. 171 // The resolution is 2s. 172 // See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx 173 func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) { 174 t = t.In(time.UTC) 175 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9) 176 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11) 177 return 178 } 179 180 // ModTime returns the modification time in UTC. 181 // The resolution is 2s. 182 func (h *FileHeader) ModTime() time.Time { 183 return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) 184 } 185 186 // SetModTime sets the ModifiedTime and ModifiedDate fields to the given time in UTC. 187 // The resolution is 2s. 188 func (h *FileHeader) SetModTime(t time.Time) { 189 h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t) 190 } 191 192 const ( 193 // Unix constants. The specification doesn't mention them, 194 // but these seem to be the values agreed on by tools. 195 s_IFMT = 0xf000 196 s_IFSOCK = 0xc000 197 s_IFLNK = 0xa000 198 s_IFREG = 0x8000 199 s_IFBLK = 0x6000 200 s_IFDIR = 0x4000 201 s_IFCHR = 0x2000 202 s_IFIFO = 0x1000 203 s_ISUID = 0x800 204 s_ISGID = 0x400 205 s_ISVTX = 0x200 206 207 msdosDir = 0x10 208 msdosReadOnly = 0x01 209 ) 210 211 // Mode returns the permission and mode bits for the FileHeader. 212 func (h *FileHeader) Mode() (mode os.FileMode) { 213 switch h.CreatorVersion >> 8 { 214 case creatorUnix, creatorMacOSX: 215 mode = unixModeToFileMode(h.ExternalAttrs >> 16) 216 case creatorNTFS, creatorVFAT, creatorFAT: 217 mode = msdosModeToFileMode(h.ExternalAttrs) 218 } 219 if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' { 220 mode |= os.ModeDir 221 } 222 return mode 223 } 224 225 // SetMode changes the permission and mode bits for the FileHeader. 226 func (h *FileHeader) SetMode(mode os.FileMode) { 227 h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8 228 h.ExternalAttrs = fileModeToUnixMode(mode) << 16 229 230 // set MSDOS attributes too, as the original zip does. 231 if mode&os.ModeDir != 0 { 232 h.ExternalAttrs |= msdosDir 233 } 234 if mode&0200 == 0 { 235 h.ExternalAttrs |= msdosReadOnly 236 } 237 } 238 239 // isZip64 reports whether the file size exceeds the 32 bit limit 240 func (fh *FileHeader) isZip64() bool { 241 return fh.CompressedSize64 >= uint32max || fh.UncompressedSize64 >= uint32max 242 } 243 244 func msdosModeToFileMode(m uint32) (mode os.FileMode) { 245 if m&msdosDir != 0 { 246 mode = os.ModeDir | 0777 247 } else { 248 mode = 0666 249 } 250 if m&msdosReadOnly != 0 { 251 mode &^= 0222 252 } 253 return mode 254 } 255 256 func fileModeToUnixMode(mode os.FileMode) uint32 { 257 var m uint32 258 switch mode & os.ModeType { 259 default: 260 m = s_IFREG 261 case os.ModeDir: 262 m = s_IFDIR 263 case os.ModeSymlink: 264 m = s_IFLNK 265 case os.ModeNamedPipe: 266 m = s_IFIFO 267 case os.ModeSocket: 268 m = s_IFSOCK 269 case os.ModeDevice: 270 if mode&os.ModeCharDevice != 0 { 271 m = s_IFCHR 272 } else { 273 m = s_IFBLK 274 } 275 } 276 if mode&os.ModeSetuid != 0 { 277 m |= s_ISUID 278 } 279 if mode&os.ModeSetgid != 0 { 280 m |= s_ISGID 281 } 282 if mode&os.ModeSticky != 0 { 283 m |= s_ISVTX 284 } 285 return m | uint32(mode&0777) 286 } 287 288 func unixModeToFileMode(m uint32) os.FileMode { 289 mode := os.FileMode(m & 0777) 290 switch m & s_IFMT { 291 case s_IFBLK: 292 mode |= os.ModeDevice 293 case s_IFCHR: 294 mode |= os.ModeDevice | os.ModeCharDevice 295 case s_IFDIR: 296 mode |= os.ModeDir 297 case s_IFIFO: 298 mode |= os.ModeNamedPipe 299 case s_IFLNK: 300 mode |= os.ModeSymlink 301 case s_IFREG: 302 // nothing to do 303 case s_IFSOCK: 304 mode |= os.ModeSocket 305 } 306 if m&s_ISGID != 0 { 307 mode |= os.ModeSetgid 308 } 309 if m&s_ISUID != 0 { 310 mode |= os.ModeSetuid 311 } 312 if m&s_ISVTX != 0 { 313 mode |= os.ModeSticky 314 } 315 return mode 316 }