github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/archive/zip/struct.go (about)

     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  /*
     6  Package zip provides support for reading and writing ZIP archives.
     7  
     8  See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
     9  
    10  This package does not support disk spanning.
    11  
    12  A note about ZIP64:
    13  
    14  To be backwards compatible the FileHeader has both 32 and 64 bit Size
    15  fields. The 64 bit fields will always contain the correct value and
    16  for normal archives both fields will be the same. For files requiring
    17  the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit
    18  fields must be used instead.
    19  */
    20  package zip
    21  
    22  import (
    23  	"os"
    24  	"time"
    25  )
    26  
    27  // Compression methods.
    28  const (
    29  	Store   uint16 = 0
    30  	Deflate uint16 = 8
    31  )
    32  
    33  const (
    34  	fileHeaderSignature      = 0x04034b50
    35  	directoryHeaderSignature = 0x02014b50
    36  	directoryEndSignature    = 0x06054b50
    37  	directory64LocSignature  = 0x07064b50
    38  	directory64EndSignature  = 0x06064b50
    39  	dataDescriptorSignature  = 0x08074b50 // de-facto standard; required by OS X Finder
    40  	fileHeaderLen            = 30         // + filename + extra
    41  	directoryHeaderLen       = 46         // + filename + extra + comment
    42  	directoryEndLen          = 22         // + comment
    43  	dataDescriptorLen        = 16         // four uint32: descriptor signature, crc32, compressed size, size
    44  	dataDescriptor64Len      = 24         // descriptor with 8 byte sizes
    45  	directory64LocLen        = 20         //
    46  	directory64EndLen        = 56         // + extra
    47  
    48  	// Constants for the first byte in CreatorVersion
    49  	creatorFAT    = 0
    50  	creatorUnix   = 3
    51  	creatorNTFS   = 11
    52  	creatorVFAT   = 14
    53  	creatorMacOSX = 19
    54  
    55  	// version numbers
    56  	zipVersion20 = 20 // 2.0
    57  	zipVersion45 = 45 // 4.5 (reads and writes zip64 archives)
    58  
    59  	// limits for non zip64 files
    60  	uint16max = (1 << 16) - 1
    61  	uint32max = (1 << 32) - 1
    62  
    63  	// extra header id's
    64  	zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field
    65  )
    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  	// It must be a relative path: it must not start with a drive
    72  	// letter (e.g. C:) or leading slash, and only forward slashes
    73  	// are allowed.
    74  	Name string
    75  
    76  	CreatorVersion     uint16
    77  	ReaderVersion      uint16
    78  	Flags              uint16
    79  	Method             uint16
    80  	ModifiedTime       uint16 // MS-DOS time
    81  	ModifiedDate       uint16 // MS-DOS date
    82  	CRC32              uint32
    83  	CompressedSize     uint32 // deprecated; use CompressedSize64
    84  	UncompressedSize   uint32 // deprecated; use UncompressedSize64
    85  	CompressedSize64   uint64
    86  	UncompressedSize64 uint64
    87  	Extra              []byte
    88  	ExternalAttrs      uint32 // Meaning depends on CreatorVersion
    89  	Comment            string
    90  }
    91  
    92  // FileInfo returns an os.FileInfo for the FileHeader.
    93  func (h *FileHeader) FileInfo() os.FileInfo {
    94  	return headerFileInfo{h}
    95  }
    96  
    97  // headerFileInfo implements os.FileInfo.
    98  type headerFileInfo struct {
    99  	fh *FileHeader
   100  }
   101  
   102  func (fi headerFileInfo) Name() string { return fi.fh.Name }
   103  func (fi headerFileInfo) Size() int64 {
   104  	if fi.fh.UncompressedSize64 > 0 {
   105  		return int64(fi.fh.UncompressedSize64)
   106  	}
   107  	return int64(fi.fh.UncompressedSize)
   108  }
   109  func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
   110  func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
   111  func (fi headerFileInfo) Mode() os.FileMode  { return fi.fh.Mode() }
   112  func (fi headerFileInfo) Sys() interface{}   { return fi.fh }
   113  
   114  // FileInfoHeader creates a partially-populated FileHeader from an
   115  // os.FileInfo.
   116  func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
   117  	size := fi.Size()
   118  	fh := &FileHeader{
   119  		Name:               fi.Name(),
   120  		UncompressedSize64: uint64(size),
   121  	}
   122  	fh.SetModTime(fi.ModTime())
   123  	fh.SetMode(fi.Mode())
   124  	if fh.UncompressedSize64 > uint32max {
   125  		fh.UncompressedSize = uint32max
   126  	} else {
   127  		fh.UncompressedSize = uint32(fh.UncompressedSize64)
   128  	}
   129  	return fh, nil
   130  }
   131  
   132  type directoryEnd struct {
   133  	diskNbr            uint32 // unused
   134  	dirDiskNbr         uint32 // unused
   135  	dirRecordsThisDisk uint64 // unused
   136  	directoryRecords   uint64
   137  	directorySize      uint64
   138  	directoryOffset    uint64 // relative to file
   139  	commentLen         uint16
   140  	comment            string
   141  }
   142  
   143  // msDosTimeToTime converts an MS-DOS date and time into a time.Time.
   144  // The resolution is 2s.
   145  // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
   146  func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
   147  	return time.Date(
   148  		// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
   149  		int(dosDate>>9+1980),
   150  		time.Month(dosDate>>5&0xf),
   151  		int(dosDate&0x1f),
   152  
   153  		// time bits 0-4: second/2; 5-10: minute; 11-15: hour
   154  		int(dosTime>>11),
   155  		int(dosTime>>5&0x3f),
   156  		int(dosTime&0x1f*2),
   157  		0, // nanoseconds
   158  
   159  		time.UTC,
   160  	)
   161  }
   162  
   163  // timeToMsDosTime converts a time.Time to an MS-DOS date and time.
   164  // The resolution is 2s.
   165  // See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
   166  func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
   167  	t = t.In(time.UTC)
   168  	fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
   169  	fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
   170  	return
   171  }
   172  
   173  // ModTime returns the modification time.
   174  // The resolution is 2s.
   175  func (h *FileHeader) ModTime() time.Time {
   176  	return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
   177  }
   178  
   179  // SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.
   180  // The resolution is 2s.
   181  func (h *FileHeader) SetModTime(t time.Time) {
   182  	h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
   183  }
   184  
   185  const (
   186  	// Unix constants. The specification doesn't mention them,
   187  	// but these seem to be the values agreed on by tools.
   188  	s_IFMT   = 0xf000
   189  	s_IFSOCK = 0xc000
   190  	s_IFLNK  = 0xa000
   191  	s_IFREG  = 0x8000
   192  	s_IFBLK  = 0x6000
   193  	s_IFDIR  = 0x4000
   194  	s_IFCHR  = 0x2000
   195  	s_IFIFO  = 0x1000
   196  	s_ISUID  = 0x800
   197  	s_ISGID  = 0x400
   198  	s_ISVTX  = 0x200
   199  
   200  	msdosDir      = 0x10
   201  	msdosReadOnly = 0x01
   202  )
   203  
   204  // Mode returns the permission and mode bits for the FileHeader.
   205  func (h *FileHeader) Mode() (mode os.FileMode) {
   206  	switch h.CreatorVersion >> 8 {
   207  	case creatorUnix, creatorMacOSX:
   208  		mode = unixModeToFileMode(h.ExternalAttrs >> 16)
   209  	case creatorNTFS, creatorVFAT, creatorFAT:
   210  		mode = msdosModeToFileMode(h.ExternalAttrs)
   211  	}
   212  	if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
   213  		mode |= os.ModeDir
   214  	}
   215  	return mode
   216  }
   217  
   218  // SetMode changes the permission and mode bits for the FileHeader.
   219  func (h *FileHeader) SetMode(mode os.FileMode) {
   220  	h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
   221  	h.ExternalAttrs = fileModeToUnixMode(mode) << 16
   222  
   223  	// set MSDOS attributes too, as the original zip does.
   224  	if mode&os.ModeDir != 0 {
   225  		h.ExternalAttrs |= msdosDir
   226  	}
   227  	if mode&0200 == 0 {
   228  		h.ExternalAttrs |= msdosReadOnly
   229  	}
   230  }
   231  
   232  // isZip64 returns true if the file size exceeds the 32 bit limit
   233  func (fh *FileHeader) isZip64() bool {
   234  	return fh.CompressedSize64 > uint32max || fh.UncompressedSize64 > uint32max
   235  }
   236  
   237  func msdosModeToFileMode(m uint32) (mode os.FileMode) {
   238  	if m&msdosDir != 0 {
   239  		mode = os.ModeDir | 0777
   240  	} else {
   241  		mode = 0666
   242  	}
   243  	if m&msdosReadOnly != 0 {
   244  		mode &^= 0222
   245  	}
   246  	return mode
   247  }
   248  
   249  func fileModeToUnixMode(mode os.FileMode) uint32 {
   250  	var m uint32
   251  	switch mode & os.ModeType {
   252  	default:
   253  		m = s_IFREG
   254  	case os.ModeDir:
   255  		m = s_IFDIR
   256  	case os.ModeSymlink:
   257  		m = s_IFLNK
   258  	case os.ModeNamedPipe:
   259  		m = s_IFIFO
   260  	case os.ModeSocket:
   261  		m = s_IFSOCK
   262  	case os.ModeDevice:
   263  		if mode&os.ModeCharDevice != 0 {
   264  			m = s_IFCHR
   265  		} else {
   266  			m = s_IFBLK
   267  		}
   268  	}
   269  	if mode&os.ModeSetuid != 0 {
   270  		m |= s_ISUID
   271  	}
   272  	if mode&os.ModeSetgid != 0 {
   273  		m |= s_ISGID
   274  	}
   275  	if mode&os.ModeSticky != 0 {
   276  		m |= s_ISVTX
   277  	}
   278  	return m | uint32(mode&0777)
   279  }
   280  
   281  func unixModeToFileMode(m uint32) os.FileMode {
   282  	mode := os.FileMode(m & 0777)
   283  	switch m & s_IFMT {
   284  	case s_IFBLK:
   285  		mode |= os.ModeDevice
   286  	case s_IFCHR:
   287  		mode |= os.ModeDevice | os.ModeCharDevice
   288  	case s_IFDIR:
   289  		mode |= os.ModeDir
   290  	case s_IFIFO:
   291  		mode |= os.ModeNamedPipe
   292  	case s_IFLNK:
   293  		mode |= os.ModeSymlink
   294  	case s_IFREG:
   295  		// nothing to do
   296  	case s_IFSOCK:
   297  		mode |= os.ModeSocket
   298  	}
   299  	if m&s_ISGID != 0 {
   300  		mode |= os.ModeSetgid
   301  	}
   302  	if m&s_ISUID != 0 {
   303  		mode |= os.ModeSetuid
   304  	}
   305  	if m&s_ISVTX != 0 {
   306  		mode |= os.ModeSticky
   307  	}
   308  	return mode
   309  }