Source file src/pkg/archive/zip/struct.go
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 ZIP64 or disk spanning. 11 */ 12 package zip 13 14 import ( 15 "errors" 16 "os" 17 "time" 18 ) 19 20 // Compression methods. 21 const ( 22 Store uint16 = 0 23 Deflate uint16 = 8 24 ) 25 26 const ( 27 fileHeaderSignature = 0x04034b50 28 directoryHeaderSignature = 0x02014b50 29 directoryEndSignature = 0x06054b50 30 dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder 31 fileHeaderLen = 30 // + filename + extra 32 directoryHeaderLen = 46 // + filename + extra + comment 33 directoryEndLen = 22 // + comment 34 dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size 35 36 // Constants for the first byte in CreatorVersion 37 creatorFAT = 0 38 creatorUnix = 3 39 creatorNTFS = 11 40 creatorVFAT = 14 41 creatorMacOSX = 19 42 ) 43 44 type FileHeader struct { 45 Name string 46 CreatorVersion uint16 47 ReaderVersion uint16 48 Flags uint16 49 Method uint16 50 ModifiedTime uint16 // MS-DOS time 51 ModifiedDate uint16 // MS-DOS date 52 CRC32 uint32 53 CompressedSize uint32 54 UncompressedSize uint32 55 Extra []byte 56 ExternalAttrs uint32 // Meaning depends on CreatorVersion 57 Comment string 58 } 59 60 // FileInfo returns an os.FileInfo for the FileHeader. 61 func (h *FileHeader) FileInfo() os.FileInfo { 62 return headerFileInfo{h} 63 } 64 65 // headerFileInfo implements os.FileInfo. 66 type headerFileInfo struct { 67 fh *FileHeader 68 } 69 70 func (fi headerFileInfo) Name() string { return fi.fh.Name } 71 func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) } 72 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } 73 func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } 74 func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } 75 func (fi headerFileInfo) Sys() interface{} { return fi.fh } 76 77 // FileInfoHeader creates a partially-populated FileHeader from an 78 // os.FileInfo. 79 func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { 80 size := fi.Size() 81 if size > (1<<32 - 1) { 82 return nil, errors.New("zip: file over 4GB") 83 } 84 fh := &FileHeader{ 85 Name: fi.Name(), 86 UncompressedSize: uint32(size), 87 } 88 fh.SetModTime(fi.ModTime()) 89 fh.SetMode(fi.Mode()) 90 return fh, nil 91 } 92 93 type directoryEnd struct { 94 diskNbr uint16 // unused 95 dirDiskNbr uint16 // unused 96 dirRecordsThisDisk uint16 // unused 97 directoryRecords uint16 98 directorySize uint32 99 directoryOffset uint32 // relative to file 100 commentLen uint16 101 comment string 102 } 103 104 // msDosTimeToTime converts an MS-DOS date and time into a time.Time. 105 // The resolution is 2s. 106 // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx 107 func msDosTimeToTime(dosDate, dosTime uint16) time.Time { 108 return time.Date( 109 // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 110 int(dosDate>>9+1980), 111 time.Month(dosDate>>5&0xf), 112 int(dosDate&0x1f), 113 114 // time bits 0-4: second/2; 5-10: minute; 11-15: hour 115 int(dosTime>>11), 116 int(dosTime>>5&0x3f), 117 int(dosTime&0x1f*2), 118 0, // nanoseconds 119 120 time.UTC, 121 ) 122 } 123 124 // timeToMsDosTime converts a time.Time to an MS-DOS date and time. 125 // The resolution is 2s. 126 // See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx 127 func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) { 128 t = t.In(time.UTC) 129 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9) 130 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11) 131 return 132 } 133 134 // ModTime returns the modification time. 135 // The resolution is 2s. 136 func (h *FileHeader) ModTime() time.Time { 137 return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) 138 } 139 140 // SetModTime sets the ModifiedTime and ModifiedDate fields to the given time. 141 // The resolution is 2s. 142 func (h *FileHeader) SetModTime(t time.Time) { 143 h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t) 144 } 145 146 const ( 147 // Unix constants. The specification doesn't mention them, 148 // but these seem to be the values agreed on by tools. 149 s_IFMT = 0xf000 150 s_IFSOCK = 0xc000 151 s_IFLNK = 0xa000 152 s_IFREG = 0x8000 153 s_IFBLK = 0x6000 154 s_IFDIR = 0x4000 155 s_IFCHR = 0x2000 156 s_IFIFO = 0x1000 157 s_ISUID = 0x800 158 s_ISGID = 0x400 159 s_ISVTX = 0x200 160 161 msdosDir = 0x10 162 msdosReadOnly = 0x01 163 ) 164 165 // Mode returns the permission and mode bits for the FileHeader. 166 func (h *FileHeader) Mode() (mode os.FileMode) { 167 switch h.CreatorVersion >> 8 { 168 case creatorUnix, creatorMacOSX: 169 mode = unixModeToFileMode(h.ExternalAttrs >> 16) 170 case creatorNTFS, creatorVFAT, creatorFAT: 171 mode = msdosModeToFileMode(h.ExternalAttrs) 172 } 173 if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' { 174 mode |= os.ModeDir 175 } 176 return mode 177 } 178 179 // SetMode changes the permission and mode bits for the FileHeader. 180 func (h *FileHeader) SetMode(mode os.FileMode) { 181 h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8 182 h.ExternalAttrs = fileModeToUnixMode(mode) << 16 183 184 // set MSDOS attributes too, as the original zip does. 185 if mode&os.ModeDir != 0 { 186 h.ExternalAttrs |= msdosDir 187 } 188 if mode&0200 == 0 { 189 h.ExternalAttrs |= msdosReadOnly 190 } 191 } 192 193 func msdosModeToFileMode(m uint32) (mode os.FileMode) { 194 if m&msdosDir != 0 { 195 mode = os.ModeDir | 0777 196 } else { 197 mode = 0666 198 } 199 if m&msdosReadOnly != 0 { 200 mode &^= 0222 201 } 202 return mode 203 } 204 205 func fileModeToUnixMode(mode os.FileMode) uint32 { 206 var m uint32 207 switch mode & os.ModeType { 208 default: 209 m = s_IFREG 210 case os.ModeDir: 211 m = s_IFDIR 212 case os.ModeSymlink: 213 m = s_IFLNK 214 case os.ModeNamedPipe: 215 m = s_IFIFO 216 case os.ModeSocket: 217 m = s_IFSOCK 218 case os.ModeDevice: 219 if mode&os.ModeCharDevice != 0 { 220 m = s_IFCHR 221 } else { 222 m = s_IFBLK 223 } 224 } 225 if mode&os.ModeSetuid != 0 { 226 m |= s_ISUID 227 } 228 if mode&os.ModeSetgid != 0 { 229 m |= s_ISGID 230 } 231 if mode&os.ModeSticky != 0 { 232 m |= s_ISVTX 233 } 234 return m | uint32(mode&0777) 235 } 236 237 func unixModeToFileMode(m uint32) os.FileMode { 238 mode := os.FileMode(m & 0777) 239 switch m & s_IFMT { 240 case s_IFBLK: 241 mode |= os.ModeDevice 242 case s_IFCHR: 243 mode |= os.ModeDevice | os.ModeCharDevice 244 case s_IFDIR: 245 mode |= os.ModeDir 246 case s_IFIFO: 247 mode |= os.ModeNamedPipe 248 case s_IFLNK: 249 mode |= os.ModeSymlink 250 case s_IFREG: 251 // nothing to do 252 case s_IFSOCK: 253 mode |= os.ModeSocket 254 } 255 if m&s_ISGID != 0 { 256 mode |= os.ModeSetgid 257 } 258 if m&s_ISUID != 0 { 259 mode |= os.ModeSetuid 260 } 261 if m&s_ISVTX != 0 { 262 mode |= os.ModeSticky 263 } 264 return mode 265 }