mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			314 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			8.3 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: https://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 instead.
 | 
						|
	UncompressedSize   uint32 // Deprecated: Use UncompressedSize64 instead.
 | 
						|
	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 reports whether 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
 | 
						|
}
 |