diff options
| author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 | 
|---|---|---|
| committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 | 
| commit | f154da9e12608589e8d5f0508f908a0c3e88a1bb (patch) | |
| tree | f8255d51e10c6f1e0ed69702200b966c9556a431 /src/archive/zip/struct.go | |
| parent | 8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff) | |
| download | golang-f154da9e12608589e8d5f0508f908a0c3e88a1bb.tar.gz | |
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/archive/zip/struct.go')
| -rw-r--r-- | src/archive/zip/struct.go | 313 | 
1 files changed, 313 insertions, 0 deletions
| diff --git a/src/archive/zip/struct.go b/src/archive/zip/struct.go new file mode 100644 index 000000000..cb28e8324 --- /dev/null +++ b/src/archive/zip/struct.go @@ -0,0 +1,313 @@ +// 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 disk spanning. + +A note about ZIP64: + +To be backwards compatible the FileHeader has both 32 and 64 bit Size +fields. The 64 bit fields will always contain the correct value and +for normal archives both fields will be the same. For files requiring +the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit +fields must be used instead. +*/ +package zip + +import ( +	"os" +	"path" +	"time" +) + +// Compression methods. +const ( +	Store   uint16 = 0 +	Deflate uint16 = 8 +) + +const ( +	fileHeaderSignature      = 0x04034b50 +	directoryHeaderSignature = 0x02014b50 +	directoryEndSignature    = 0x06054b50 +	directory64LocSignature  = 0x07064b50 +	directory64EndSignature  = 0x06064b50 +	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 +	dataDescriptor64Len      = 24         // descriptor with 8 byte sizes +	directory64LocLen        = 20         // +	directory64EndLen        = 56         // + extra + +	// Constants for the first byte in CreatorVersion +	creatorFAT    = 0 +	creatorUnix   = 3 +	creatorNTFS   = 11 +	creatorVFAT   = 14 +	creatorMacOSX = 19 + +	// version numbers +	zipVersion20 = 20 // 2.0 +	zipVersion45 = 45 // 4.5 (reads and writes zip64 archives) + +	// limits for non zip64 files +	uint16max = (1 << 16) - 1 +	uint32max = (1 << 32) - 1 + +	// extra header id's +	zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field +) + +// FileHeader describes a file within a zip file. +// See the zip spec for details. +type FileHeader struct { +	// Name is the name of the file. +	// It must be a relative path: it must not start with a drive +	// letter (e.g. C:) or leading slash, and only forward slashes +	// are allowed. +	Name string + +	CreatorVersion     uint16 +	ReaderVersion      uint16 +	Flags              uint16 +	Method             uint16 +	ModifiedTime       uint16 // MS-DOS time +	ModifiedDate       uint16 // MS-DOS date +	CRC32              uint32 +	CompressedSize     uint32 // deprecated; use CompressedSize64 +	UncompressedSize   uint32 // deprecated; use UncompressedSize64 +	CompressedSize64   uint64 +	UncompressedSize64 uint64 +	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 path.Base(fi.fh.Name) } +func (fi headerFileInfo) Size() int64 { +	if fi.fh.UncompressedSize64 > 0 { +		return int64(fi.fh.UncompressedSize64) +	} +	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. +// Because os.FileInfo's Name method returns only the base name of +// the file it describes, it may be necessary to modify the Name field +// of the returned header to provide the full path name of the file. +func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { +	size := fi.Size() +	fh := &FileHeader{ +		Name:               fi.Name(), +		UncompressedSize64: uint64(size), +	} +	fh.SetModTime(fi.ModTime()) +	fh.SetMode(fi.Mode()) +	if fh.UncompressedSize64 > uint32max { +		fh.UncompressedSize = uint32max +	} else { +		fh.UncompressedSize = uint32(fh.UncompressedSize64) +	} +	return fh, nil +} + +type directoryEnd struct { +	diskNbr            uint32 // unused +	dirDiskNbr         uint32 // unused +	dirRecordsThisDisk uint64 // unused +	directoryRecords   uint64 +	directorySize      uint64 +	directoryOffset    uint64 // 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 in UTC. +// 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 in UTC. +// 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 +	} +} + +// isZip64 returns true if the file size exceeds the 32 bit limit +func (fh *FileHeader) isZip64() bool { +	return fh.CompressedSize64 > uint32max || fh.UncompressedSize64 > uint32max +} + +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 +} | 
