mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
We now always write the "optional" streaming data descriptor signature, which turns out to be required for OS X. Also, handle reading the data descriptor with or without the signature, per the spec's recommendation. Fix data descriptor reading bugs found in the process. Fixes #3252 R=golang-dev, alex.brainman, nigeltao, rsc CC=golang-dev https://golang.org/cl/5787062
265 lines
6.5 KiB
Go
265 lines
6.5 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
/*
|
|
Package zip provides support for reading and writing ZIP archives.
|
|
|
|
See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
|
|
|
|
This package does not support ZIP64 or disk spanning.
|
|
*/
|
|
package zip
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// Compression methods.
|
|
const (
|
|
Store uint16 = 0
|
|
Deflate uint16 = 8
|
|
)
|
|
|
|
const (
|
|
fileHeaderSignature = 0x04034b50
|
|
directoryHeaderSignature = 0x02014b50
|
|
directoryEndSignature = 0x06054b50
|
|
dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder
|
|
fileHeaderLen = 30 // + filename + extra
|
|
directoryHeaderLen = 46 // + filename + extra + comment
|
|
directoryEndLen = 22 // + comment
|
|
dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size
|
|
|
|
// Constants for the first byte in CreatorVersion
|
|
creatorFAT = 0
|
|
creatorUnix = 3
|
|
creatorNTFS = 11
|
|
creatorVFAT = 14
|
|
creatorMacOSX = 19
|
|
)
|
|
|
|
type FileHeader struct {
|
|
Name string
|
|
CreatorVersion uint16
|
|
ReaderVersion uint16
|
|
Flags uint16
|
|
Method uint16
|
|
ModifiedTime uint16 // MS-DOS time
|
|
ModifiedDate uint16 // MS-DOS date
|
|
CRC32 uint32
|
|
CompressedSize uint32
|
|
UncompressedSize uint32
|
|
Extra []byte
|
|
ExternalAttrs uint32 // Meaning depends on CreatorVersion
|
|
Comment string
|
|
}
|
|
|
|
// FileInfo returns an os.FileInfo for the FileHeader.
|
|
func (h *FileHeader) FileInfo() os.FileInfo {
|
|
return headerFileInfo{h}
|
|
}
|
|
|
|
// headerFileInfo implements os.FileInfo.
|
|
type headerFileInfo struct {
|
|
fh *FileHeader
|
|
}
|
|
|
|
func (fi headerFileInfo) Name() string { return fi.fh.Name }
|
|
func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) }
|
|
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
|
func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
|
|
func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
|
|
func (fi headerFileInfo) Sys() interface{} { return fi.fh }
|
|
|
|
// FileInfoHeader creates a partially-populated FileHeader from an
|
|
// os.FileInfo.
|
|
func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
|
|
size := fi.Size()
|
|
if size > (1<<32 - 1) {
|
|
return nil, errors.New("zip: file over 4GB")
|
|
}
|
|
fh := &FileHeader{
|
|
Name: fi.Name(),
|
|
UncompressedSize: uint32(size),
|
|
}
|
|
fh.SetModTime(fi.ModTime())
|
|
fh.SetMode(fi.Mode())
|
|
return fh, nil
|
|
}
|
|
|
|
type directoryEnd struct {
|
|
diskNbr uint16 // unused
|
|
dirDiskNbr uint16 // unused
|
|
dirRecordsThisDisk uint16 // unused
|
|
directoryRecords uint16
|
|
directorySize uint32
|
|
directoryOffset uint32 // relative to file
|
|
commentLen uint16
|
|
comment string
|
|
}
|
|
|
|
// msDosTimeToTime converts an MS-DOS date and time into a time.Time.
|
|
// The resolution is 2s.
|
|
// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
|
|
func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
|
|
return time.Date(
|
|
// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
|
|
int(dosDate>>9+1980),
|
|
time.Month(dosDate>>5&0xf),
|
|
int(dosDate&0x1f),
|
|
|
|
// time bits 0-4: second/2; 5-10: minute; 11-15: hour
|
|
int(dosTime>>11),
|
|
int(dosTime>>5&0x3f),
|
|
int(dosTime&0x1f*2),
|
|
0, // nanoseconds
|
|
|
|
time.UTC,
|
|
)
|
|
}
|
|
|
|
// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
|
|
// The resolution is 2s.
|
|
// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
|
|
func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
|
|
t = t.In(time.UTC)
|
|
fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
|
|
fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
|
|
return
|
|
}
|
|
|
|
// ModTime returns the modification time.
|
|
// The resolution is 2s.
|
|
func (h *FileHeader) ModTime() time.Time {
|
|
return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
|
|
}
|
|
|
|
// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.
|
|
// The resolution is 2s.
|
|
func (h *FileHeader) SetModTime(t time.Time) {
|
|
h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
|
|
}
|
|
|
|
const (
|
|
// Unix constants. The specification doesn't mention them,
|
|
// but these seem to be the values agreed on by tools.
|
|
s_IFMT = 0xf000
|
|
s_IFSOCK = 0xc000
|
|
s_IFLNK = 0xa000
|
|
s_IFREG = 0x8000
|
|
s_IFBLK = 0x6000
|
|
s_IFDIR = 0x4000
|
|
s_IFCHR = 0x2000
|
|
s_IFIFO = 0x1000
|
|
s_ISUID = 0x800
|
|
s_ISGID = 0x400
|
|
s_ISVTX = 0x200
|
|
|
|
msdosDir = 0x10
|
|
msdosReadOnly = 0x01
|
|
)
|
|
|
|
// Mode returns the permission and mode bits for the FileHeader.
|
|
func (h *FileHeader) Mode() (mode os.FileMode) {
|
|
switch h.CreatorVersion >> 8 {
|
|
case creatorUnix, creatorMacOSX:
|
|
mode = unixModeToFileMode(h.ExternalAttrs >> 16)
|
|
case creatorNTFS, creatorVFAT, creatorFAT:
|
|
mode = msdosModeToFileMode(h.ExternalAttrs)
|
|
}
|
|
if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
|
|
mode |= os.ModeDir
|
|
}
|
|
return mode
|
|
}
|
|
|
|
// SetMode changes the permission and mode bits for the FileHeader.
|
|
func (h *FileHeader) SetMode(mode os.FileMode) {
|
|
h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
|
|
h.ExternalAttrs = fileModeToUnixMode(mode) << 16
|
|
|
|
// set MSDOS attributes too, as the original zip does.
|
|
if mode&os.ModeDir != 0 {
|
|
h.ExternalAttrs |= msdosDir
|
|
}
|
|
if mode&0200 == 0 {
|
|
h.ExternalAttrs |= msdosReadOnly
|
|
}
|
|
}
|
|
|
|
func msdosModeToFileMode(m uint32) (mode os.FileMode) {
|
|
if m&msdosDir != 0 {
|
|
mode = os.ModeDir | 0777
|
|
} else {
|
|
mode = 0666
|
|
}
|
|
if m&msdosReadOnly != 0 {
|
|
mode &^= 0222
|
|
}
|
|
return mode
|
|
}
|
|
|
|
func fileModeToUnixMode(mode os.FileMode) uint32 {
|
|
var m uint32
|
|
switch mode & os.ModeType {
|
|
default:
|
|
m = s_IFREG
|
|
case os.ModeDir:
|
|
m = s_IFDIR
|
|
case os.ModeSymlink:
|
|
m = s_IFLNK
|
|
case os.ModeNamedPipe:
|
|
m = s_IFIFO
|
|
case os.ModeSocket:
|
|
m = s_IFSOCK
|
|
case os.ModeDevice:
|
|
if mode&os.ModeCharDevice != 0 {
|
|
m = s_IFCHR
|
|
} else {
|
|
m = s_IFBLK
|
|
}
|
|
}
|
|
if mode&os.ModeSetuid != 0 {
|
|
m |= s_ISUID
|
|
}
|
|
if mode&os.ModeSetgid != 0 {
|
|
m |= s_ISGID
|
|
}
|
|
if mode&os.ModeSticky != 0 {
|
|
m |= s_ISVTX
|
|
}
|
|
return m | uint32(mode&0777)
|
|
}
|
|
|
|
func unixModeToFileMode(m uint32) os.FileMode {
|
|
mode := os.FileMode(m & 0777)
|
|
switch m & s_IFMT {
|
|
case s_IFBLK:
|
|
mode |= os.ModeDevice
|
|
case s_IFCHR:
|
|
mode |= os.ModeDevice | os.ModeCharDevice
|
|
case s_IFDIR:
|
|
mode |= os.ModeDir
|
|
case s_IFIFO:
|
|
mode |= os.ModeNamedPipe
|
|
case s_IFLNK:
|
|
mode |= os.ModeSymlink
|
|
case s_IFREG:
|
|
// nothing to do
|
|
case s_IFSOCK:
|
|
mode |= os.ModeSocket
|
|
}
|
|
if m&s_ISGID != 0 {
|
|
mode |= os.ModeSetgid
|
|
}
|
|
if m&s_ISUID != 0 {
|
|
mode |= os.ModeSetuid
|
|
}
|
|
if m&s_ISVTX != 0 {
|
|
mode |= os.ModeSticky
|
|
}
|
|
return mode
|
|
}
|