...
Run Format

Source file src/archive/tar/common.go

Documentation: archive/tar

     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("archive/tar: invalid tar header")
    31  	ErrWriteTooLong    = errors.New("archive/tar: write too long")
    32  	ErrFieldTooLong    = errors.New("archive/tar: header field too long")
    33  	ErrWriteAfterClose = errors.New("archive/tar: write after close")
    34  	errMissData        = errors.New("archive/tar: sparse file references non-existent data")
    35  	errUnrefData       = errors.New("archive/tar: sparse file contains unreferenced data")
    36  	errWriteHole       = errors.New("archive/tar: write non-NUL byte in sparse hole")
    37  )
    38  
    39  type headerError []string
    40  
    41  func (he headerError) Error() string {
    42  	const prefix = "archive/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  	TypeGNUSparse = 'S'
    85  
    86  	// Types 'L' and 'K' are used by the GNU format for a meta file
    87  	// used to store the path or link name for the next file.
    88  	// This package transparently handles these types.
    89  	TypeGNULongName = 'L'
    90  	TypeGNULongLink = 'K'
    91  )
    92  
    93  // Keywords for PAX extended header records.
    94  const (
    95  	paxNone     = "" // Indicates that no PAX key is suitable
    96  	paxPath     = "path"
    97  	paxLinkpath = "linkpath"
    98  	paxSize     = "size"
    99  	paxUid      = "uid"
   100  	paxGid      = "gid"
   101  	paxUname    = "uname"
   102  	paxGname    = "gname"
   103  	paxMtime    = "mtime"
   104  	paxAtime    = "atime"
   105  	paxCtime    = "ctime"   // Removed from later revision of PAX spec, but was valid
   106  	paxCharset  = "charset" // Currently unused
   107  	paxComment  = "comment" // Currently unused
   108  
   109  	paxSchilyXattr = "SCHILY.xattr."
   110  
   111  	// Keywords for GNU sparse files in a PAX extended header.
   112  	paxGNUSparse          = "GNU.sparse."
   113  	paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
   114  	paxGNUSparseOffset    = "GNU.sparse.offset"
   115  	paxGNUSparseNumBytes  = "GNU.sparse.numbytes"
   116  	paxGNUSparseMap       = "GNU.sparse.map"
   117  	paxGNUSparseName      = "GNU.sparse.name"
   118  	paxGNUSparseMajor     = "GNU.sparse.major"
   119  	paxGNUSparseMinor     = "GNU.sparse.minor"
   120  	paxGNUSparseSize      = "GNU.sparse.size"
   121  	paxGNUSparseRealSize  = "GNU.sparse.realsize"
   122  )
   123  
   124  // basicKeys is a set of the PAX keys for which we have built-in support.
   125  // This does not contain "charset" or "comment", which are both PAX-specific,
   126  // so adding them as first-class features of Header is unlikely.
   127  // Users can use the PAXRecords field to set it themselves.
   128  var basicKeys = map[string]bool{
   129  	paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true,
   130  	paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true,
   131  }
   132  
   133  // A Header represents a single header in a tar archive.
   134  // Some fields may not be populated.
   135  //
   136  // For forward compatibility, users that retrieve a Header from Reader.Next,
   137  // mutate it in some ways, and then pass it back to Writer.WriteHeader
   138  // should do so by creating a new Header and copying the fields
   139  // that they are interested in preserving.
   140  type Header struct {
   141  	Typeflag byte // Type of header entry (should be TypeReg for most files)
   142  
   143  	Name     string // Name of file entry
   144  	Linkname string // Target name of link (valid for TypeLink or TypeSymlink)
   145  
   146  	Size  int64  // Logical file size in bytes
   147  	Mode  int64  // Permission and mode bits
   148  	Uid   int    // User ID of owner
   149  	Gid   int    // Group ID of owner
   150  	Uname string // User name of owner
   151  	Gname string // Group name of owner
   152  
   153  	// If the Format is unspecified, then Writer.WriteHeader rounds ModTime
   154  	// to the nearest second and ignores the AccessTime and ChangeTime fields.
   155  	//
   156  	// To use AccessTime or ChangeTime, specify the Format as PAX or GNU.
   157  	// To use sub-second resolution, specify the Format as PAX.
   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  	// Xattrs stores extended attributes as PAX records under the
   166  	// "SCHILY.xattr." namespace.
   167  	//
   168  	// The following are semantically equivalent:
   169  	//  h.Xattrs[key] = value
   170  	//  h.PAXRecords["SCHILY.xattr."+key] = value
   171  	//
   172  	// When Writer.WriteHeader is called, the contents of Xattrs will take
   173  	// precedence over those in PAXRecords.
   174  	//
   175  	// Deprecated: Use PAXRecords instead.
   176  	Xattrs map[string]string
   177  
   178  	// PAXRecords is a map of PAX extended header records.
   179  	//
   180  	// User-defined records should have keys of the following form:
   181  	//	VENDOR.keyword
   182  	// Where VENDOR is some namespace in all uppercase, and keyword may
   183  	// not contain the '=' character (e.g., "GOLANG.pkg.version").
   184  	// The key and value should be non-empty UTF-8 strings.
   185  	//
   186  	// When Writer.WriteHeader is called, PAX records derived from the
   187  	// the other fields in Header take precedence over PAXRecords.
   188  	PAXRecords map[string]string
   189  
   190  	// Format specifies the format of the tar header.
   191  	//
   192  	// This is set by Reader.Next as a best-effort guess at the format.
   193  	// Since the Reader liberally reads some non-compliant files,
   194  	// it is possible for this to be FormatUnknown.
   195  	//
   196  	// If the format is unspecified when Writer.WriteHeader is called,
   197  	// then it uses the first format (in the order of USTAR, PAX, GNU)
   198  	// capable of encoding this Header (see Format).
   199  	Format Format
   200  }
   201  
   202  // sparseEntry represents a Length-sized fragment at Offset in the file.
   203  type sparseEntry struct{ Offset, Length int64 }
   204  
   205  func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length }
   206  
   207  // A sparse file can be represented as either a sparseDatas or a sparseHoles.
   208  // As long as the total size is known, they are equivalent and one can be
   209  // converted to the other form and back. The various tar formats with sparse
   210  // file support represent sparse files in the sparseDatas form. That is, they
   211  // specify the fragments in the file that has data, and treat everything else as
   212  // having zero bytes. As such, the encoding and decoding logic in this package
   213  // deals with sparseDatas.
   214  //
   215  // However, the external API uses sparseHoles instead of sparseDatas because the
   216  // zero value of sparseHoles logically represents a normal file (i.e., there are
   217  // no holes in it). On the other hand, the zero value of sparseDatas implies
   218  // that the file has no data in it, which is rather odd.
   219  //
   220  // As an example, if the underlying raw file contains the 10-byte data:
   221  //	var compactFile = "abcdefgh"
   222  //
   223  // And the sparse map has the following entries:
   224  //	var spd sparseDatas = []sparseEntry{
   225  //		{Offset: 2,  Length: 5},  // Data fragment for 2..6
   226  //		{Offset: 18, Length: 3},  // Data fragment for 18..20
   227  //	}
   228  //	var sph sparseHoles = []sparseEntry{
   229  //		{Offset: 0,  Length: 2},  // Hole fragment for 0..1
   230  //		{Offset: 7,  Length: 11}, // Hole fragment for 7..17
   231  //		{Offset: 21, Length: 4},  // Hole fragment for 21..24
   232  //	}
   233  //
   234  // Then the content of the resulting sparse file with a Header.Size of 25 is:
   235  //	var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4
   236  type (
   237  	sparseDatas []sparseEntry
   238  	sparseHoles []sparseEntry
   239  )
   240  
   241  // validateSparseEntries reports whether sp is a valid sparse map.
   242  // It does not matter whether sp represents data fragments or hole fragments.
   243  func validateSparseEntries(sp []sparseEntry, size int64) bool {
   244  	// Validate all sparse entries. These are the same checks as performed by
   245  	// the BSD tar utility.
   246  	if size < 0 {
   247  		return false
   248  	}
   249  	var pre sparseEntry
   250  	for _, cur := range sp {
   251  		switch {
   252  		case cur.Offset < 0 || cur.Length < 0:
   253  			return false // Negative values are never okay
   254  		case cur.Offset > math.MaxInt64-cur.Length:
   255  			return false // Integer overflow with large length
   256  		case cur.endOffset() > size:
   257  			return false // Region extends beyond the actual size
   258  		case pre.endOffset() > cur.Offset:
   259  			return false // Regions cannot overlap and must be in order
   260  		}
   261  		pre = cur
   262  	}
   263  	return true
   264  }
   265  
   266  // alignSparseEntries mutates src and returns dst where each fragment's
   267  // starting offset is aligned up to the nearest block edge, and each
   268  // ending offset is aligned down to the nearest block edge.
   269  //
   270  // Even though the Go tar Reader and the BSD tar utility can handle entries
   271  // with arbitrary offsets and lengths, the GNU tar utility can only handle
   272  // offsets and lengths that are multiples of blockSize.
   273  func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry {
   274  	dst := src[:0]
   275  	for _, s := range src {
   276  		pos, end := s.Offset, s.endOffset()
   277  		pos += blockPadding(+pos) // Round-up to nearest blockSize
   278  		if end != size {
   279  			end -= blockPadding(-end) // Round-down to nearest blockSize
   280  		}
   281  		if pos < end {
   282  			dst = append(dst, sparseEntry{Offset: pos, Length: end - pos})
   283  		}
   284  	}
   285  	return dst
   286  }
   287  
   288  // invertSparseEntries converts a sparse map from one form to the other.
   289  // If the input is sparseHoles, then it will output sparseDatas and vice-versa.
   290  // The input must have been already validated.
   291  //
   292  // This function mutates src and returns a normalized map where:
   293  //	* adjacent fragments are coalesced together
   294  //	* only the last fragment may be empty
   295  //	* the endOffset of the last fragment is the total size
   296  func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry {
   297  	dst := src[:0]
   298  	var pre sparseEntry
   299  	for _, cur := range src {
   300  		if cur.Length == 0 {
   301  			continue // Skip empty fragments
   302  		}
   303  		pre.Length = cur.Offset - pre.Offset
   304  		if pre.Length > 0 {
   305  			dst = append(dst, pre) // Only add non-empty fragments
   306  		}
   307  		pre.Offset = cur.endOffset()
   308  	}
   309  	pre.Length = size - pre.Offset // Possibly the only empty fragment
   310  	return append(dst, pre)
   311  }
   312  
   313  // fileState tracks the number of logical (includes sparse holes) and physical
   314  // (actual in tar archive) bytes remaining for the current file.
   315  //
   316  // Invariant: LogicalRemaining >= PhysicalRemaining
   317  type fileState interface {
   318  	LogicalRemaining() int64
   319  	PhysicalRemaining() int64
   320  }
   321  
   322  // allowedFormats determines which formats can be used.
   323  // The value returned is the logical OR of multiple possible formats.
   324  // If the value is FormatUnknown, then the input Header cannot be encoded
   325  // and an error is returned explaining why.
   326  //
   327  // As a by-product of checking the fields, this function returns paxHdrs, which
   328  // contain all fields that could not be directly encoded.
   329  // A value receiver ensures that this method does not mutate the source Header.
   330  func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) {
   331  	format = FormatUSTAR | FormatPAX | FormatGNU
   332  	paxHdrs = make(map[string]string)
   333  
   334  	var whyNoUSTAR, whyNoPAX, whyNoGNU string
   335  	var preferPAX bool // Prefer PAX over USTAR
   336  	verifyString := func(s string, size int, name, paxKey string) {
   337  		// NUL-terminator is optional for path and linkpath.
   338  		// Technically, it is required for uname and gname,
   339  		// but neither GNU nor BSD tar checks for it.
   340  		tooLong := len(s) > size
   341  		allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath
   342  		if hasNUL(s) || (tooLong && !allowLongGNU) {
   343  			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s)
   344  			format.mustNotBe(FormatGNU)
   345  		}
   346  		if !isASCII(s) || tooLong {
   347  			canSplitUSTAR := paxKey == paxPath
   348  			if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok {
   349  				whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s)
   350  				format.mustNotBe(FormatUSTAR)
   351  			}
   352  			if paxKey == paxNone {
   353  				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s)
   354  				format.mustNotBe(FormatPAX)
   355  			} else {
   356  				paxHdrs[paxKey] = s
   357  			}
   358  		}
   359  		if v, ok := h.PAXRecords[paxKey]; ok && v == s {
   360  			paxHdrs[paxKey] = v
   361  		}
   362  	}
   363  	verifyNumeric := func(n int64, size int, name, paxKey string) {
   364  		if !fitsInBase256(size, n) {
   365  			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n)
   366  			format.mustNotBe(FormatGNU)
   367  		}
   368  		if !fitsInOctal(size, n) {
   369  			whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n)
   370  			format.mustNotBe(FormatUSTAR)
   371  			if paxKey == paxNone {
   372  				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n)
   373  				format.mustNotBe(FormatPAX)
   374  			} else {
   375  				paxHdrs[paxKey] = strconv.FormatInt(n, 10)
   376  			}
   377  		}
   378  		if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) {
   379  			paxHdrs[paxKey] = v
   380  		}
   381  	}
   382  	verifyTime := func(ts time.Time, size int, name, paxKey string) {
   383  		if ts.IsZero() {
   384  			return // Always okay
   385  		}
   386  		if !fitsInBase256(size, ts.Unix()) {
   387  			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts)
   388  			format.mustNotBe(FormatGNU)
   389  		}
   390  		isMtime := paxKey == paxMtime
   391  		fitsOctal := fitsInOctal(size, ts.Unix())
   392  		if (isMtime && !fitsOctal) || !isMtime {
   393  			whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts)
   394  			format.mustNotBe(FormatUSTAR)
   395  		}
   396  		needsNano := ts.Nanosecond() != 0
   397  		if !isMtime || !fitsOctal || needsNano {
   398  			preferPAX = true // USTAR may truncate sub-second measurements
   399  			if paxKey == paxNone {
   400  				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts)
   401  				format.mustNotBe(FormatPAX)
   402  			} else {
   403  				paxHdrs[paxKey] = formatPAXTime(ts)
   404  			}
   405  		}
   406  		if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) {
   407  			paxHdrs[paxKey] = v
   408  		}
   409  	}
   410  
   411  	// Check basic fields.
   412  	var blk block
   413  	v7 := blk.V7()
   414  	ustar := blk.USTAR()
   415  	gnu := blk.GNU()
   416  	verifyString(h.Name, len(v7.Name()), "Name", paxPath)
   417  	verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath)
   418  	verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname)
   419  	verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname)
   420  	verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone)
   421  	verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid)
   422  	verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid)
   423  	verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize)
   424  	verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone)
   425  	verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone)
   426  	verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime)
   427  	verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime)
   428  	verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime)
   429  
   430  	// Check for header-only types.
   431  	var whyOnlyPAX, whyOnlyGNU string
   432  	switch h.Typeflag {
   433  	case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse:
   434  		// Exclude TypeLink and TypeSymlink, since they may reference directories.
   435  		if strings.HasSuffix(h.Name, "/") {
   436  			return FormatUnknown, nil, headerError{"filename may not have trailing slash"}
   437  		}
   438  	case TypeXHeader, TypeGNULongName, TypeGNULongLink:
   439  		return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"}
   440  	case TypeXGlobalHeader:
   441  		h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format}
   442  		if !reflect.DeepEqual(h, h2) {
   443  			return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"}
   444  		}
   445  		whyOnlyPAX = "only PAX supports TypeXGlobalHeader"
   446  		format.mayOnlyBe(FormatPAX)
   447  	}
   448  	if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 {
   449  		return FormatUnknown, nil, headerError{"negative size on header-only type"}
   450  	}
   451  
   452  	// Check PAX records.
   453  	if len(h.Xattrs) > 0 {
   454  		for k, v := range h.Xattrs {
   455  			paxHdrs[paxSchilyXattr+k] = v
   456  		}
   457  		whyOnlyPAX = "only PAX supports Xattrs"
   458  		format.mayOnlyBe(FormatPAX)
   459  	}
   460  	if len(h.PAXRecords) > 0 {
   461  		for k, v := range h.PAXRecords {
   462  			switch _, exists := paxHdrs[k]; {
   463  			case exists:
   464  				continue // Do not overwrite existing records
   465  			case h.Typeflag == TypeXGlobalHeader:
   466  				paxHdrs[k] = v // Copy all records
   467  			case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse):
   468  				paxHdrs[k] = v // Ignore local records that may conflict
   469  			}
   470  		}
   471  		whyOnlyPAX = "only PAX supports PAXRecords"
   472  		format.mayOnlyBe(FormatPAX)
   473  	}
   474  	for k, v := range paxHdrs {
   475  		if !validPAXRecord(k, v) {
   476  			return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)}
   477  		}
   478  	}
   479  
   480  	// TODO(dsnet): Re-enable this when adding sparse support.
   481  	// See https://golang.org/issue/22735
   482  	/*
   483  		// Check sparse files.
   484  		if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse {
   485  			if isHeaderOnlyType(h.Typeflag) {
   486  				return FormatUnknown, nil, headerError{"header-only type cannot be sparse"}
   487  			}
   488  			if !validateSparseEntries(h.SparseHoles, h.Size) {
   489  				return FormatUnknown, nil, headerError{"invalid sparse holes"}
   490  			}
   491  			if h.Typeflag == TypeGNUSparse {
   492  				whyOnlyGNU = "only GNU supports TypeGNUSparse"
   493  				format.mayOnlyBe(FormatGNU)
   494  			} else {
   495  				whyNoGNU = "GNU supports sparse files only with TypeGNUSparse"
   496  				format.mustNotBe(FormatGNU)
   497  			}
   498  			whyNoUSTAR = "USTAR does not support sparse files"
   499  			format.mustNotBe(FormatUSTAR)
   500  		}
   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  func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
   627  	if fi == nil {
   628  		return nil, errors.New("archive/tar: FileInfo is nil")
   629  	}
   630  	fm := fi.Mode()
   631  	h := &Header{
   632  		Name:    fi.Name(),
   633  		ModTime: fi.ModTime(),
   634  		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
   635  	}
   636  	switch {
   637  	case fm.IsRegular():
   638  		h.Typeflag = TypeReg
   639  		h.Size = fi.Size()
   640  	case fi.IsDir():
   641  		h.Typeflag = TypeDir
   642  		h.Name += "/"
   643  	case fm&os.ModeSymlink != 0:
   644  		h.Typeflag = TypeSymlink
   645  		h.Linkname = link
   646  	case fm&os.ModeDevice != 0:
   647  		if fm&os.ModeCharDevice != 0 {
   648  			h.Typeflag = TypeChar
   649  		} else {
   650  			h.Typeflag = TypeBlock
   651  		}
   652  	case fm&os.ModeNamedPipe != 0:
   653  		h.Typeflag = TypeFifo
   654  	case fm&os.ModeSocket != 0:
   655  		return nil, fmt.Errorf("archive/tar: sockets not supported")
   656  	default:
   657  		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
   658  	}
   659  	if fm&os.ModeSetuid != 0 {
   660  		h.Mode |= c_ISUID
   661  	}
   662  	if fm&os.ModeSetgid != 0 {
   663  		h.Mode |= c_ISGID
   664  	}
   665  	if fm&os.ModeSticky != 0 {
   666  		h.Mode |= c_ISVTX
   667  	}
   668  	// If possible, populate additional fields from OS-specific
   669  	// FileInfo fields.
   670  	if sys, ok := fi.Sys().(*Header); ok {
   671  		// This FileInfo came from a Header (not the OS). Use the
   672  		// original Header to populate all remaining fields.
   673  		h.Uid = sys.Uid
   674  		h.Gid = sys.Gid
   675  		h.Uname = sys.Uname
   676  		h.Gname = sys.Gname
   677  		h.AccessTime = sys.AccessTime
   678  		h.ChangeTime = sys.ChangeTime
   679  		if sys.Xattrs != nil {
   680  			h.Xattrs = make(map[string]string)
   681  			for k, v := range sys.Xattrs {
   682  				h.Xattrs[k] = v
   683  			}
   684  		}
   685  		if sys.Typeflag == TypeLink {
   686  			// hard link
   687  			h.Typeflag = TypeLink
   688  			h.Size = 0
   689  			h.Linkname = sys.Linkname
   690  		}
   691  		if sys.PAXRecords != nil {
   692  			h.PAXRecords = make(map[string]string)
   693  			for k, v := range sys.PAXRecords {
   694  				h.PAXRecords[k] = v
   695  			}
   696  		}
   697  	}
   698  	if sysStat != nil {
   699  		return h, sysStat(fi, h)
   700  	}
   701  	return h, nil
   702  }
   703  
   704  // isHeaderOnlyType checks if the given type flag is of the type that has no
   705  // data section even if a size is specified.
   706  func isHeaderOnlyType(flag byte) bool {
   707  	switch flag {
   708  	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
   709  		return true
   710  	default:
   711  		return false
   712  	}
   713  }
   714  
   715  func min(a, b int64) int64 {
   716  	if a < b {
   717  		return a
   718  	}
   719  	return b
   720  }
   721  

View as plain text