...

Source file src/archive/zip/struct.go

     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/appnote
     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 // no compression
    31		Deflate uint16 = 8 // DEFLATE compressed
    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 IDs.
    65		//
    66		// IDs 0..31 are reserved for official use by PKWARE.
    67		// IDs above that range are defined by third-party vendors.
    68		// Since ZIP lacked high precision timestamps (nor a official specification
    69		// of the timezone used for the date fields), many competing extra fields
    70		// have been invented. Pervasive use effectively makes them "official".
    71		//
    72		// See http://mdfs.net/Docs/Comp/Archiving/Zip/ExtraField
    73		zip64ExtraID       = 0x0001 // Zip64 extended information
    74		ntfsExtraID        = 0x000a // NTFS
    75		unixExtraID        = 0x000d // UNIX
    76		extTimeExtraID     = 0x5455 // Extended timestamp
    77		infoZipUnixExtraID = 0x5855 // Info-ZIP Unix extension
    78	)
    79	
    80	// FileHeader describes a file within a zip file.
    81	// See the zip spec for details.
    82	type FileHeader struct {
    83		// Name is the name of the file.
    84		//
    85		// It must be a relative path, not start with a drive letter (such as "C:"),
    86		// and must use forward slashes instead of back slashes. A trailing slash
    87		// indicates that this file is a directory and should have no data.
    88		//
    89		// When reading zip files, the Name field is populated from
    90		// the zip file directly and is not validated for correctness.
    91		// It is the caller's responsibility to sanitize it as
    92		// appropriate, including canonicalizing slash directions,
    93		// validating that paths are relative, and preventing path
    94		// traversal through filenames ("../../../").
    95		Name string
    96	
    97		// Comment is any arbitrary user-defined string shorter than 64KiB.
    98		Comment string
    99	
   100		// NonUTF8 indicates that Name and Comment are not encoded in UTF-8.
   101		//
   102		// By specification, the only other encoding permitted should be CP-437,
   103		// but historically many ZIP readers interpret Name and Comment as whatever
   104		// the system's local character encoding happens to be.
   105		//
   106		// This flag should only be set if the user intends to encode a non-portable
   107		// ZIP file for a specific localized region. Otherwise, the Writer
   108		// automatically sets the ZIP format's UTF-8 flag for valid UTF-8 strings.
   109		NonUTF8 bool
   110	
   111		CreatorVersion uint16
   112		ReaderVersion  uint16
   113		Flags          uint16
   114	
   115		// Method is the compression method. If zero, Store is used.
   116		Method uint16
   117	
   118		// Modified is the modified time of the file.
   119		//
   120		// When reading, an extended timestamp is preferred over the legacy MS-DOS
   121		// date field, and the offset between the times is used as the timezone.
   122		// If only the MS-DOS date is present, the timezone is assumed to be UTC.
   123		//
   124		// When writing, an extended timestamp (which is timezone-agnostic) is
   125		// always emitted. The legacy MS-DOS date field is encoded according to the
   126		// location of the Modified time.
   127		Modified     time.Time
   128		ModifiedTime uint16 // Deprecated: Legacy MS-DOS date; use Modified instead.
   129		ModifiedDate uint16 // Deprecated: Legacy MS-DOS time; use Modified instead.
   130	
   131		CRC32              uint32
   132		CompressedSize     uint32 // Deprecated: Use CompressedSize64 instead.
   133		UncompressedSize   uint32 // Deprecated: Use UncompressedSize64 instead.
   134		CompressedSize64   uint64
   135		UncompressedSize64 uint64
   136		Extra              []byte
   137		ExternalAttrs      uint32 // Meaning depends on CreatorVersion
   138	}
   139	
   140	// FileInfo returns an os.FileInfo for the FileHeader.
   141	func (h *FileHeader) FileInfo() os.FileInfo {
   142		return headerFileInfo{h}
   143	}
   144	
   145	// headerFileInfo implements os.FileInfo.
   146	type headerFileInfo struct {
   147		fh *FileHeader
   148	}
   149	
   150	func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
   151	func (fi headerFileInfo) Size() int64 {
   152		if fi.fh.UncompressedSize64 > 0 {
   153			return int64(fi.fh.UncompressedSize64)
   154		}
   155		return int64(fi.fh.UncompressedSize)
   156	}
   157	func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
   158	func (fi headerFileInfo) ModTime() time.Time {
   159		if fi.fh.Modified.IsZero() {
   160			return fi.fh.ModTime()
   161		}
   162		return fi.fh.Modified.UTC()
   163	}
   164	func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
   165	func (fi headerFileInfo) Sys() interface{}  { return fi.fh }
   166	
   167	// FileInfoHeader creates a partially-populated FileHeader from an
   168	// os.FileInfo.
   169	// Because os.FileInfo's Name method returns only the base name of
   170	// the file it describes, it may be necessary to modify the Name field
   171	// of the returned header to provide the full path name of the file.
   172	// If compression is desired, callers should set the FileHeader.Method
   173	// field; it is unset by default.
   174	func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
   175		size := fi.Size()
   176		fh := &FileHeader{
   177			Name:               fi.Name(),
   178			UncompressedSize64: uint64(size),
   179		}
   180		fh.SetModTime(fi.ModTime())
   181		fh.SetMode(fi.Mode())
   182		if fh.UncompressedSize64 > uint32max {
   183			fh.UncompressedSize = uint32max
   184		} else {
   185			fh.UncompressedSize = uint32(fh.UncompressedSize64)
   186		}
   187		return fh, nil
   188	}
   189	
   190	type directoryEnd struct {
   191		diskNbr            uint32 // unused
   192		dirDiskNbr         uint32 // unused
   193		dirRecordsThisDisk uint64 // unused
   194		directoryRecords   uint64
   195		directorySize      uint64
   196		directoryOffset    uint64 // relative to file
   197		commentLen         uint16
   198		comment            string
   199	}
   200	
   201	// timeZone returns a *time.Location based on the provided offset.
   202	// If the offset is non-sensible, then this uses an offset of zero.
   203	func timeZone(offset time.Duration) *time.Location {
   204		const (
   205			minOffset   = -12 * time.Hour  // E.g., Baker island at -12:00
   206			maxOffset   = +14 * time.Hour  // E.g., Line island at +14:00
   207			offsetAlias = 15 * time.Minute // E.g., Nepal at +5:45
   208		)
   209		offset = offset.Round(offsetAlias)
   210		if offset < minOffset || maxOffset < offset {
   211			offset = 0
   212		}
   213		return time.FixedZone("", int(offset/time.Second))
   214	}
   215	
   216	// msDosTimeToTime converts an MS-DOS date and time into a time.Time.
   217	// The resolution is 2s.
   218	// See: https://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
   219	func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
   220		return time.Date(
   221			// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
   222			int(dosDate>>9+1980),
   223			time.Month(dosDate>>5&0xf),
   224			int(dosDate&0x1f),
   225	
   226			// time bits 0-4: second/2; 5-10: minute; 11-15: hour
   227			int(dosTime>>11),
   228			int(dosTime>>5&0x3f),
   229			int(dosTime&0x1f*2),
   230			0, // nanoseconds
   231	
   232			time.UTC,
   233		)
   234	}
   235	
   236	// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
   237	// The resolution is 2s.
   238	// See: https://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
   239	func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
   240		fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
   241		fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
   242		return
   243	}
   244	
   245	// ModTime returns the modification time in UTC using the legacy
   246	// ModifiedDate and ModifiedTime fields.
   247	//
   248	// Deprecated: Use Modified instead.
   249	func (h *FileHeader) ModTime() time.Time {
   250		return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
   251	}
   252	
   253	// SetModTime sets the Modified, ModifiedTime, and ModifiedDate fields
   254	// to the given time in UTC.
   255	//
   256	// Deprecated: Use Modified instead.
   257	func (h *FileHeader) SetModTime(t time.Time) {
   258		t = t.UTC() // Convert to UTC for compatibility
   259		h.Modified = t
   260		h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
   261	}
   262	
   263	const (
   264		// Unix constants. The specification doesn't mention them,
   265		// but these seem to be the values agreed on by tools.
   266		s_IFMT   = 0xf000
   267		s_IFSOCK = 0xc000
   268		s_IFLNK  = 0xa000
   269		s_IFREG  = 0x8000
   270		s_IFBLK  = 0x6000
   271		s_IFDIR  = 0x4000
   272		s_IFCHR  = 0x2000
   273		s_IFIFO  = 0x1000
   274		s_ISUID  = 0x800
   275		s_ISGID  = 0x400
   276		s_ISVTX  = 0x200
   277	
   278		msdosDir      = 0x10
   279		msdosReadOnly = 0x01
   280	)
   281	
   282	// Mode returns the permission and mode bits for the FileHeader.
   283	func (h *FileHeader) Mode() (mode os.FileMode) {
   284		switch h.CreatorVersion >> 8 {
   285		case creatorUnix, creatorMacOSX:
   286			mode = unixModeToFileMode(h.ExternalAttrs >> 16)
   287		case creatorNTFS, creatorVFAT, creatorFAT:
   288			mode = msdosModeToFileMode(h.ExternalAttrs)
   289		}
   290		if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
   291			mode |= os.ModeDir
   292		}
   293		return mode
   294	}
   295	
   296	// SetMode changes the permission and mode bits for the FileHeader.
   297	func (h *FileHeader) SetMode(mode os.FileMode) {
   298		h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
   299		h.ExternalAttrs = fileModeToUnixMode(mode) << 16
   300	
   301		// set MSDOS attributes too, as the original zip does.
   302		if mode&os.ModeDir != 0 {
   303			h.ExternalAttrs |= msdosDir
   304		}
   305		if mode&0200 == 0 {
   306			h.ExternalAttrs |= msdosReadOnly
   307		}
   308	}
   309	
   310	// isZip64 reports whether the file size exceeds the 32 bit limit
   311	func (h *FileHeader) isZip64() bool {
   312		return h.CompressedSize64 >= uint32max || h.UncompressedSize64 >= uint32max
   313	}
   314	
   315	func msdosModeToFileMode(m uint32) (mode os.FileMode) {
   316		if m&msdosDir != 0 {
   317			mode = os.ModeDir | 0777
   318		} else {
   319			mode = 0666
   320		}
   321		if m&msdosReadOnly != 0 {
   322			mode &^= 0222
   323		}
   324		return mode
   325	}
   326	
   327	func fileModeToUnixMode(mode os.FileMode) uint32 {
   328		var m uint32
   329		switch mode & os.ModeType {
   330		default:
   331			m = s_IFREG
   332		case os.ModeDir:
   333			m = s_IFDIR
   334		case os.ModeSymlink:
   335			m = s_IFLNK
   336		case os.ModeNamedPipe:
   337			m = s_IFIFO
   338		case os.ModeSocket:
   339			m = s_IFSOCK
   340		case os.ModeDevice:
   341			if mode&os.ModeCharDevice != 0 {
   342				m = s_IFCHR
   343			} else {
   344				m = s_IFBLK
   345			}
   346		}
   347		if mode&os.ModeSetuid != 0 {
   348			m |= s_ISUID
   349		}
   350		if mode&os.ModeSetgid != 0 {
   351			m |= s_ISGID
   352		}
   353		if mode&os.ModeSticky != 0 {
   354			m |= s_ISVTX
   355		}
   356		return m | uint32(mode&0777)
   357	}
   358	
   359	func unixModeToFileMode(m uint32) os.FileMode {
   360		mode := os.FileMode(m & 0777)
   361		switch m & s_IFMT {
   362		case s_IFBLK:
   363			mode |= os.ModeDevice
   364		case s_IFCHR:
   365			mode |= os.ModeDevice | os.ModeCharDevice
   366		case s_IFDIR:
   367			mode |= os.ModeDir
   368		case s_IFIFO:
   369			mode |= os.ModeNamedPipe
   370		case s_IFLNK:
   371			mode |= os.ModeSymlink
   372		case s_IFREG:
   373			// nothing to do
   374		case s_IFSOCK:
   375			mode |= os.ModeSocket
   376		}
   377		if m&s_ISGID != 0 {
   378			mode |= os.ModeSetgid
   379		}
   380		if m&s_ISUID != 0 {
   381			mode |= os.ModeSetuid
   382		}
   383		if m&s_ISVTX != 0 {
   384			mode |= os.ModeSticky
   385		}
   386		return mode
   387	}
   388	

View as plain text