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