...

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

View as plain text