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: http://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 } 314