github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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  	"bytes"
    17  	"errors"
    18  	"fmt"
    19  	"os"
    20  	"path"
    21  	"time"
    22  )
    23  
    24  const (
    25  	blockSize = 512
    26  
    27  	// Types
    28  	TypeReg           = '0'    // regular file
    29  	TypeRegA          = '\x00' // regular file
    30  	TypeLink          = '1'    // hard link
    31  	TypeSymlink       = '2'    // symbolic link
    32  	TypeChar          = '3'    // character device node
    33  	TypeBlock         = '4'    // block device node
    34  	TypeDir           = '5'    // directory
    35  	TypeFifo          = '6'    // fifo node
    36  	TypeCont          = '7'    // reserved
    37  	TypeXHeader       = 'x'    // extended header
    38  	TypeXGlobalHeader = 'g'    // global extended header
    39  	TypeGNULongName   = 'L'    // Next file has a long name
    40  	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
    41  	TypeGNUSparse     = 'S'    // sparse file
    42  )
    43  
    44  // A Header represents a single header in a tar archive.
    45  // Some fields may not be populated.
    46  type Header struct {
    47  	Name       string    // name of header file entry
    48  	Mode       int64     // permission and mode bits
    49  	Uid        int       // user id of owner
    50  	Gid        int       // group id of owner
    51  	Size       int64     // length in bytes
    52  	ModTime    time.Time // modified time
    53  	Typeflag   byte      // type of header entry
    54  	Linkname   string    // target name of link
    55  	Uname      string    // user name of owner
    56  	Gname      string    // group name of owner
    57  	Devmajor   int64     // major number of character or block device
    58  	Devminor   int64     // minor number of character or block device
    59  	AccessTime time.Time // access time
    60  	ChangeTime time.Time // status change time
    61  	Xattrs     map[string]string
    62  }
    63  
    64  // File name constants from the tar spec.
    65  const (
    66  	fileNameSize       = 100 // Maximum number of bytes in a standard tar name.
    67  	fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
    68  )
    69  
    70  // FileInfo returns an os.FileInfo for the Header.
    71  func (h *Header) FileInfo() os.FileInfo {
    72  	return headerFileInfo{h}
    73  }
    74  
    75  // headerFileInfo implements os.FileInfo.
    76  type headerFileInfo struct {
    77  	h *Header
    78  }
    79  
    80  func (fi headerFileInfo) Size() int64        { return fi.h.Size }
    81  func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
    82  func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
    83  func (fi headerFileInfo) Sys() interface{}   { return fi.h }
    84  
    85  // Name returns the base name of the file.
    86  func (fi headerFileInfo) Name() string {
    87  	if fi.IsDir() {
    88  		return path.Base(path.Clean(fi.h.Name))
    89  	}
    90  	return path.Base(fi.h.Name)
    91  }
    92  
    93  // Mode returns the permission and mode bits for the headerFileInfo.
    94  func (fi headerFileInfo) Mode() (mode os.FileMode) {
    95  	// Set file permission bits.
    96  	mode = os.FileMode(fi.h.Mode).Perm()
    97  
    98  	// Set setuid, setgid and sticky bits.
    99  	if fi.h.Mode&c_ISUID != 0 {
   100  		// setuid
   101  		mode |= os.ModeSetuid
   102  	}
   103  	if fi.h.Mode&c_ISGID != 0 {
   104  		// setgid
   105  		mode |= os.ModeSetgid
   106  	}
   107  	if fi.h.Mode&c_ISVTX != 0 {
   108  		// sticky
   109  		mode |= os.ModeSticky
   110  	}
   111  
   112  	// Set file mode bits.
   113  	// clear perm, setuid, setgid and sticky bits.
   114  	m := os.FileMode(fi.h.Mode) &^ 07777
   115  	if m == c_ISDIR {
   116  		// directory
   117  		mode |= os.ModeDir
   118  	}
   119  	if m == c_ISFIFO {
   120  		// named pipe (FIFO)
   121  		mode |= os.ModeNamedPipe
   122  	}
   123  	if m == c_ISLNK {
   124  		// symbolic link
   125  		mode |= os.ModeSymlink
   126  	}
   127  	if m == c_ISBLK {
   128  		// device file
   129  		mode |= os.ModeDevice
   130  	}
   131  	if m == c_ISCHR {
   132  		// Unix character device
   133  		mode |= os.ModeDevice
   134  		mode |= os.ModeCharDevice
   135  	}
   136  	if m == c_ISSOCK {
   137  		// Unix domain socket
   138  		mode |= os.ModeSocket
   139  	}
   140  
   141  	switch fi.h.Typeflag {
   142  	case TypeSymlink:
   143  		// symbolic link
   144  		mode |= os.ModeSymlink
   145  	case TypeChar:
   146  		// character device node
   147  		mode |= os.ModeDevice
   148  		mode |= os.ModeCharDevice
   149  	case TypeBlock:
   150  		// block device node
   151  		mode |= os.ModeDevice
   152  	case TypeDir:
   153  		// directory
   154  		mode |= os.ModeDir
   155  	case TypeFifo:
   156  		// fifo node
   157  		mode |= os.ModeNamedPipe
   158  	}
   159  
   160  	return mode
   161  }
   162  
   163  // sysStat, if non-nil, populates h from system-dependent fields of fi.
   164  var sysStat func(fi os.FileInfo, h *Header) error
   165  
   166  // Mode constants from the tar spec.
   167  const (
   168  	c_ISUID  = 04000   // Set uid
   169  	c_ISGID  = 02000   // Set gid
   170  	c_ISVTX  = 01000   // Save text (sticky bit)
   171  	c_ISDIR  = 040000  // Directory
   172  	c_ISFIFO = 010000  // FIFO
   173  	c_ISREG  = 0100000 // Regular file
   174  	c_ISLNK  = 0120000 // Symbolic link
   175  	c_ISBLK  = 060000  // Block special file
   176  	c_ISCHR  = 020000  // Character special file
   177  	c_ISSOCK = 0140000 // Socket
   178  )
   179  
   180  // Keywords for the PAX Extended Header
   181  const (
   182  	paxAtime    = "atime"
   183  	paxCharset  = "charset"
   184  	paxComment  = "comment"
   185  	paxCtime    = "ctime" // please note that ctime is not a valid pax header.
   186  	paxGid      = "gid"
   187  	paxGname    = "gname"
   188  	paxLinkpath = "linkpath"
   189  	paxMtime    = "mtime"
   190  	paxPath     = "path"
   191  	paxSize     = "size"
   192  	paxUid      = "uid"
   193  	paxUname    = "uname"
   194  	paxXattr    = "SCHILY.xattr."
   195  	paxNone     = ""
   196  )
   197  
   198  // FileInfoHeader creates a partially-populated Header from fi.
   199  // If fi describes a symlink, FileInfoHeader records link as the link target.
   200  // If fi describes a directory, a slash is appended to the name.
   201  // Because os.FileInfo's Name method returns only the base name of
   202  // the file it describes, it may be necessary to modify the Name field
   203  // of the returned header to provide the full path name of the file.
   204  func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
   205  	if fi == nil {
   206  		return nil, errors.New("tar: FileInfo is nil")
   207  	}
   208  	fm := fi.Mode()
   209  	h := &Header{
   210  		Name:    fi.Name(),
   211  		ModTime: fi.ModTime(),
   212  		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
   213  	}
   214  	switch {
   215  	case fm.IsRegular():
   216  		h.Mode |= c_ISREG
   217  		h.Typeflag = TypeReg
   218  		h.Size = fi.Size()
   219  	case fi.IsDir():
   220  		h.Typeflag = TypeDir
   221  		h.Mode |= c_ISDIR
   222  		h.Name += "/"
   223  	case fm&os.ModeSymlink != 0:
   224  		h.Typeflag = TypeSymlink
   225  		h.Mode |= c_ISLNK
   226  		h.Linkname = link
   227  	case fm&os.ModeDevice != 0:
   228  		if fm&os.ModeCharDevice != 0 {
   229  			h.Mode |= c_ISCHR
   230  			h.Typeflag = TypeChar
   231  		} else {
   232  			h.Mode |= c_ISBLK
   233  			h.Typeflag = TypeBlock
   234  		}
   235  	case fm&os.ModeNamedPipe != 0:
   236  		h.Typeflag = TypeFifo
   237  		h.Mode |= c_ISFIFO
   238  	case fm&os.ModeSocket != 0:
   239  		h.Mode |= c_ISSOCK
   240  	default:
   241  		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
   242  	}
   243  	if fm&os.ModeSetuid != 0 {
   244  		h.Mode |= c_ISUID
   245  	}
   246  	if fm&os.ModeSetgid != 0 {
   247  		h.Mode |= c_ISGID
   248  	}
   249  	if fm&os.ModeSticky != 0 {
   250  		h.Mode |= c_ISVTX
   251  	}
   252  	// If possible, populate additional fields from OS-specific
   253  	// FileInfo fields.
   254  	if sys, ok := fi.Sys().(*Header); ok {
   255  		// This FileInfo came from a Header (not the OS). Use the
   256  		// original Header to populate all remaining fields.
   257  		h.Uid = sys.Uid
   258  		h.Gid = sys.Gid
   259  		h.Uname = sys.Uname
   260  		h.Gname = sys.Gname
   261  		h.AccessTime = sys.AccessTime
   262  		h.ChangeTime = sys.ChangeTime
   263  		if sys.Xattrs != nil {
   264  			h.Xattrs = make(map[string]string)
   265  			for k, v := range sys.Xattrs {
   266  				h.Xattrs[k] = v
   267  			}
   268  		}
   269  		if sys.Typeflag == TypeLink {
   270  			// hard link
   271  			h.Typeflag = TypeLink
   272  			h.Size = 0
   273  			h.Linkname = sys.Linkname
   274  		}
   275  	}
   276  	if sysStat != nil {
   277  		return h, sysStat(fi, h)
   278  	}
   279  	return h, nil
   280  }
   281  
   282  var zeroBlock = make([]byte, blockSize)
   283  
   284  // POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
   285  // We compute and return both.
   286  func checksum(header []byte) (unsigned int64, signed int64) {
   287  	for i := 0; i < len(header); i++ {
   288  		if i == 148 {
   289  			// The chksum field (header[148:156]) is special: it should be treated as space bytes.
   290  			unsigned += ' ' * 8
   291  			signed += ' ' * 8
   292  			i += 7
   293  			continue
   294  		}
   295  		unsigned += int64(header[i])
   296  		signed += int64(int8(header[i]))
   297  	}
   298  	return
   299  }
   300  
   301  type slicer []byte
   302  
   303  func (sp *slicer) next(n int) (b []byte) {
   304  	s := *sp
   305  	b, *sp = s[0:n], s[n:]
   306  	return
   307  }
   308  
   309  func isASCII(s string) bool {
   310  	for _, c := range s {
   311  		if c >= 0x80 {
   312  			return false
   313  		}
   314  	}
   315  	return true
   316  }
   317  
   318  func toASCII(s string) string {
   319  	if isASCII(s) {
   320  		return s
   321  	}
   322  	var buf bytes.Buffer
   323  	for _, c := range s {
   324  		if c < 0x80 {
   325  			buf.WriteByte(byte(c))
   326  		}
   327  	}
   328  	return buf.String()
   329  }
   330  
   331  // isHeaderOnlyType checks if the given type flag is of the type that has no
   332  // data section even if a size is specified.
   333  func isHeaderOnlyType(flag byte) bool {
   334  	switch flag {
   335  	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
   336  		return true
   337  	default:
   338  		return false
   339  	}
   340  }