...
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
87
88
89
90
91
92
93
94
95 Name string
96
97
98 Comment string
99
100
101
102
103
104
105
106
107
108
109 NonUTF8 bool
110
111 CreatorVersion uint16
112 ReaderVersion uint16
113 Flags uint16
114
115
116 Method uint16
117
118
119
120
121
122
123
124
125
126
127 Modified time.Time
128 ModifiedTime uint16
129 ModifiedDate uint16
130
131 CRC32 uint32
132 CompressedSize uint32
133 UncompressedSize uint32
134 CompressedSize64 uint64
135 UncompressedSize64 uint64
136 Extra []byte
137 ExternalAttrs uint32
138 }
139
140
141 func (h *FileHeader) FileInfo() os.FileInfo {
142 return headerFileInfo{h}
143 }
144
145
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 { return fi.fh.ModTime() }
159 func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
160 func (fi headerFileInfo) Sys() interface{} { return fi.fh }
161
162
163
164
165
166
167
168
169 func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
170 size := fi.Size()
171 fh := &FileHeader{
172 Name: fi.Name(),
173 UncompressedSize64: uint64(size),
174 }
175 fh.SetModTime(fi.ModTime())
176 fh.SetMode(fi.Mode())
177 if fh.UncompressedSize64 > uint32max {
178 fh.UncompressedSize = uint32max
179 } else {
180 fh.UncompressedSize = uint32(fh.UncompressedSize64)
181 }
182 return fh, nil
183 }
184
185 type directoryEnd struct {
186 diskNbr uint32
187 dirDiskNbr uint32
188 dirRecordsThisDisk uint64
189 directoryRecords uint64
190 directorySize uint64
191 directoryOffset uint64
192 commentLen uint16
193 comment string
194 }
195
196
197
198 func timeZone(offset time.Duration) *time.Location {
199 const (
200 minOffset = -12 * time.Hour
201 maxOffset = +14 * time.Hour
202 offsetAlias = 15 * time.Minute
203 )
204 offset = offset.Round(offsetAlias)
205 if offset < minOffset || maxOffset < offset {
206 offset = 0
207 }
208 return time.FixedZone("", int(offset/time.Second))
209 }
210
211
212
213
214 func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
215 return time.Date(
216
217 int(dosDate>>9+1980),
218 time.Month(dosDate>>5&0xf),
219 int(dosDate&0x1f),
220
221
222 int(dosTime>>11),
223 int(dosTime>>5&0x3f),
224 int(dosTime&0x1f*2),
225 0,
226
227 time.UTC,
228 )
229 }
230
231
232
233
234 func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
235 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
236 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
237 return
238 }
239
240
241
242
243
244 func (h *FileHeader) ModTime() time.Time {
245 return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
246 }
247
248
249
250
251
252 func (h *FileHeader) SetModTime(t time.Time) {
253 t = t.UTC()
254 h.Modified = t
255 h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
256 }
257
258 const (
259
260
261 s_IFMT = 0xf000
262 s_IFSOCK = 0xc000
263 s_IFLNK = 0xa000
264 s_IFREG = 0x8000
265 s_IFBLK = 0x6000
266 s_IFDIR = 0x4000
267 s_IFCHR = 0x2000
268 s_IFIFO = 0x1000
269 s_ISUID = 0x800
270 s_ISGID = 0x400
271 s_ISVTX = 0x200
272
273 msdosDir = 0x10
274 msdosReadOnly = 0x01
275 )
276
277
278 func (h *FileHeader) Mode() (mode os.FileMode) {
279 switch h.CreatorVersion >> 8 {
280 case creatorUnix, creatorMacOSX:
281 mode = unixModeToFileMode(h.ExternalAttrs >> 16)
282 case creatorNTFS, creatorVFAT, creatorFAT:
283 mode = msdosModeToFileMode(h.ExternalAttrs)
284 }
285 if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
286 mode |= os.ModeDir
287 }
288 return mode
289 }
290
291
292 func (h *FileHeader) SetMode(mode os.FileMode) {
293 h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
294 h.ExternalAttrs = fileModeToUnixMode(mode) << 16
295
296
297 if mode&os.ModeDir != 0 {
298 h.ExternalAttrs |= msdosDir
299 }
300 if mode&0200 == 0 {
301 h.ExternalAttrs |= msdosReadOnly
302 }
303 }
304
305
306 func (fh *FileHeader) isZip64() bool {
307 return fh.CompressedSize64 >= uint32max || fh.UncompressedSize64 >= uint32max
308 }
309
310 func msdosModeToFileMode(m uint32) (mode os.FileMode) {
311 if m&msdosDir != 0 {
312 mode = os.ModeDir | 0777
313 } else {
314 mode = 0666
315 }
316 if m&msdosReadOnly != 0 {
317 mode &^= 0222
318 }
319 return mode
320 }
321
322 func fileModeToUnixMode(mode os.FileMode) uint32 {
323 var m uint32
324 switch mode & os.ModeType {
325 default:
326 m = s_IFREG
327 case os.ModeDir:
328 m = s_IFDIR
329 case os.ModeSymlink:
330 m = s_IFLNK
331 case os.ModeNamedPipe:
332 m = s_IFIFO
333 case os.ModeSocket:
334 m = s_IFSOCK
335 case os.ModeDevice:
336 if mode&os.ModeCharDevice != 0 {
337 m = s_IFCHR
338 } else {
339 m = s_IFBLK
340 }
341 }
342 if mode&os.ModeSetuid != 0 {
343 m |= s_ISUID
344 }
345 if mode&os.ModeSetgid != 0 {
346 m |= s_ISGID
347 }
348 if mode&os.ModeSticky != 0 {
349 m |= s_ISVTX
350 }
351 return m | uint32(mode&0777)
352 }
353
354 func unixModeToFileMode(m uint32) os.FileMode {
355 mode := os.FileMode(m & 0777)
356 switch m & s_IFMT {
357 case s_IFBLK:
358 mode |= os.ModeDevice
359 case s_IFCHR:
360 mode |= os.ModeDevice | os.ModeCharDevice
361 case s_IFDIR:
362 mode |= os.ModeDir
363 case s_IFIFO:
364 mode |= os.ModeNamedPipe
365 case s_IFLNK:
366 mode |= os.ModeSymlink
367 case s_IFREG:
368
369 case s_IFSOCK:
370 mode |= os.ModeSocket
371 }
372 if m&s_ISGID != 0 {
373 mode |= os.ModeSetgid
374 }
375 if m&s_ISUID != 0 {
376 mode |= os.ModeSetuid
377 }
378 if m&s_ISVTX != 0 {
379 mode |= os.ModeSticky
380 }
381 return mode
382 }
383
View as plain text