...
1
2
3
4
5
22 package zip
23
24 import (
25 "io/fs"
26 "path"
27 "time"
28 )
29
30
31 const (
32 Store uint16 = 0
33 Deflate uint16 = 8
34 )
35
36 const (
37 fileHeaderSignature = 0x04034b50
38 directoryHeaderSignature = 0x02014b50
39 directoryEndSignature = 0x06054b50
40 directory64LocSignature = 0x07064b50
41 directory64EndSignature = 0x06064b50
42 dataDescriptorSignature = 0x08074b50
43 fileHeaderLen = 30
44 directoryHeaderLen = 46
45 directoryEndLen = 22
46 dataDescriptorLen = 16
47 dataDescriptor64Len = 24
48 directory64LocLen = 20
49 directory64EndLen = 56
50
51
52 creatorFAT = 0
53 creatorUnix = 3
54 creatorNTFS = 11
55 creatorVFAT = 14
56 creatorMacOSX = 19
57
58
59 zipVersion20 = 20
60 zipVersion45 = 45
61
62
63 uint16max = (1 << 16) - 1
64 uint32max = (1 << 32) - 1
65
66
67
68
69
70
71
72
73
74
75 zip64ExtraID = 0x0001
76 ntfsExtraID = 0x000a
77 unixExtraID = 0x000d
78 extTimeExtraID = 0x5455
79 infoZipUnixExtraID = 0x5855
80 )
81
82
83
84
85
86 type FileHeader struct {
87
88
89
90
91
92 Name string
93
94
95 Comment string
96
97
98
99
100
101
102
103
104
105
106 NonUTF8 bool
107
108 CreatorVersion uint16
109 ReaderVersion uint16
110 Flags uint16
111
112
113 Method uint16
114
115
116
117
118
119
120
121
122
123
124 Modified time.Time
125
126
127
128
129 ModifiedTime uint16
130
131
132
133
134 ModifiedDate uint16
135
136
137 CRC32 uint32
138
139
140
141
142
143
144 CompressedSize uint32
145
146
147
148
149
150
151 UncompressedSize uint32
152
153
154 CompressedSize64 uint64
155
156
157 UncompressedSize64 uint64
158
159
160
161
162 Extra []byte
163
164 ExternalAttrs uint32
165 }
166
167
168 func (h *FileHeader) FileInfo() fs.FileInfo {
169 return headerFileInfo{h}
170 }
171
172
173 type headerFileInfo struct {
174 fh *FileHeader
175 }
176
177 func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
178 func (fi headerFileInfo) Size() int64 {
179 if fi.fh.UncompressedSize64 > 0 {
180 return int64(fi.fh.UncompressedSize64)
181 }
182 return int64(fi.fh.UncompressedSize)
183 }
184 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
185 func (fi headerFileInfo) ModTime() time.Time {
186 if fi.fh.Modified.IsZero() {
187 return fi.fh.ModTime()
188 }
189 return fi.fh.Modified.UTC()
190 }
191 func (fi headerFileInfo) Mode() fs.FileMode { return fi.fh.Mode() }
192 func (fi headerFileInfo) Type() fs.FileMode { return fi.fh.Mode().Type() }
193 func (fi headerFileInfo) Sys() any { return fi.fh }
194
195 func (fi headerFileInfo) Info() (fs.FileInfo, error) { return fi, nil }
196
197 func (fi headerFileInfo) String() string {
198 return fs.FormatFileInfo(fi)
199 }
200
201
202
203
204
205
206
207
208 func FileInfoHeader(fi fs.FileInfo) (*FileHeader, error) {
209 size := fi.Size()
210 fh := &FileHeader{
211 Name: fi.Name(),
212 UncompressedSize64: uint64(size),
213 }
214 fh.SetModTime(fi.ModTime())
215 fh.SetMode(fi.Mode())
216 if fh.UncompressedSize64 > uint32max {
217 fh.UncompressedSize = uint32max
218 } else {
219 fh.UncompressedSize = uint32(fh.UncompressedSize64)
220 }
221 return fh, nil
222 }
223
224 type directoryEnd struct {
225 diskNbr uint32
226 dirDiskNbr uint32
227 dirRecordsThisDisk uint64
228 directoryRecords uint64
229 directorySize uint64
230 directoryOffset uint64
231 commentLen uint16
232 comment string
233 }
234
235
236
237 func timeZone(offset time.Duration) *time.Location {
238 const (
239 minOffset = -12 * time.Hour
240 maxOffset = +14 * time.Hour
241 offsetAlias = 15 * time.Minute
242 )
243 offset = offset.Round(offsetAlias)
244 if offset < minOffset || maxOffset < offset {
245 offset = 0
246 }
247 return time.FixedZone("", int(offset/time.Second))
248 }
249
250
251
252
253 func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
254 return time.Date(
255
256 int(dosDate>>9+1980),
257 time.Month(dosDate>>5&0xf),
258 int(dosDate&0x1f),
259
260
261 int(dosTime>>11),
262 int(dosTime>>5&0x3f),
263 int(dosTime&0x1f*2),
264 0,
265
266 time.UTC,
267 )
268 }
269
270
271
272
273 func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
274 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
275 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
276 return
277 }
278
279
280
281
282
283 func (h *FileHeader) ModTime() time.Time {
284 return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
285 }
286
287
288
289
290
291 func (h *FileHeader) SetModTime(t time.Time) {
292 t = t.UTC()
293 h.Modified = t
294 h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
295 }
296
297 const (
298
299
300 s_IFMT = 0xf000
301 s_IFSOCK = 0xc000
302 s_IFLNK = 0xa000
303 s_IFREG = 0x8000
304 s_IFBLK = 0x6000
305 s_IFDIR = 0x4000
306 s_IFCHR = 0x2000
307 s_IFIFO = 0x1000
308 s_ISUID = 0x800
309 s_ISGID = 0x400
310 s_ISVTX = 0x200
311
312 msdosDir = 0x10
313 msdosReadOnly = 0x01
314 )
315
316
317 func (h *FileHeader) Mode() (mode fs.FileMode) {
318 switch h.CreatorVersion >> 8 {
319 case creatorUnix, creatorMacOSX:
320 mode = unixModeToFileMode(h.ExternalAttrs >> 16)
321 case creatorNTFS, creatorVFAT, creatorFAT:
322 mode = msdosModeToFileMode(h.ExternalAttrs)
323 }
324 if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
325 mode |= fs.ModeDir
326 }
327 return mode
328 }
329
330
331 func (h *FileHeader) SetMode(mode fs.FileMode) {
332 h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
333 h.ExternalAttrs = fileModeToUnixMode(mode) << 16
334
335
336 if mode&fs.ModeDir != 0 {
337 h.ExternalAttrs |= msdosDir
338 }
339 if mode&0200 == 0 {
340 h.ExternalAttrs |= msdosReadOnly
341 }
342 }
343
344 func (h *FileHeader) hasDataDescriptor() bool {
345 return h.Flags&0x8 != 0
346 }
347
348 func msdosModeToFileMode(m uint32) (mode fs.FileMode) {
349 if m&msdosDir != 0 {
350 mode = fs.ModeDir | 0777
351 } else {
352 mode = 0666
353 }
354 if m&msdosReadOnly != 0 {
355 mode &^= 0222
356 }
357 return mode
358 }
359
360 func fileModeToUnixMode(mode fs.FileMode) uint32 {
361 var m uint32
362 switch mode & fs.ModeType {
363 default:
364 m = s_IFREG
365 case fs.ModeDir:
366 m = s_IFDIR
367 case fs.ModeSymlink:
368 m = s_IFLNK
369 case fs.ModeNamedPipe:
370 m = s_IFIFO
371 case fs.ModeSocket:
372 m = s_IFSOCK
373 case fs.ModeDevice:
374 m = s_IFBLK
375 case fs.ModeDevice | fs.ModeCharDevice:
376 m = s_IFCHR
377 }
378 if mode&fs.ModeSetuid != 0 {
379 m |= s_ISUID
380 }
381 if mode&fs.ModeSetgid != 0 {
382 m |= s_ISGID
383 }
384 if mode&fs.ModeSticky != 0 {
385 m |= s_ISVTX
386 }
387 return m | uint32(mode&0777)
388 }
389
390 func unixModeToFileMode(m uint32) fs.FileMode {
391 mode := fs.FileMode(m & 0777)
392 switch m & s_IFMT {
393 case s_IFBLK:
394 mode |= fs.ModeDevice
395 case s_IFCHR:
396 mode |= fs.ModeDevice | fs.ModeCharDevice
397 case s_IFDIR:
398 mode |= fs.ModeDir
399 case s_IFIFO:
400 mode |= fs.ModeNamedPipe
401 case s_IFLNK:
402 mode |= fs.ModeSymlink
403 case s_IFREG:
404
405 case s_IFSOCK:
406 mode |= fs.ModeSocket
407 }
408 if m&s_ISGID != 0 {
409 mode |= fs.ModeSetgid
410 }
411 if m&s_ISUID != 0 {
412 mode |= fs.ModeSetuid
413 }
414 if m&s_ISVTX != 0 {
415 mode |= fs.ModeSticky
416 }
417 return mode
418 }
419
View as plain text