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