...
1
2
3
4
5
20 package zip
21
22 import (
23 "os"
24 "path"
25 "time"
26 )
27
28
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
41 fileHeaderLen = 30
42 directoryHeaderLen = 46
43 directoryEndLen = 22
44 dataDescriptorLen = 16
45 dataDescriptor64Len = 24
46 directory64LocLen = 20
47 directory64EndLen = 56
48
49
50 creatorFAT = 0
51 creatorUnix = 3
52 creatorNTFS = 11
53 creatorVFAT = 14
54 creatorMacOSX = 19
55
56
57 zipVersion20 = 20
58 zipVersion45 = 45
59
60
61 uint16max = (1 << 16) - 1
62 uint32max = (1 << 32) - 1
63
64
65
66
67
68
69
70
71
72
73 zip64ExtraID = 0x0001
74 ntfsExtraID = 0x000a
75 unixExtraID = 0x000d
76 extTimeExtraID = 0x5455
77 infoZipUnixExtraID = 0x5855
78 )
79
80
81
82 type FileHeader struct {
83
84
85
86 Name string
87
88
89 Comment string
90
91
92
93
94
95
96
97
98
99
100 NonUTF8 bool
101
102 CreatorVersion uint16
103 ReaderVersion uint16
104 Flags uint16
105
106
107 Method uint16
108
109
110
111
112
113
114
115
116
117
118 Modified time.Time
119 ModifiedTime uint16
120 ModifiedDate uint16
121
122 CRC32 uint32
123 CompressedSize uint32
124 UncompressedSize uint32
125 CompressedSize64 uint64
126 UncompressedSize64 uint64
127 Extra []byte
128 ExternalAttrs uint32
129 }
130
131
132 func (h *FileHeader) FileInfo() os.FileInfo {
133 return headerFileInfo{h}
134 }
135
136
137 type headerFileInfo struct {
138 fh *FileHeader
139 }
140
141 func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
142 func (fi headerFileInfo) Size() int64 {
143 if fi.fh.UncompressedSize64 > 0 {
144 return int64(fi.fh.UncompressedSize64)
145 }
146 return int64(fi.fh.UncompressedSize)
147 }
148 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
149 func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
150 func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
151 func (fi headerFileInfo) Sys() interface{} { return fi.fh }
152
153
154
155
156
157
158
159
160 func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
161 size := fi.Size()
162 fh := &FileHeader{
163 Name: fi.Name(),
164 UncompressedSize64: uint64(size),
165 }
166 fh.SetModTime(fi.ModTime())
167 fh.SetMode(fi.Mode())
168 if fh.UncompressedSize64 > uint32max {
169 fh.UncompressedSize = uint32max
170 } else {
171 fh.UncompressedSize = uint32(fh.UncompressedSize64)
172 }
173 return fh, nil
174 }
175
176 type directoryEnd struct {
177 diskNbr uint32
178 dirDiskNbr uint32
179 dirRecordsThisDisk uint64
180 directoryRecords uint64
181 directorySize uint64
182 directoryOffset uint64
183 commentLen uint16
184 comment string
185 }
186
187
188
189 func timeZone(offset time.Duration) *time.Location {
190 const (
191 minOffset = -12 * time.Hour
192 maxOffset = +14 * time.Hour
193 offsetAlias = 15 * time.Minute
194 )
195 offset = offset.Round(offsetAlias)
196 if offset < minOffset || maxOffset < offset {
197 offset = 0
198 }
199 return time.FixedZone("", int(offset/time.Second))
200 }
201
202
203
204
205 func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
206 return time.Date(
207
208 int(dosDate>>9+1980),
209 time.Month(dosDate>>5&0xf),
210 int(dosDate&0x1f),
211
212
213 int(dosTime>>11),
214 int(dosTime>>5&0x3f),
215 int(dosTime&0x1f*2),
216 0,
217
218 time.UTC,
219 )
220 }
221
222
223
224
225 func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
226 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
227 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
228 return
229 }
230
231
232
233
234
235 func (h *FileHeader) ModTime() time.Time {
236 return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
237 }
238
239
240
241
242
243 func (h *FileHeader) SetModTime(t time.Time) {
244 t = t.UTC()
245 h.Modified = t
246 h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
247 }
248
249 const (
250
251
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
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
283 func (h *FileHeader) SetMode(mode os.FileMode) {
284 h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
285 h.ExternalAttrs = fileModeToUnixMode(mode) << 16
286
287
288 if mode&os.ModeDir != 0 {
289 h.ExternalAttrs |= msdosDir
290 }
291 if mode&0200 == 0 {
292 h.ExternalAttrs |= msdosReadOnly
293 }
294 }
295
296
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
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 }
374
View as plain text