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 }