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