struct.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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. Package zip provides support for reading and writing ZIP archives.
  6. See: https://www.pkware.com/appnote
  7. This package does not support disk spanning.
  8. A note about ZIP64:
  9. To be backwards compatible the FileHeader has both 32 and 64 bit Size
  10. fields. The 64 bit fields will always contain the correct value and
  11. for normal archives both fields will be the same. For files requiring
  12. the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit
  13. fields must be used instead.
  14. */
  15. package zip
  16. import (
  17. "os"
  18. "path"
  19. "time"
  20. )
  21. // Compression methods.
  22. const (
  23. Store uint16 = 0 // no compression
  24. Deflate uint16 = 8 // DEFLATE compressed
  25. )
  26. const (
  27. fileHeaderSignature = 0x04034b50
  28. directoryHeaderSignature = 0x02014b50
  29. directoryEndSignature = 0x06054b50
  30. directory64LocSignature = 0x07064b50
  31. directory64EndSignature = 0x06064b50
  32. dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder
  33. fileHeaderLen = 30 // + filename + extra
  34. directoryHeaderLen = 46 // + filename + extra + comment
  35. directoryEndLen = 22 // + comment
  36. dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size
  37. dataDescriptor64Len = 24 // descriptor with 8 byte sizes
  38. directory64LocLen = 20 //
  39. directory64EndLen = 56 // + extra
  40. // Constants for the first byte in CreatorVersion.
  41. creatorFAT = 0
  42. creatorUnix = 3
  43. creatorNTFS = 11
  44. creatorVFAT = 14
  45. creatorMacOSX = 19
  46. // Version numbers.
  47. zipVersion20 = 20 // 2.0
  48. zipVersion45 = 45 // 4.5 (reads and writes zip64 archives)
  49. // Limits for non zip64 files.
  50. uint16max = (1 << 16) - 1
  51. uint32max = (1 << 32) - 1
  52. // Extra header IDs.
  53. //
  54. // IDs 0..31 are reserved for official use by PKWARE.
  55. // IDs above that range are defined by third-party vendors.
  56. // Since ZIP lacked high precision timestamps (nor a official specification
  57. // of the timezone used for the date fields), many competing extra fields
  58. // have been invented. Pervasive use effectively makes them "official".
  59. //
  60. // See http://mdfs.net/Docs/Comp/Archiving/Zip/ExtraField
  61. zip64ExtraID = 0x0001 // Zip64 extended information
  62. ntfsExtraID = 0x000a // NTFS
  63. unixExtraID = 0x000d // UNIX
  64. extTimeExtraID = 0x5455 // Extended timestamp
  65. infoZipUnixExtraID = 0x5855 // Info-ZIP Unix extension
  66. )
  67. // FileHeader describes a file within a zip file.
  68. // See the zip spec for details.
  69. type FileHeader struct {
  70. // Name is the name of the file.
  71. //
  72. // It must be a relative path, not start with a drive letter (such as "C:"),
  73. // and must use forward slashes instead of back slashes. A trailing slash
  74. // indicates that this file is a directory and should have no data.
  75. //
  76. // When reading zip files, the Name field is populated from
  77. // the zip file directly and is not validated for correctness.
  78. // It is the caller's responsibility to sanitize it as
  79. // appropriate, including canonicalizing slash directions,
  80. // validating that paths are relative, and preventing path
  81. // traversal through filenames ("../../../").
  82. Name string
  83. // Comment is any arbitrary user-defined string shorter than 64KiB.
  84. Comment string
  85. // NonUTF8 indicates that Name and Comment are not encoded in UTF-8.
  86. //
  87. // By specification, the only other encoding permitted should be CP-437,
  88. // but historically many ZIP readers interpret Name and Comment as whatever
  89. // the system's local character encoding happens to be.
  90. //
  91. // This flag should only be set if the user intends to encode a non-portable
  92. // ZIP file for a specific localized region. Otherwise, the Writer
  93. // automatically sets the ZIP format's UTF-8 flag for valid UTF-8 strings.
  94. NonUTF8 bool
  95. CreatorVersion uint16
  96. ReaderVersion uint16
  97. Flags uint16
  98. // Method is the compression method. If zero, Store is used.
  99. Method uint16
  100. // Modified is the modified time of the file.
  101. //
  102. // When reading, an extended timestamp is preferred over the legacy MS-DOS
  103. // date field, and the offset between the times is used as the timezone.
  104. // If only the MS-DOS date is present, the timezone is assumed to be UTC.
  105. //
  106. // When writing, an extended timestamp (which is timezone-agnostic) is
  107. // always emitted. The legacy MS-DOS date field is encoded according to the
  108. // location of the Modified time.
  109. Modified time.Time
  110. ModifiedTime uint16 // Deprecated: Legacy MS-DOS date; use Modified instead.
  111. ModifiedDate uint16 // Deprecated: Legacy MS-DOS time; use Modified instead.
  112. CRC32 uint32
  113. CompressedSize uint32 // Deprecated: Use CompressedSize64 instead.
  114. UncompressedSize uint32 // Deprecated: Use UncompressedSize64 instead.
  115. CompressedSize64 uint64
  116. UncompressedSize64 uint64
  117. Extra []byte
  118. ExternalAttrs uint32 // Meaning depends on CreatorVersion
  119. }
  120. // FileInfo returns an os.FileInfo for the FileHeader.
  121. func (h *FileHeader) FileInfo() os.FileInfo {
  122. return headerFileInfo{h}
  123. }
  124. // headerFileInfo implements os.FileInfo.
  125. type headerFileInfo struct {
  126. fh *FileHeader
  127. }
  128. func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
  129. func (fi headerFileInfo) Size() int64 {
  130. if fi.fh.UncompressedSize64 > 0 {
  131. return int64(fi.fh.UncompressedSize64)
  132. }
  133. return int64(fi.fh.UncompressedSize)
  134. }
  135. func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
  136. func (fi headerFileInfo) ModTime() time.Time {
  137. if fi.fh.Modified.IsZero() {
  138. return fi.fh.ModTime()
  139. }
  140. return fi.fh.Modified.UTC()
  141. }
  142. func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
  143. func (fi headerFileInfo) Sys() interface{} { return fi.fh }
  144. // FileInfoHeader creates a partially-populated FileHeader from an
  145. // os.FileInfo.
  146. // Because os.FileInfo's Name method returns only the base name of
  147. // the file it describes, it may be necessary to modify the Name field
  148. // of the returned header to provide the full path name of the file.
  149. // If compression is desired, callers should set the FileHeader.Method
  150. // field; it is unset by default.
  151. func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
  152. size := fi.Size()
  153. fh := &FileHeader{
  154. Name: fi.Name(),
  155. UncompressedSize64: uint64(size),
  156. }
  157. fh.SetModTime(fi.ModTime())
  158. fh.SetMode(fi.Mode())
  159. if fh.UncompressedSize64 > uint32max {
  160. fh.UncompressedSize = uint32max
  161. } else {
  162. fh.UncompressedSize = uint32(fh.UncompressedSize64)
  163. }
  164. return fh, nil
  165. }
  166. type directoryEnd struct {
  167. diskNbr uint32 // unused
  168. dirDiskNbr uint32 // unused
  169. dirRecordsThisDisk uint64 // unused
  170. directoryRecords uint64
  171. directorySize uint64
  172. directoryOffset uint64 // relative to file
  173. commentLen uint16
  174. comment string
  175. }
  176. // timeZone returns a *time.Location based on the provided offset.
  177. // If the offset is non-sensible, then this uses an offset of zero.
  178. func timeZone(offset time.Duration) *time.Location {
  179. const (
  180. minOffset = -12 * time.Hour // E.g., Baker island at -12:00
  181. maxOffset = +14 * time.Hour // E.g., Line island at +14:00
  182. offsetAlias = 15 * time.Minute // E.g., Nepal at +5:45
  183. )
  184. offset = offset.Round(offsetAlias)
  185. if offset < minOffset || maxOffset < offset {
  186. offset = 0
  187. }
  188. return time.FixedZone("", int(offset/time.Second))
  189. }
  190. // msDosTimeToTime converts an MS-DOS date and time into a time.Time.
  191. // The resolution is 2s.
  192. // See: https://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
  193. func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
  194. return time.Date(
  195. // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
  196. int(dosDate>>9+1980),
  197. time.Month(dosDate>>5&0xf),
  198. int(dosDate&0x1f),
  199. // time bits 0-4: second/2; 5-10: minute; 11-15: hour
  200. int(dosTime>>11),
  201. int(dosTime>>5&0x3f),
  202. int(dosTime&0x1f*2),
  203. 0, // nanoseconds
  204. time.UTC,
  205. )
  206. }
  207. // timeToMsDosTime converts a time.Time to an MS-DOS date and time.
  208. // The resolution is 2s.
  209. // See: https://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
  210. func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
  211. fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
  212. fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
  213. return
  214. }
  215. // ModTime returns the modification time in UTC using the legacy
  216. // ModifiedDate and ModifiedTime fields.
  217. //
  218. // Deprecated: Use Modified instead.
  219. func (h *FileHeader) ModTime() time.Time {
  220. return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
  221. }
  222. // SetModTime sets the Modified, ModifiedTime, and ModifiedDate fields
  223. // to the given time in UTC.
  224. //
  225. // Deprecated: Use Modified instead.
  226. func (h *FileHeader) SetModTime(t time.Time) {
  227. t = t.UTC() // Convert to UTC for compatibility
  228. h.Modified = t
  229. h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
  230. }
  231. const (
  232. // Unix constants. The specification doesn't mention them,
  233. // but these seem to be the values agreed on by tools.
  234. s_IFMT = 0xf000
  235. s_IFSOCK = 0xc000
  236. s_IFLNK = 0xa000
  237. s_IFREG = 0x8000
  238. s_IFBLK = 0x6000
  239. s_IFDIR = 0x4000
  240. s_IFCHR = 0x2000
  241. s_IFIFO = 0x1000
  242. s_ISUID = 0x800
  243. s_ISGID = 0x400
  244. s_ISVTX = 0x200
  245. msdosDir = 0x10
  246. msdosReadOnly = 0x01
  247. )
  248. // Mode returns the permission and mode bits for the FileHeader.
  249. func (h *FileHeader) Mode() (mode os.FileMode) {
  250. switch h.CreatorVersion >> 8 {
  251. case creatorUnix, creatorMacOSX:
  252. mode = unixModeToFileMode(h.ExternalAttrs >> 16)
  253. case creatorNTFS, creatorVFAT, creatorFAT:
  254. mode = msdosModeToFileMode(h.ExternalAttrs)
  255. }
  256. if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
  257. mode |= os.ModeDir
  258. }
  259. return mode
  260. }
  261. // SetMode changes the permission and mode bits for the FileHeader.
  262. func (h *FileHeader) SetMode(mode os.FileMode) {
  263. h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
  264. h.ExternalAttrs = fileModeToUnixMode(mode) << 16
  265. // set MSDOS attributes too, as the original zip does.
  266. if mode&os.ModeDir != 0 {
  267. h.ExternalAttrs |= msdosDir
  268. }
  269. if mode&0200 == 0 {
  270. h.ExternalAttrs |= msdosReadOnly
  271. }
  272. }
  273. // isZip64 reports whether the file size exceeds the 32 bit limit
  274. func (h *FileHeader) isZip64() bool {
  275. return h.CompressedSize64 >= uint32max || h.UncompressedSize64 >= uint32max
  276. }
  277. func msdosModeToFileMode(m uint32) (mode os.FileMode) {
  278. if m&msdosDir != 0 {
  279. mode = os.ModeDir | 0777
  280. } else {
  281. mode = 0666
  282. }
  283. if m&msdosReadOnly != 0 {
  284. mode &^= 0222
  285. }
  286. return mode
  287. }
  288. func fileModeToUnixMode(mode os.FileMode) uint32 {
  289. var m uint32
  290. switch mode & os.ModeType {
  291. default:
  292. m = s_IFREG
  293. case os.ModeDir:
  294. m = s_IFDIR
  295. case os.ModeSymlink:
  296. m = s_IFLNK
  297. case os.ModeNamedPipe:
  298. m = s_IFIFO
  299. case os.ModeSocket:
  300. m = s_IFSOCK
  301. case os.ModeDevice:
  302. if mode&os.ModeCharDevice != 0 {
  303. m = s_IFCHR
  304. } else {
  305. m = s_IFBLK
  306. }
  307. }
  308. if mode&os.ModeSetuid != 0 {
  309. m |= s_ISUID
  310. }
  311. if mode&os.ModeSetgid != 0 {
  312. m |= s_ISGID
  313. }
  314. if mode&os.ModeSticky != 0 {
  315. m |= s_ISVTX
  316. }
  317. return m | uint32(mode&0777)
  318. }
  319. func unixModeToFileMode(m uint32) os.FileMode {
  320. mode := os.FileMode(m & 0777)
  321. switch m & s_IFMT {
  322. case s_IFBLK:
  323. mode |= os.ModeDevice
  324. case s_IFCHR:
  325. mode |= os.ModeDevice | os.ModeCharDevice
  326. case s_IFDIR:
  327. mode |= os.ModeDir
  328. case s_IFIFO:
  329. mode |= os.ModeNamedPipe
  330. case s_IFLNK:
  331. mode |= os.ModeSymlink
  332. case s_IFREG:
  333. // nothing to do
  334. case s_IFSOCK:
  335. mode |= os.ModeSocket
  336. }
  337. if m&s_ISGID != 0 {
  338. mode |= os.ModeSetgid
  339. }
  340. if m&s_ISUID != 0 {
  341. mode |= os.ModeSetuid
  342. }
  343. if m&s_ISVTX != 0 {
  344. mode |= os.ModeSticky
  345. }
  346. return mode
  347. }