github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/src/archive/tar/common.go (about)

     1  // Copyright 2009 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  // Package tar implements access to tar archives.
     6  //
     7  // Tape archives (tar) are a file format for storing a sequence of files that
     8  // can be read and written in a streaming manner.
     9  // This package aims to cover most variations of the format,
    10  // including those produced by GNU and BSD tar tools.
    11  package tar
    12  
    13  import (
    14  	"errors"
    15  	"fmt"
    16  	"math"
    17  	"os"
    18  	"path"
    19  	"reflect"
    20  	"strconv"
    21  	"strings"
    22  	"time"
    23  )
    24  
    25  // BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit
    26  // architectures. If a large value is encountered when decoding, the result
    27  // stored in Header will be the truncated version.
    28  
    29  var (
    30  	ErrHeader          = errors.New("tar: invalid tar header")
    31  	ErrWriteTooLong    = errors.New("tar: write too long")
    32  	ErrFieldTooLong    = errors.New("tar: header field too long")
    33  	ErrWriteAfterClose = errors.New("tar: write after close")
    34  	errMissData        = errors.New("tar: sparse file references non-existent data")
    35  	errUnrefData       = errors.New("tar: sparse file contains unreferenced data")
    36  	errWriteHole       = errors.New("tar: write non-NUL byte in sparse hole")
    37  )
    38  
    39  type headerError []string
    40  
    41  func (he headerError) Error() string {
    42  	const prefix = "tar: cannot encode header"
    43  	var ss []string
    44  	for _, s := range he {
    45  		if s != "" {
    46  			ss = append(ss, s)
    47  		}
    48  	}
    49  	if len(ss) == 0 {
    50  		return prefix
    51  	}
    52  	return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and "))
    53  }
    54  
    55  // Type flags for Header.Typeflag.
    56  const (
    57  	// Type '0' indicates a regular file.
    58  	TypeReg  = '0'
    59  	TypeRegA = '\x00' // For legacy support; use TypeReg instead
    60  
    61  	// Type '1' to '6' are header-only flags and may not have a data body.
    62  	TypeLink    = '1' // Hard link
    63  	TypeSymlink = '2' // Symbolic link
    64  	TypeChar    = '3' // Character device node
    65  	TypeBlock   = '4' // Block device node
    66  	TypeDir     = '5' // Directory
    67  	TypeFifo    = '6' // FIFO node
    68  
    69  	// Type '7' is reserved.
    70  	TypeCont = '7'
    71  
    72  	// Type 'x' is used by the PAX format to store key-value records that
    73  	// are only relevant to the next file.
    74  	// This package transparently handles these types.
    75  	TypeXHeader = 'x'
    76  
    77  	// Type 'g' is used by the PAX format to store key-value records that
    78  	// are relevant to all subsequent files.
    79  	// This package only supports parsing and composing such headers,
    80  	// but does not currently support persisting the global state across files.
    81  	TypeXGlobalHeader = 'g'
    82  
    83  	// Type 'S' indicates a sparse file in the GNU format.
    84  	// Header.SparseHoles should be populated when using this type.
    85  	TypeGNUSparse = 'S'
    86  
    87  	// Types 'L' and 'K' are used by the GNU format for a meta file
    88  	// used to store the path or link name for the next file.
    89  	// This package transparently handles these types.
    90  	TypeGNULongName = 'L'
    91  	TypeGNULongLink = 'K'
    92  )
    93  
    94  // Keywords for PAX extended header records.
    95  const (
    96  	paxNone     = "" // Indicates that no PAX key is suitable
    97  	paxPath     = "path"
    98  	paxLinkpath = "linkpath"
    99  	paxSize     = "size"
   100  	paxUid      = "uid"
   101  	paxGid      = "gid"
   102  	paxUname    = "uname"
   103  	paxGname    = "gname"
   104  	paxMtime    = "mtime"
   105  	paxAtime    = "atime"
   106  	paxCtime    = "ctime"   // Removed from later revision of PAX spec, but was valid
   107  	paxCharset  = "charset" // Currently unused
   108  	paxComment  = "comment" // Currently unused
   109  
   110  	paxSchilyXattr = "SCHILY.xattr."
   111  
   112  	// Keywords for GNU sparse files in a PAX extended header.
   113  	paxGNUSparse          = "GNU.sparse."
   114  	paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
   115  	paxGNUSparseOffset    = "GNU.sparse.offset"
   116  	paxGNUSparseNumBytes  = "GNU.sparse.numbytes"
   117  	paxGNUSparseMap       = "GNU.sparse.map"
   118  	paxGNUSparseName      = "GNU.sparse.name"
   119  	paxGNUSparseMajor     = "GNU.sparse.major"
   120  	paxGNUSparseMinor     = "GNU.sparse.minor"
   121  	paxGNUSparseSize      = "GNU.sparse.size"
   122  	paxGNUSparseRealSize  = "GNU.sparse.realsize"
   123  )
   124  
   125  // basicKeys is a set of the PAX keys for which we have built-in support.
   126  // This does not contain "charset" or "comment", which are both PAX-specific,
   127  // so adding them as first-class features of Header is unlikely.
   128  // Users can use the PAXRecords field to set it themselves.
   129  var basicKeys = map[string]bool{
   130  	paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true,
   131  	paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true,
   132  }
   133  
   134  // A Header represents a single header in a tar archive.
   135  // Some fields may not be populated.
   136  //
   137  // For forward compatibility, users that retrieve a Header from Reader.Next,
   138  // mutate it in some ways, and then pass it back to Writer.WriteHeader
   139  // should do so by creating a new Header and copying the fields
   140  // that they are interested in preserving.
   141  type Header struct {
   142  	Typeflag byte // Type of header entry (should be TypeReg for most files)
   143  
   144  	Name     string // Name of file entry
   145  	Linkname string // Target name of link (valid for TypeLink or TypeSymlink)
   146  
   147  	Size  int64  // Logical file size in bytes
   148  	Mode  int64  // Permission and mode bits
   149  	Uid   int    // User ID of owner
   150  	Gid   int    // Group ID of owner
   151  	Uname string // User name of owner
   152  	Gname string // Group name of owner
   153  
   154  	// The PAX format encodes the timestamps with sub-second resolution,
   155  	// while the other formats (USTAR and GNU) truncate to the nearest second.
   156  	// If the Format is unspecified, then Writer.WriteHeader ignores
   157  	// AccessTime and ChangeTime when using the USTAR format.
   158  	ModTime    time.Time // Modification time
   159  	AccessTime time.Time // Access time (requires either PAX or GNU support)
   160  	ChangeTime time.Time // Change time (requires either PAX or GNU support)
   161  
   162  	Devmajor int64 // Major device number (valid for TypeChar or TypeBlock)
   163  	Devminor int64 // Minor device number (valid for TypeChar or TypeBlock)
   164  
   165  	// SparseHoles represents a sequence of holes in a sparse file.
   166  	//
   167  	// A file is sparse if len(SparseHoles) > 0 or Typeflag is TypeGNUSparse.
   168  	// If TypeGNUSparse is set, then the format is GNU, otherwise
   169  	// the format is PAX (by using GNU-specific PAX records).
   170  	//
   171  	// A sparse file consists of fragments of data, intermixed with holes
   172  	// (described by this field). A hole is semantically a block of NUL-bytes,
   173  	// but does not actually exist within the tar file.
   174  	// The holes must be sorted in ascending order,
   175  	// not overlap with each other, and not extend past the specified Size.
   176  	SparseHoles []SparseEntry
   177  
   178  	// Xattrs stores extended attributes as PAX records under the
   179  	// "SCHILY.xattr." namespace.
   180  	//
   181  	// The following are semantically equivalent:
   182  	//  h.Xattrs[key] = value
   183  	//  h.PAXRecords["SCHILY.xattr."+key] = value
   184  	//
   185  	// When Writer.WriteHeader is called, the contents of Xattrs will take
   186  	// precedence over those in PAXRecords.
   187  	//
   188  	// Deprecated: Use PAXRecords instead.
   189  	Xattrs map[string]string
   190  
   191  	// PAXRecords is a map of PAX extended header records.
   192  	//
   193  	// User-defined records should have keys of the following form:
   194  	//	VENDOR.keyword
   195  	// Where VENDOR is some namespace in all uppercase, and keyword may
   196  	// not contain the '=' character (e.g., "GOLANG.pkg.version").
   197  	// The key and value should be non-empty UTF-8 strings.
   198  	//
   199  	// When Writer.WriteHeader is called, PAX records derived from the
   200  	// the other fields in Header take precedence over PAXRecords.
   201  	PAXRecords map[string]string
   202  
   203  	// Format specifies the format of the tar header.
   204  	//
   205  	// This is set by Reader.Next as a best-effort guess at the format.
   206  	// Since the Reader liberally reads some non-compliant files,
   207  	// it is possible for this to be FormatUnknown.
   208  	//
   209  	// If the format is unspecified when Writer.WriteHeader is called,
   210  	// then it uses the first format (in the order of USTAR, PAX, GNU)
   211  	// capable of encoding this Header (see Format).
   212  	Format Format
   213  }
   214  
   215  // SparseEntry represents a Length-sized fragment at Offset in the file.
   216  type SparseEntry struct{ Offset, Length int64 }
   217  
   218  func (s SparseEntry) endOffset() int64 { return s.Offset + s.Length }
   219  
   220  // A sparse file can be represented as either a sparseDatas or a sparseHoles.
   221  // As long as the total size is known, they are equivalent and one can be
   222  // converted to the other form and back. The various tar formats with sparse
   223  // file support represent sparse files in the sparseDatas form. That is, they
   224  // specify the fragments in the file that has data, and treat everything else as
   225  // having zero bytes. As such, the encoding and decoding logic in this package
   226  // deals with sparseDatas.
   227  //
   228  // However, the external API uses sparseHoles instead of sparseDatas because the
   229  // zero value of sparseHoles logically represents a normal file (i.e., there are
   230  // no holes in it). On the other hand, the zero value of sparseDatas implies
   231  // that the file has no data in it, which is rather odd.
   232  //
   233  // As an example, if the underlying raw file contains the 10-byte data:
   234  //	var compactFile = "abcdefgh"
   235  //
   236  // And the sparse map has the following entries:
   237  //	var spd sparseDatas = []sparseEntry{
   238  //		{Offset: 2,  Length: 5},  // Data fragment for 2..6
   239  //		{Offset: 18, Length: 3},  // Data fragment for 18..20
   240  //	}
   241  //	var sph sparseHoles = []SparseEntry{
   242  //		{Offset: 0,  Length: 2},  // Hole fragment for 0..1
   243  //		{Offset: 7,  Length: 11}, // Hole fragment for 7..17
   244  //		{Offset: 21, Length: 4},  // Hole fragment for 21..24
   245  //	}
   246  //
   247  // Then the content of the resulting sparse file with a Header.Size of 25 is:
   248  //	var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4
   249  type (
   250  	sparseDatas []SparseEntry
   251  	sparseHoles []SparseEntry
   252  )
   253  
   254  // validateSparseEntries reports whether sp is a valid sparse map.
   255  // It does not matter whether sp represents data fragments or hole fragments.
   256  func validateSparseEntries(sp []SparseEntry, size int64) bool {
   257  	// Validate all sparse entries. These are the same checks as performed by
   258  	// the BSD tar utility.
   259  	if size < 0 {
   260  		return false
   261  	}
   262  	var pre SparseEntry
   263  	for _, cur := range sp {
   264  		switch {
   265  		case cur.Offset < 0 || cur.Length < 0:
   266  			return false // Negative values are never okay
   267  		case cur.Offset > math.MaxInt64-cur.Length:
   268  			return false // Integer overflow with large length
   269  		case cur.endOffset() > size:
   270  			return false // Region extends beyond the actual size
   271  		case pre.endOffset() > cur.Offset:
   272  			return false // Regions cannot overlap and must be in order
   273  		}
   274  		pre = cur
   275  	}
   276  	return true
   277  }
   278  
   279  // alignSparseEntries mutates src and returns dst where each fragment's
   280  // starting offset is aligned up to the nearest block edge, and each
   281  // ending offset is aligned down to the nearest block edge.
   282  //
   283  // Even though the Go tar Reader and the BSD tar utility can handle entries
   284  // with arbitrary offsets and lengths, the GNU tar utility can only handle
   285  // offsets and lengths that are multiples of blockSize.
   286  func alignSparseEntries(src []SparseEntry, size int64) []SparseEntry {
   287  	dst := src[:0]
   288  	for _, s := range src {
   289  		pos, end := s.Offset, s.endOffset()
   290  		pos += blockPadding(+pos) // Round-up to nearest blockSize
   291  		if end != size {
   292  			end -= blockPadding(-end) // Round-down to nearest blockSize
   293  		}
   294  		if pos < end {
   295  			dst = append(dst, SparseEntry{Offset: pos, Length: end - pos})
   296  		}
   297  	}
   298  	return dst
   299  }
   300  
   301  // invertSparseEntries converts a sparse map from one form to the other.
   302  // If the input is sparseHoles, then it will output sparseDatas and vice-versa.
   303  // The input must have been already validated.
   304  //
   305  // This function mutates src and returns a normalized map where:
   306  //	* adjacent fragments are coalesced together
   307  //	* only the last fragment may be empty
   308  //	* the endOffset of the last fragment is the total size
   309  func invertSparseEntries(src []SparseEntry, size int64) []SparseEntry {
   310  	dst := src[:0]
   311  	var pre SparseEntry
   312  	for _, cur := range src {
   313  		if cur.Length == 0 {
   314  			continue // Skip empty fragments
   315  		}
   316  		pre.Length = cur.Offset - pre.Offset
   317  		if pre.Length > 0 {
   318  			dst = append(dst, pre) // Only add non-empty fragments
   319  		}
   320  		pre.Offset = cur.endOffset()
   321  	}
   322  	pre.Length = size - pre.Offset // Possibly the only empty fragment
   323  	return append(dst, pre)
   324  }
   325  
   326  type fileState interface {
   327  	// Remaining reports the number of remaining bytes in the current file.
   328  	// This count includes any sparse holes that may exist.
   329  	Remaining() int64
   330  }
   331  
   332  // allowedFormats determines which formats can be used.
   333  // The value returned is the logical OR of multiple possible formats.
   334  // If the value is FormatUnknown, then the input Header cannot be encoded
   335  // and an error is returned explaining why.
   336  //
   337  // As a by-product of checking the fields, this function returns paxHdrs, which
   338  // contain all fields that could not be directly encoded.
   339  func (h *Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) {
   340  	format = FormatUSTAR | FormatPAX | FormatGNU
   341  	paxHdrs = make(map[string]string)
   342  
   343  	var whyNoUSTAR, whyNoPAX, whyNoGNU string
   344  	var preferPAX bool // Prefer PAX over USTAR
   345  	verifyString := func(s string, size int, name, paxKey string) {
   346  		// NUL-terminator is optional for path and linkpath.
   347  		// Technically, it is required for uname and gname,
   348  		// but neither GNU nor BSD tar checks for it.
   349  		tooLong := len(s) > size
   350  		allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath
   351  		if hasNUL(s) || (tooLong && !allowLongGNU) {
   352  			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s)
   353  			format.mustNotBe(FormatGNU)
   354  		}
   355  		if !isASCII(s) || tooLong {
   356  			canSplitUSTAR := paxKey == paxPath
   357  			if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok {
   358  				whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s)
   359  				format.mustNotBe(FormatUSTAR)
   360  			}
   361  			if paxKey == paxNone {
   362  				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s)
   363  				format.mustNotBe(FormatPAX)
   364  			} else {
   365  				paxHdrs[paxKey] = s
   366  			}
   367  		}
   368  		if v, ok := h.PAXRecords[paxKey]; ok && v == s {
   369  			paxHdrs[paxKey] = v
   370  		}
   371  	}
   372  	verifyNumeric := func(n int64, size int, name, paxKey string) {
   373  		if !fitsInBase256(size, n) {
   374  			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n)
   375  			format.mustNotBe(FormatGNU)
   376  		}
   377  		if !fitsInOctal(size, n) {
   378  			whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n)
   379  			format.mustNotBe(FormatUSTAR)
   380  			if paxKey == paxNone {
   381  				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n)
   382  				format.mustNotBe(FormatPAX)
   383  			} else {
   384  				paxHdrs[paxKey] = strconv.FormatInt(n, 10)
   385  			}
   386  		}
   387  		if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) {
   388  			paxHdrs[paxKey] = v
   389  		}
   390  	}
   391  	verifyTime := func(ts time.Time, size int, name, paxKey string) {
   392  		if ts.IsZero() {
   393  			return // Always okay
   394  		}
   395  		if !fitsInBase256(size, ts.Unix()) {
   396  			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts)
   397  			format.mustNotBe(FormatGNU)
   398  		}
   399  		isMtime := paxKey == paxMtime
   400  		fitsOctal := fitsInOctal(size, ts.Unix())
   401  		noACTime := !isMtime && h.Format != FormatUnknown
   402  		if (isMtime && !fitsOctal) || noACTime {
   403  			whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts)
   404  			format.mustNotBe(FormatUSTAR)
   405  		}
   406  		needsNano := ts.Nanosecond() != 0
   407  		if !isMtime || !fitsOctal || needsNano {
   408  			preferPAX = true // USTAR may truncate sub-second measurements
   409  			if paxKey == paxNone {
   410  				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts)
   411  				format.mustNotBe(FormatPAX)
   412  			} else {
   413  				paxHdrs[paxKey] = formatPAXTime(ts)
   414  			}
   415  		}
   416  		if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) {
   417  			paxHdrs[paxKey] = v
   418  		}
   419  	}
   420  
   421  	// Check basic fields.
   422  	var blk block
   423  	v7 := blk.V7()
   424  	ustar := blk.USTAR()
   425  	gnu := blk.GNU()
   426  	verifyString(h.Name, len(v7.Name()), "Name", paxPath)
   427  	verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath)
   428  	verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname)
   429  	verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname)
   430  	verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone)
   431  	verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid)
   432  	verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid)
   433  	verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize)
   434  	verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone)
   435  	verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone)
   436  	verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime)
   437  	verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime)
   438  	verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime)
   439  
   440  	// Check for header-only types.
   441  	var whyOnlyPAX, whyOnlyGNU string
   442  	switch h.Typeflag {
   443  	case TypeXHeader, TypeGNULongName, TypeGNULongLink:
   444  		return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"}
   445  	case TypeXGlobalHeader:
   446  		if !reflect.DeepEqual(h, &Header{Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format}) {
   447  			return FormatUnknown, nil, headerError{"only PAXRecords may be set for TypeXGlobalHeader"}
   448  		}
   449  		whyOnlyPAX = "only PAX supports TypeXGlobalHeader"
   450  		format.mayOnlyBe(FormatPAX)
   451  	}
   452  	if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 {
   453  		return FormatUnknown, nil, headerError{"negative size on header-only type"}
   454  	}
   455  
   456  	// Check PAX records.
   457  	if len(h.Xattrs) > 0 {
   458  		for k, v := range h.Xattrs {
   459  			paxHdrs[paxSchilyXattr+k] = v
   460  		}
   461  		whyOnlyPAX = "only PAX supports Xattrs"
   462  		format.mayOnlyBe(FormatPAX)
   463  	}
   464  	if len(h.PAXRecords) > 0 {
   465  		for k, v := range h.PAXRecords {
   466  			switch _, exists := paxHdrs[k]; {
   467  			case exists:
   468  				continue // Do not overwrite existing records
   469  			case h.Typeflag == TypeXGlobalHeader:
   470  				paxHdrs[k] = v // Copy all records
   471  			case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse):
   472  				paxHdrs[k] = v // Ignore local records that may conflict
   473  			}
   474  		}
   475  		whyOnlyPAX = "only PAX supports PAXRecords"
   476  		format.mayOnlyBe(FormatPAX)
   477  	}
   478  	for k, v := range paxHdrs {
   479  		if !validPAXRecord(k, v) {
   480  			return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)}
   481  		}
   482  	}
   483  
   484  	// Check sparse files.
   485  	if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse {
   486  		if isHeaderOnlyType(h.Typeflag) {
   487  			return FormatUnknown, nil, headerError{"header-only type cannot be sparse"}
   488  		}
   489  		if !validateSparseEntries(h.SparseHoles, h.Size) {
   490  			return FormatUnknown, nil, headerError{"invalid sparse holes"}
   491  		}
   492  		if h.Typeflag == TypeGNUSparse {
   493  			whyOnlyGNU = "only GNU supports TypeGNUSparse"
   494  			format.mayOnlyBe(FormatGNU)
   495  		} else {
   496  			whyNoGNU = "GNU supports sparse files only with TypeGNUSparse"
   497  			format.mustNotBe(FormatGNU)
   498  		}
   499  		whyNoUSTAR = "USTAR does not support sparse files"
   500  		format.mustNotBe(FormatUSTAR)
   501  	}
   502  
   503  	// Check desired format.
   504  	if wantFormat := h.Format; wantFormat != FormatUnknown {
   505  		if wantFormat.has(FormatPAX) && !preferPAX {
   506  			wantFormat.mayBe(FormatUSTAR) // PAX implies USTAR allowed too
   507  		}
   508  		format.mayOnlyBe(wantFormat) // Set union of formats allowed and format wanted
   509  	}
   510  	if format == FormatUnknown {
   511  		switch h.Format {
   512  		case FormatUSTAR:
   513  			err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU}
   514  		case FormatPAX:
   515  			err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU}
   516  		case FormatGNU:
   517  			err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX}
   518  		default:
   519  			err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU}
   520  		}
   521  	}
   522  	return format, paxHdrs, err
   523  }
   524  
   525  // FileInfo returns an os.FileInfo for the Header.
   526  func (h *Header) FileInfo() os.FileInfo {
   527  	return headerFileInfo{h}
   528  }
   529  
   530  // headerFileInfo implements os.FileInfo.
   531  type headerFileInfo struct {
   532  	h *Header
   533  }
   534  
   535  func (fi headerFileInfo) Size() int64        { return fi.h.Size }
   536  func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
   537  func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
   538  func (fi headerFileInfo) Sys() interface{}   { return fi.h }
   539  
   540  // Name returns the base name of the file.
   541  func (fi headerFileInfo) Name() string {
   542  	if fi.IsDir() {
   543  		return path.Base(path.Clean(fi.h.Name))
   544  	}
   545  	return path.Base(fi.h.Name)
   546  }
   547  
   548  // Mode returns the permission and mode bits for the headerFileInfo.
   549  func (fi headerFileInfo) Mode() (mode os.FileMode) {
   550  	// Set file permission bits.
   551  	mode = os.FileMode(fi.h.Mode).Perm()
   552  
   553  	// Set setuid, setgid and sticky bits.
   554  	if fi.h.Mode&c_ISUID != 0 {
   555  		mode |= os.ModeSetuid
   556  	}
   557  	if fi.h.Mode&c_ISGID != 0 {
   558  		mode |= os.ModeSetgid
   559  	}
   560  	if fi.h.Mode&c_ISVTX != 0 {
   561  		mode |= os.ModeSticky
   562  	}
   563  
   564  	// Set file mode bits; clear perm, setuid, setgid, and sticky bits.
   565  	switch m := os.FileMode(fi.h.Mode) &^ 07777; m {
   566  	case c_ISDIR:
   567  		mode |= os.ModeDir
   568  	case c_ISFIFO:
   569  		mode |= os.ModeNamedPipe
   570  	case c_ISLNK:
   571  		mode |= os.ModeSymlink
   572  	case c_ISBLK:
   573  		mode |= os.ModeDevice
   574  	case c_ISCHR:
   575  		mode |= os.ModeDevice
   576  		mode |= os.ModeCharDevice
   577  	case c_ISSOCK:
   578  		mode |= os.ModeSocket
   579  	}
   580  
   581  	switch fi.h.Typeflag {
   582  	case TypeSymlink:
   583  		mode |= os.ModeSymlink
   584  	case TypeChar:
   585  		mode |= os.ModeDevice
   586  		mode |= os.ModeCharDevice
   587  	case TypeBlock:
   588  		mode |= os.ModeDevice
   589  	case TypeDir:
   590  		mode |= os.ModeDir
   591  	case TypeFifo:
   592  		mode |= os.ModeNamedPipe
   593  	}
   594  
   595  	return mode
   596  }
   597  
   598  // sysStat, if non-nil, populates h from system-dependent fields of fi.
   599  var sysStat func(fi os.FileInfo, h *Header) error
   600  
   601  const (
   602  	// Mode constants from the USTAR spec:
   603  	// See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
   604  	c_ISUID = 04000 // Set uid
   605  	c_ISGID = 02000 // Set gid
   606  	c_ISVTX = 01000 // Save text (sticky bit)
   607  
   608  	// Common Unix mode constants; these are not defined in any common tar standard.
   609  	// Header.FileInfo understands these, but FileInfoHeader will never produce these.
   610  	c_ISDIR  = 040000  // Directory
   611  	c_ISFIFO = 010000  // FIFO
   612  	c_ISREG  = 0100000 // Regular file
   613  	c_ISLNK  = 0120000 // Symbolic link
   614  	c_ISBLK  = 060000  // Block special file
   615  	c_ISCHR  = 020000  // Character special file
   616  	c_ISSOCK = 0140000 // Socket
   617  )
   618  
   619  // FileInfoHeader creates a partially-populated Header from fi.
   620  // If fi describes a symlink, FileInfoHeader records link as the link target.
   621  // If fi describes a directory, a slash is appended to the name.
   622  //
   623  // Since os.FileInfo's Name method only returns the base name of
   624  // the file it describes, it may be necessary to modify Header.Name
   625  // to provide the full path name of the file.
   626  //
   627  // This function does not populate Header.SparseHoles.
   628  func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
   629  	if fi == nil {
   630  		return nil, errors.New("tar: FileInfo is nil")
   631  	}
   632  	fm := fi.Mode()
   633  	h := &Header{
   634  		Name:    fi.Name(),
   635  		ModTime: fi.ModTime(),
   636  		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
   637  	}
   638  	switch {
   639  	case fm.IsRegular():
   640  		h.Typeflag = TypeReg
   641  		h.Size = fi.Size()
   642  	case fi.IsDir():
   643  		h.Typeflag = TypeDir
   644  		h.Name += "/"
   645  	case fm&os.ModeSymlink != 0:
   646  		h.Typeflag = TypeSymlink
   647  		h.Linkname = link
   648  	case fm&os.ModeDevice != 0:
   649  		if fm&os.ModeCharDevice != 0 {
   650  			h.Typeflag = TypeChar
   651  		} else {
   652  			h.Typeflag = TypeBlock
   653  		}
   654  	case fm&os.ModeNamedPipe != 0:
   655  		h.Typeflag = TypeFifo
   656  	case fm&os.ModeSocket != 0:
   657  		return nil, fmt.Errorf("tar: sockets not supported")
   658  	default:
   659  		return nil, fmt.Errorf("tar: unknown file mode %v", fm)
   660  	}
   661  	if fm&os.ModeSetuid != 0 {
   662  		h.Mode |= c_ISUID
   663  	}
   664  	if fm&os.ModeSetgid != 0 {
   665  		h.Mode |= c_ISGID
   666  	}
   667  	if fm&os.ModeSticky != 0 {
   668  		h.Mode |= c_ISVTX
   669  	}
   670  	// If possible, populate additional fields from OS-specific
   671  	// FileInfo fields.
   672  	if sys, ok := fi.Sys().(*Header); ok {
   673  		// This FileInfo came from a Header (not the OS). Use the
   674  		// original Header to populate all remaining fields.
   675  		h.Uid = sys.Uid
   676  		h.Gid = sys.Gid
   677  		h.Uname = sys.Uname
   678  		h.Gname = sys.Gname
   679  		h.AccessTime = sys.AccessTime
   680  		h.ChangeTime = sys.ChangeTime
   681  		if sys.Xattrs != nil {
   682  			h.Xattrs = make(map[string]string)
   683  			for k, v := range sys.Xattrs {
   684  				h.Xattrs[k] = v
   685  			}
   686  		}
   687  		if sys.Typeflag == TypeLink {
   688  			// hard link
   689  			h.Typeflag = TypeLink
   690  			h.Size = 0
   691  			h.Linkname = sys.Linkname
   692  		}
   693  		if sys.SparseHoles != nil {
   694  			h.SparseHoles = append([]SparseEntry{}, sys.SparseHoles...)
   695  		}
   696  		if sys.PAXRecords != nil {
   697  			h.PAXRecords = make(map[string]string)
   698  			for k, v := range sys.PAXRecords {
   699  				h.PAXRecords[k] = v
   700  			}
   701  		}
   702  	}
   703  	if sysStat != nil {
   704  		return h, sysStat(fi, h)
   705  	}
   706  	return h, nil
   707  }
   708  
   709  // isHeaderOnlyType checks if the given type flag is of the type that has no
   710  // data section even if a size is specified.
   711  func isHeaderOnlyType(flag byte) bool {
   712  	switch flag {
   713  	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
   714  		return true
   715  	default:
   716  		return false
   717  	}
   718  }
   719  
   720  func min(a, b int64) int64 {
   721  	if a < b {
   722  		return a
   723  	}
   724  	return b
   725  }