github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/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  // It aims to cover most of the variations, including those produced
     7  // by GNU and BSD tars.
     8  //
     9  // References:
    10  //   http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5
    11  //   http://www.gnu.org/software/tar/manual/html_node/Standard.html
    12  //   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
    13  package tar
    14  
    15  import (
    16  	"errors"
    17  	"fmt"
    18  	"os"
    19  	"path"
    20  	"strconv"
    21  	"time"
    22  )
    23  
    24  // BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit
    25  // architectures. If a large value is encountered when decoding, the result
    26  // stored in Header will be the truncated version.
    27  
    28  var (
    29  	ErrHeader          = errors.New("tar: invalid tar header")
    30  	ErrWriteTooLong    = errors.New("tar: write too long")
    31  	ErrFieldTooLong    = errors.New("tar: header field too long")
    32  	ErrWriteAfterClose = errors.New("tar: write after close")
    33  )
    34  
    35  // Header type flags.
    36  const (
    37  	TypeReg           = '0'    // regular file
    38  	TypeRegA          = '\x00' // regular file
    39  	TypeLink          = '1'    // hard link
    40  	TypeSymlink       = '2'    // symbolic link
    41  	TypeChar          = '3'    // character device node
    42  	TypeBlock         = '4'    // block device node
    43  	TypeDir           = '5'    // directory
    44  	TypeFifo          = '6'    // fifo node
    45  	TypeCont          = '7'    // reserved
    46  	TypeXHeader       = 'x'    // extended header
    47  	TypeXGlobalHeader = 'g'    // global extended header
    48  	TypeGNULongName   = 'L'    // Next file has a long name
    49  	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
    50  	TypeGNUSparse     = 'S'    // sparse file
    51  )
    52  
    53  // A Header represents a single header in a tar archive.
    54  // Some fields may not be populated.
    55  type Header struct {
    56  	Name       string    // name of header file entry
    57  	Mode       int64     // permission and mode bits
    58  	Uid        int       // user id of owner
    59  	Gid        int       // group id of owner
    60  	Size       int64     // length in bytes
    61  	ModTime    time.Time // modified time
    62  	Typeflag   byte      // type of header entry
    63  	Linkname   string    // target name of link
    64  	Uname      string    // user name of owner
    65  	Gname      string    // group name of owner
    66  	Devmajor   int64     // major number of character or block device
    67  	Devminor   int64     // minor number of character or block device
    68  	AccessTime time.Time // access time
    69  	ChangeTime time.Time // status change time
    70  	Xattrs     map[string]string
    71  }
    72  
    73  // FileInfo returns an os.FileInfo for the Header.
    74  func (h *Header) FileInfo() os.FileInfo {
    75  	return headerFileInfo{h}
    76  }
    77  
    78  // allowedFormats determines which formats can be used. The value returned
    79  // is the logical OR of multiple possible formats. If the value is
    80  // formatUnknown, then the input Header cannot be encoded.
    81  //
    82  // As a by-product of checking the fields, this function returns paxHdrs, which
    83  // contain all fields that could not be directly encoded.
    84  func (h *Header) allowedFormats() (format int, paxHdrs map[string]string) {
    85  	format = formatUSTAR | formatPAX | formatGNU
    86  	paxHdrs = make(map[string]string)
    87  
    88  	verifyString := func(s string, size int, paxKey string) {
    89  		// NUL-terminator is optional for path and linkpath.
    90  		// Technically, it is required for uname and gname,
    91  		// but neither GNU nor BSD tar checks for it.
    92  		tooLong := len(s) > size
    93  		allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath
    94  		if hasNUL(s) || (tooLong && !allowLongGNU) {
    95  			format &^= formatGNU // No GNU
    96  		}
    97  		if !isASCII(s) || tooLong {
    98  			canSplitUSTAR := paxKey == paxPath
    99  			if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok {
   100  				format &^= formatUSTAR // No USTAR
   101  			}
   102  			if paxKey == paxNone {
   103  				format &^= formatPAX // No PAX
   104  			} else {
   105  				paxHdrs[paxKey] = s
   106  			}
   107  		}
   108  	}
   109  	verifyNumeric := func(n int64, size int, paxKey string) {
   110  		if !fitsInBase256(size, n) {
   111  			format &^= formatGNU // No GNU
   112  		}
   113  		if !fitsInOctal(size, n) {
   114  			format &^= formatUSTAR // No USTAR
   115  			if paxKey == paxNone {
   116  				format &^= formatPAX // No PAX
   117  			} else {
   118  				paxHdrs[paxKey] = strconv.FormatInt(n, 10)
   119  			}
   120  		}
   121  	}
   122  	verifyTime := func(ts time.Time, size int, paxKey string) {
   123  		if ts.IsZero() {
   124  			return // Always okay
   125  		}
   126  		needsNano := ts.Nanosecond() != 0
   127  		hasFieldUSTAR := paxKey == paxMtime
   128  		if !fitsInBase256(size, ts.Unix()) || needsNano {
   129  			format &^= formatGNU // No GNU
   130  		}
   131  		if !fitsInOctal(size, ts.Unix()) || needsNano || !hasFieldUSTAR {
   132  			format &^= formatUSTAR // No USTAR
   133  			if paxKey == paxNone {
   134  				format &^= formatPAX // No PAX
   135  			} else {
   136  				paxHdrs[paxKey] = formatPAXTime(ts)
   137  			}
   138  		}
   139  	}
   140  
   141  	var blk block
   142  	v7 := blk.V7()
   143  	ustar := blk.USTAR()
   144  	gnu := blk.GNU()
   145  	verifyString(h.Name, len(v7.Name()), paxPath)
   146  	verifyString(h.Linkname, len(v7.LinkName()), paxLinkpath)
   147  	verifyString(h.Uname, len(ustar.UserName()), paxUname)
   148  	verifyString(h.Gname, len(ustar.GroupName()), paxGname)
   149  	verifyNumeric(h.Mode, len(v7.Mode()), paxNone)
   150  	verifyNumeric(int64(h.Uid), len(v7.UID()), paxUid)
   151  	verifyNumeric(int64(h.Gid), len(v7.GID()), paxGid)
   152  	verifyNumeric(h.Size, len(v7.Size()), paxSize)
   153  	verifyNumeric(h.Devmajor, len(ustar.DevMajor()), paxNone)
   154  	verifyNumeric(h.Devminor, len(ustar.DevMinor()), paxNone)
   155  	verifyTime(h.ModTime, len(v7.ModTime()), paxMtime)
   156  	verifyTime(h.AccessTime, len(gnu.AccessTime()), paxAtime)
   157  	verifyTime(h.ChangeTime, len(gnu.ChangeTime()), paxCtime)
   158  
   159  	if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 {
   160  		return formatUnknown, nil
   161  	}
   162  	if len(h.Xattrs) > 0 {
   163  		for k, v := range h.Xattrs {
   164  			paxHdrs[paxXattr+k] = v
   165  		}
   166  		format &= formatPAX // PAX only
   167  	}
   168  	for k, v := range paxHdrs {
   169  		// Forbid empty values (which represent deletion) since usage of
   170  		// them are non-sensible without global PAX record support.
   171  		if !validPAXRecord(k, v) || v == "" {
   172  			return formatUnknown, nil // Invalid PAX key
   173  		}
   174  	}
   175  	return format, paxHdrs
   176  }
   177  
   178  // headerFileInfo implements os.FileInfo.
   179  type headerFileInfo struct {
   180  	h *Header
   181  }
   182  
   183  func (fi headerFileInfo) Size() int64        { return fi.h.Size }
   184  func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
   185  func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
   186  func (fi headerFileInfo) Sys() interface{}   { return fi.h }
   187  
   188  // Name returns the base name of the file.
   189  func (fi headerFileInfo) Name() string {
   190  	if fi.IsDir() {
   191  		return path.Base(path.Clean(fi.h.Name))
   192  	}
   193  	return path.Base(fi.h.Name)
   194  }
   195  
   196  // Mode returns the permission and mode bits for the headerFileInfo.
   197  func (fi headerFileInfo) Mode() (mode os.FileMode) {
   198  	// Set file permission bits.
   199  	mode = os.FileMode(fi.h.Mode).Perm()
   200  
   201  	// Set setuid, setgid and sticky bits.
   202  	if fi.h.Mode&c_ISUID != 0 {
   203  		// setuid
   204  		mode |= os.ModeSetuid
   205  	}
   206  	if fi.h.Mode&c_ISGID != 0 {
   207  		// setgid
   208  		mode |= os.ModeSetgid
   209  	}
   210  	if fi.h.Mode&c_ISVTX != 0 {
   211  		// sticky
   212  		mode |= os.ModeSticky
   213  	}
   214  
   215  	// Set file mode bits.
   216  	// clear perm, setuid, setgid and sticky bits.
   217  	m := os.FileMode(fi.h.Mode) &^ 07777
   218  	if m == c_ISDIR {
   219  		// directory
   220  		mode |= os.ModeDir
   221  	}
   222  	if m == c_ISFIFO {
   223  		// named pipe (FIFO)
   224  		mode |= os.ModeNamedPipe
   225  	}
   226  	if m == c_ISLNK {
   227  		// symbolic link
   228  		mode |= os.ModeSymlink
   229  	}
   230  	if m == c_ISBLK {
   231  		// device file
   232  		mode |= os.ModeDevice
   233  	}
   234  	if m == c_ISCHR {
   235  		// Unix character device
   236  		mode |= os.ModeDevice
   237  		mode |= os.ModeCharDevice
   238  	}
   239  	if m == c_ISSOCK {
   240  		// Unix domain socket
   241  		mode |= os.ModeSocket
   242  	}
   243  
   244  	switch fi.h.Typeflag {
   245  	case TypeSymlink:
   246  		// symbolic link
   247  		mode |= os.ModeSymlink
   248  	case TypeChar:
   249  		// character device node
   250  		mode |= os.ModeDevice
   251  		mode |= os.ModeCharDevice
   252  	case TypeBlock:
   253  		// block device node
   254  		mode |= os.ModeDevice
   255  	case TypeDir:
   256  		// directory
   257  		mode |= os.ModeDir
   258  	case TypeFifo:
   259  		// fifo node
   260  		mode |= os.ModeNamedPipe
   261  	}
   262  
   263  	return mode
   264  }
   265  
   266  // sysStat, if non-nil, populates h from system-dependent fields of fi.
   267  var sysStat func(fi os.FileInfo, h *Header) error
   268  
   269  const (
   270  	// Mode constants from the USTAR spec:
   271  	// See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
   272  	c_ISUID = 04000 // Set uid
   273  	c_ISGID = 02000 // Set gid
   274  	c_ISVTX = 01000 // Save text (sticky bit)
   275  
   276  	// Common Unix mode constants; these are not defined in any common tar standard.
   277  	// Header.FileInfo understands these, but FileInfoHeader will never produce these.
   278  	c_ISDIR  = 040000  // Directory
   279  	c_ISFIFO = 010000  // FIFO
   280  	c_ISREG  = 0100000 // Regular file
   281  	c_ISLNK  = 0120000 // Symbolic link
   282  	c_ISBLK  = 060000  // Block special file
   283  	c_ISCHR  = 020000  // Character special file
   284  	c_ISSOCK = 0140000 // Socket
   285  )
   286  
   287  // Keywords for the PAX Extended Header
   288  const (
   289  	paxAtime    = "atime"
   290  	paxCharset  = "charset"
   291  	paxComment  = "comment"
   292  	paxCtime    = "ctime" // please note that ctime is not a valid pax header.
   293  	paxGid      = "gid"
   294  	paxGname    = "gname"
   295  	paxLinkpath = "linkpath"
   296  	paxMtime    = "mtime"
   297  	paxPath     = "path"
   298  	paxSize     = "size"
   299  	paxUid      = "uid"
   300  	paxUname    = "uname"
   301  	paxXattr    = "SCHILY.xattr."
   302  	paxNone     = ""
   303  )
   304  
   305  // FileInfoHeader creates a partially-populated Header from fi.
   306  // If fi describes a symlink, FileInfoHeader records link as the link target.
   307  // If fi describes a directory, a slash is appended to the name.
   308  // Because os.FileInfo's Name method returns only the base name of
   309  // the file it describes, it may be necessary to modify the Name field
   310  // of the returned header to provide the full path name of the file.
   311  func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
   312  	if fi == nil {
   313  		return nil, errors.New("tar: FileInfo is nil")
   314  	}
   315  	fm := fi.Mode()
   316  	h := &Header{
   317  		Name:    fi.Name(),
   318  		ModTime: fi.ModTime(),
   319  		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
   320  	}
   321  	switch {
   322  	case fm.IsRegular():
   323  		h.Typeflag = TypeReg
   324  		h.Size = fi.Size()
   325  	case fi.IsDir():
   326  		h.Typeflag = TypeDir
   327  		h.Name += "/"
   328  	case fm&os.ModeSymlink != 0:
   329  		h.Typeflag = TypeSymlink
   330  		h.Linkname = link
   331  	case fm&os.ModeDevice != 0:
   332  		if fm&os.ModeCharDevice != 0 {
   333  			h.Typeflag = TypeChar
   334  		} else {
   335  			h.Typeflag = TypeBlock
   336  		}
   337  	case fm&os.ModeNamedPipe != 0:
   338  		h.Typeflag = TypeFifo
   339  	case fm&os.ModeSocket != 0:
   340  		return nil, fmt.Errorf("tar: sockets not supported")
   341  	default:
   342  		return nil, fmt.Errorf("tar: unknown file mode %v", fm)
   343  	}
   344  	if fm&os.ModeSetuid != 0 {
   345  		h.Mode |= c_ISUID
   346  	}
   347  	if fm&os.ModeSetgid != 0 {
   348  		h.Mode |= c_ISGID
   349  	}
   350  	if fm&os.ModeSticky != 0 {
   351  		h.Mode |= c_ISVTX
   352  	}
   353  	// If possible, populate additional fields from OS-specific
   354  	// FileInfo fields.
   355  	if sys, ok := fi.Sys().(*Header); ok {
   356  		// This FileInfo came from a Header (not the OS). Use the
   357  		// original Header to populate all remaining fields.
   358  		h.Uid = sys.Uid
   359  		h.Gid = sys.Gid
   360  		h.Uname = sys.Uname
   361  		h.Gname = sys.Gname
   362  		h.AccessTime = sys.AccessTime
   363  		h.ChangeTime = sys.ChangeTime
   364  		if sys.Xattrs != nil {
   365  			h.Xattrs = make(map[string]string)
   366  			for k, v := range sys.Xattrs {
   367  				h.Xattrs[k] = v
   368  			}
   369  		}
   370  		if sys.Typeflag == TypeLink {
   371  			// hard link
   372  			h.Typeflag = TypeLink
   373  			h.Size = 0
   374  			h.Linkname = sys.Linkname
   375  		}
   376  	}
   377  	if sysStat != nil {
   378  		return h, sysStat(fi, h)
   379  	}
   380  	return h, nil
   381  }
   382  
   383  // isHeaderOnlyType checks if the given type flag is of the type that has no
   384  // data section even if a size is specified.
   385  func isHeaderOnlyType(flag byte) bool {
   386  	switch flag {
   387  	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
   388  		return true
   389  	default:
   390  		return false
   391  	}
   392  }