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.
6 Package zip provides support for reading and writing ZIP archives.
8 See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
10 This package does not support ZIP64 or disk spanning.
20 // Compression methods.
27 fileHeaderSignature = 0x04034b50
28 directoryHeaderSignature = 0x02014b50
29 directoryEndSignature = 0x06054b50
30 fileHeaderLen = 30 // + filename + extra
31 directoryHeaderLen = 46 // + filename + extra + comment
32 directoryEndLen = 22 // + comment
33 dataDescriptorLen = 12
35 // Constants for the first byte in CreatorVersion
43 type FileHeader struct {
49 ModifiedTime uint16 // MS-DOS time
50 ModifiedDate uint16 // MS-DOS date
53 UncompressedSize uint32
55 ExternalAttrs uint32 // Meaning depends on CreatorVersion
59 // FileInfo returns an os.FileInfo for the FileHeader.
60 func (h *FileHeader) FileInfo() os.FileInfo {
61 return headerFileInfo{h}
64 // headerFileInfo implements os.FileInfo.
65 type headerFileInfo struct {
69 func (fi headerFileInfo) Name() string { return fi.fh.Name }
70 func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) }
71 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
72 func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
73 func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
74 func (fi headerFileInfo) Sys() interface{} { return fi.fh }
76 // FileInfoHeader creates a partially-populated FileHeader from an
78 func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
80 if size > (1<<32 - 1) {
81 return nil, errors.New("zip: file over 4GB")
85 UncompressedSize: uint32(size),
87 fh.SetModTime(fi.ModTime())
92 type directoryEnd struct {
93 diskNbr uint16 // unused
94 dirDiskNbr uint16 // unused
95 dirRecordsThisDisk uint16 // unused
96 directoryRecords uint16
98 directoryOffset uint32 // relative to file
103 // msDosTimeToTime converts an MS-DOS date and time into a time.Time.
104 // The resolution is 2s.
105 // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
106 func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
108 // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
109 int(dosDate>>9+1980),
110 time.Month(dosDate>>5&0xf),
113 // time bits 0-4: second/2; 5-10: minute; 11-15: hour
115 int(dosTime>>5&0x3f),
123 // timeToMsDosTime converts a time.Time to an MS-DOS date and time.
124 // The resolution is 2s.
125 // See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
126 func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
128 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
129 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
133 // ModTime returns the modification time.
134 // The resolution is 2s.
135 func (h *FileHeader) ModTime() time.Time {
136 return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
139 // SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.
140 // The resolution is 2s.
141 func (h *FileHeader) SetModTime(t time.Time) {
142 h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
146 // Unix constants. The specification doesn't mention them,
147 // but these seem to be the values agreed on by tools.
164 // Mode returns the permission and mode bits for the FileHeader.
165 func (h *FileHeader) Mode() (mode os.FileMode) {
166 switch h.CreatorVersion >> 8 {
167 case creatorUnix, creatorMacOSX:
168 mode = unixModeToFileMode(h.ExternalAttrs >> 16)
169 case creatorNTFS, creatorVFAT, creatorFAT:
170 mode = msdosModeToFileMode(h.ExternalAttrs)
172 if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
178 // SetMode changes the permission and mode bits for the FileHeader.
179 func (h *FileHeader) SetMode(mode os.FileMode) {
180 h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
181 h.ExternalAttrs = fileModeToUnixMode(mode) << 16
183 // set MSDOS attributes too, as the original zip does.
184 if mode&os.ModeDir != 0 {
185 h.ExternalAttrs |= msdosDir
188 h.ExternalAttrs |= msdosReadOnly
192 func msdosModeToFileMode(m uint32) (mode os.FileMode) {
194 mode = os.ModeDir | 0777
198 if m&msdosReadOnly != 0 {
204 func fileModeToUnixMode(mode os.FileMode) uint32 {
206 switch mode & os.ModeType {
213 case os.ModeNamedPipe:
218 if mode&os.ModeCharDevice != 0 {
224 if mode&os.ModeSetuid != 0 {
227 if mode&os.ModeSetgid != 0 {
230 if mode&os.ModeSticky != 0 {
233 return m | uint32(mode&0777)
236 func unixModeToFileMode(m uint32) os.FileMode {
237 mode := os.FileMode(m & 0777)
240 mode |= os.ModeDevice
242 mode |= os.ModeDevice | os.ModeCharDevice
246 mode |= os.ModeNamedPipe
248 mode |= os.ModeSymlink
252 mode |= os.ModeSocket
255 mode |= os.ModeSetgid
258 mode |= os.ModeSetuid
261 mode |= os.ModeSticky