src/pkg/archive/zip/struct.go - The Go Programming Language

Golang

Source file src/pkg/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: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
     9	
    10	This package does not support ZIP64 or disk spanning.
    11	*/
    12	package zip
    13	
    14	import (
    15		"errors"
    16		"os"
    17		"time"
    18	)
    19	
    20	// Compression methods.
    21	const (
    22		Store   uint16 = 0
    23		Deflate uint16 = 8
    24	)
    25	
    26	const (
    27		fileHeaderSignature      = 0x04034b50
    28		directoryHeaderSignature = 0x02014b50
    29		directoryEndSignature    = 0x06054b50
    30		dataDescriptorSignature  = 0x08074b50 // de-facto standard; required by OS X Finder
    31		fileHeaderLen            = 30         // + filename + extra
    32		directoryHeaderLen       = 46         // + filename + extra + comment
    33		directoryEndLen          = 22         // + comment
    34		dataDescriptorLen        = 16         // four uint32: descriptor signature, crc32, compressed size, size
    35	
    36		// Constants for the first byte in CreatorVersion
    37		creatorFAT    = 0
    38		creatorUnix   = 3
    39		creatorNTFS   = 11
    40		creatorVFAT   = 14
    41		creatorMacOSX = 19
    42	)
    43	
    44	type FileHeader struct {
    45		Name             string
    46		CreatorVersion   uint16
    47		ReaderVersion    uint16
    48		Flags            uint16
    49		Method           uint16
    50		ModifiedTime     uint16 // MS-DOS time
    51		ModifiedDate     uint16 // MS-DOS date
    52		CRC32            uint32
    53		CompressedSize   uint32
    54		UncompressedSize uint32
    55		Extra            []byte
    56		ExternalAttrs    uint32 // Meaning depends on CreatorVersion
    57		Comment          string
    58	}
    59	
    60	// FileInfo returns an os.FileInfo for the FileHeader.
    61	func (h *FileHeader) FileInfo() os.FileInfo {
    62		return headerFileInfo{h}
    63	}
    64	
    65	// headerFileInfo implements os.FileInfo.
    66	type headerFileInfo struct {
    67		fh *FileHeader
    68	}
    69	
    70	func (fi headerFileInfo) Name() string       { return fi.fh.Name }
    71	func (fi headerFileInfo) Size() int64        { return int64(fi.fh.UncompressedSize) }
    72	func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
    73	func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
    74	func (fi headerFileInfo) Mode() os.FileMode  { return fi.fh.Mode() }
    75	func (fi headerFileInfo) Sys() interface{}   { return fi.fh }
    76	
    77	// FileInfoHeader creates a partially-populated FileHeader from an
    78	// os.FileInfo.
    79	func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
    80		size := fi.Size()
    81		if size > (1<<32 - 1) {
    82			return nil, errors.New("zip: file over 4GB")
    83		}
    84		fh := &FileHeader{
    85			Name:             fi.Name(),
    86			UncompressedSize: uint32(size),
    87		}
    88		fh.SetModTime(fi.ModTime())
    89		fh.SetMode(fi.Mode())
    90		return fh, nil
    91	}
    92	
    93	type directoryEnd struct {
    94		diskNbr            uint16 // unused
    95		dirDiskNbr         uint16 // unused
    96		dirRecordsThisDisk uint16 // unused
    97		directoryRecords   uint16
    98		directorySize      uint32
    99		directoryOffset    uint32 // relative to file
   100		commentLen         uint16
   101		comment            string
   102	}
   103	
   104	// msDosTimeToTime converts an MS-DOS date and time into a time.Time.
   105	// The resolution is 2s.
   106	// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
   107	func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
   108		return time.Date(
   109			// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
   110			int(dosDate>>9+1980),
   111			time.Month(dosDate>>5&0xf),
   112			int(dosDate&0x1f),
   113	
   114			// time bits 0-4: second/2; 5-10: minute; 11-15: hour
   115			int(dosTime>>11),
   116			int(dosTime>>5&0x3f),
   117			int(dosTime&0x1f*2),
   118			0, // nanoseconds
   119	
   120			time.UTC,
   121		)
   122	}
   123	
   124	// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
   125	// The resolution is 2s.
   126	// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
   127	func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
   128		t = t.In(time.UTC)
   129		fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
   130		fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
   131		return
   132	}
   133	
   134	// ModTime returns the modification time.
   135	// The resolution is 2s.
   136	func (h *FileHeader) ModTime() time.Time {
   137		return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
   138	}
   139	
   140	// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.
   141	// The resolution is 2s.
   142	func (h *FileHeader) SetModTime(t time.Time) {
   143		h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
   144	}
   145	
   146	const (
   147		// Unix constants. The specification doesn't mention them,
   148		// but these seem to be the values agreed on by tools.
   149		s_IFMT   = 0xf000
   150		s_IFSOCK = 0xc000
   151		s_IFLNK  = 0xa000
   152		s_IFREG  = 0x8000
   153		s_IFBLK  = 0x6000
   154		s_IFDIR  = 0x4000
   155		s_IFCHR  = 0x2000
   156		s_IFIFO  = 0x1000
   157		s_ISUID  = 0x800
   158		s_ISGID  = 0x400
   159		s_ISVTX  = 0x200
   160	
   161		msdosDir      = 0x10
   162		msdosReadOnly = 0x01
   163	)
   164	
   165	// Mode returns the permission and mode bits for the FileHeader.
   166	func (h *FileHeader) Mode() (mode os.FileMode) {
   167		switch h.CreatorVersion >> 8 {
   168		case creatorUnix, creatorMacOSX:
   169			mode = unixModeToFileMode(h.ExternalAttrs >> 16)
   170		case creatorNTFS, creatorVFAT, creatorFAT:
   171			mode = msdosModeToFileMode(h.ExternalAttrs)
   172		}
   173		if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
   174			mode |= os.ModeDir
   175		}
   176		return mode
   177	}
   178	
   179	// SetMode changes the permission and mode bits for the FileHeader.
   180	func (h *FileHeader) SetMode(mode os.FileMode) {
   181		h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
   182		h.ExternalAttrs = fileModeToUnixMode(mode) << 16
   183	
   184		// set MSDOS attributes too, as the original zip does.
   185		if mode&os.ModeDir != 0 {
   186			h.ExternalAttrs |= msdosDir
   187		}
   188		if mode&0200 == 0 {
   189			h.ExternalAttrs |= msdosReadOnly
   190		}
   191	}
   192	
   193	func msdosModeToFileMode(m uint32) (mode os.FileMode) {
   194		if m&msdosDir != 0 {
   195			mode = os.ModeDir | 0777
   196		} else {
   197			mode = 0666
   198		}
   199		if m&msdosReadOnly != 0 {
   200			mode &^= 0222
   201		}
   202		return mode
   203	}
   204	
   205	func fileModeToUnixMode(mode os.FileMode) uint32 {
   206		var m uint32
   207		switch mode & os.ModeType {
   208		default:
   209			m = s_IFREG
   210		case os.ModeDir:
   211			m = s_IFDIR
   212		case os.ModeSymlink:
   213			m = s_IFLNK
   214		case os.ModeNamedPipe:
   215			m = s_IFIFO
   216		case os.ModeSocket:
   217			m = s_IFSOCK
   218		case os.ModeDevice:
   219			if mode&os.ModeCharDevice != 0 {
   220				m = s_IFCHR
   221			} else {
   222				m = s_IFBLK
   223			}
   224		}
   225		if mode&os.ModeSetuid != 0 {
   226			m |= s_ISUID
   227		}
   228		if mode&os.ModeSetgid != 0 {
   229			m |= s_ISGID
   230		}
   231		if mode&os.ModeSticky != 0 {
   232			m |= s_ISVTX
   233		}
   234		return m | uint32(mode&0777)
   235	}
   236	
   237	func unixModeToFileMode(m uint32) os.FileMode {
   238		mode := os.FileMode(m & 0777)
   239		switch m & s_IFMT {
   240		case s_IFBLK:
   241			mode |= os.ModeDevice
   242		case s_IFCHR:
   243			mode |= os.ModeDevice | os.ModeCharDevice
   244		case s_IFDIR:
   245			mode |= os.ModeDir
   246		case s_IFIFO:
   247			mode |= os.ModeNamedPipe
   248		case s_IFLNK:
   249			mode |= os.ModeSymlink
   250		case s_IFREG:
   251			// nothing to do
   252		case s_IFSOCK:
   253			mode |= os.ModeSocket
   254		}
   255		if m&s_ISGID != 0 {
   256			mode |= os.ModeSetgid
   257		}
   258		if m&s_ISUID != 0 {
   259			mode |= os.ModeSetuid
   260		}
   261		if m&s_ISVTX != 0 {
   262			mode |= os.ModeSticky
   263		}
   264		return mode
   265	}