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