github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/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  	"errors"
    17  	"fmt"
    18  	"os"
    19  	"path"
    20  	"time"
    21  )
    22  
    23  const (
    24  	blockSize = 512
    25  
    26  	// Types
    27  	TypeReg           = '0'    // regular file
    28  	TypeRegA          = '\x00' // regular file
    29  	TypeLink          = '1'    // hard link
    30  	TypeSymlink       = '2'    // symbolic link
    31  	TypeChar          = '3'    // character device node
    32  	TypeBlock         = '4'    // block device node
    33  	TypeDir           = '5'    // directory
    34  	TypeFifo          = '6'    // fifo node
    35  	TypeCont          = '7'    // reserved
    36  	TypeXHeader       = 'x'    // extended header
    37  	TypeXGlobalHeader = 'g'    // global extended header
    38  	TypeGNULongName   = 'L'    // Next file has a long name
    39  	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
    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  }
    60  
    61  // File name constants from the tar spec.
    62  const (
    63  	fileNameSize       = 100 // Maximum number of bytes in a standard tar name.
    64  	fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
    65  )
    66  
    67  // FileInfo returns an os.FileInfo for the Header.
    68  func (h *Header) FileInfo() os.FileInfo {
    69  	return headerFileInfo{h}
    70  }
    71  
    72  // headerFileInfo implements os.FileInfo.
    73  type headerFileInfo struct {
    74  	h *Header
    75  }
    76  
    77  func (fi headerFileInfo) Size() int64        { return fi.h.Size }
    78  func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
    79  func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
    80  func (fi headerFileInfo) Sys() interface{}   { return fi.h }
    81  
    82  // Name returns the base name of the file.
    83  func (fi headerFileInfo) Name() string {
    84  	if fi.IsDir() {
    85  		return path.Clean(fi.h.Name)
    86  	}
    87  	return fi.h.Name
    88  }
    89  
    90  // Mode returns the permission and mode bits for the headerFileInfo.
    91  func (fi headerFileInfo) Mode() (mode os.FileMode) {
    92  	// Set file permission bits.
    93  	mode = os.FileMode(fi.h.Mode).Perm()
    94  
    95  	// Set setuid, setgid and sticky bits.
    96  	if fi.h.Mode&c_ISUID != 0 {
    97  		// setuid
    98  		mode |= os.ModeSetuid
    99  	}
   100  	if fi.h.Mode&c_ISGID != 0 {
   101  		// setgid
   102  		mode |= os.ModeSetgid
   103  	}
   104  	if fi.h.Mode&c_ISVTX != 0 {
   105  		// sticky
   106  		mode |= os.ModeSticky
   107  	}
   108  
   109  	// Set file mode bits.
   110  	// clear perm, setuid, setgid and sticky bits.
   111  	m := os.FileMode(fi.h.Mode) &^ 07777
   112  	if m == c_ISDIR {
   113  		// directory
   114  		mode |= os.ModeDir
   115  	}
   116  	if m == c_ISFIFO {
   117  		// named pipe (FIFO)
   118  		mode |= os.ModeNamedPipe
   119  	}
   120  	if m == c_ISLNK {
   121  		// symbolic link
   122  		mode |= os.ModeSymlink
   123  	}
   124  	if m == c_ISBLK {
   125  		// device file
   126  		mode |= os.ModeDevice
   127  	}
   128  	if m == c_ISCHR {
   129  		// Unix character device
   130  		mode |= os.ModeDevice
   131  		mode |= os.ModeCharDevice
   132  	}
   133  	if m == c_ISSOCK {
   134  		// Unix domain socket
   135  		mode |= os.ModeSocket
   136  	}
   137  
   138  	switch fi.h.Typeflag {
   139  	case TypeLink, TypeSymlink:
   140  		// hard link, symbolic link
   141  		mode |= os.ModeSymlink
   142  	case TypeChar:
   143  		// character device node
   144  		mode |= os.ModeDevice
   145  		mode |= os.ModeCharDevice
   146  	case TypeBlock:
   147  		// block device node
   148  		mode |= os.ModeDevice
   149  	case TypeDir:
   150  		// directory
   151  		mode |= os.ModeDir
   152  	case TypeFifo:
   153  		// fifo node
   154  		mode |= os.ModeNamedPipe
   155  	}
   156  
   157  	return mode
   158  }
   159  
   160  // sysStat, if non-nil, populates h from system-dependent fields of fi.
   161  var sysStat func(fi os.FileInfo, h *Header) error
   162  
   163  // Mode constants from the tar spec.
   164  const (
   165  	c_ISUID  = 04000   // Set uid
   166  	c_ISGID  = 02000   // Set gid
   167  	c_ISVTX  = 01000   // Save text (sticky bit)
   168  	c_ISDIR  = 040000  // Directory
   169  	c_ISFIFO = 010000  // FIFO
   170  	c_ISREG  = 0100000 // Regular file
   171  	c_ISLNK  = 0120000 // Symbolic link
   172  	c_ISBLK  = 060000  // Block special file
   173  	c_ISCHR  = 020000  // Character special file
   174  	c_ISSOCK = 0140000 // Socket
   175  )
   176  
   177  // FileInfoHeader creates a partially-populated Header from fi.
   178  // If fi describes a symlink, FileInfoHeader records link as the link target.
   179  // If fi describes a directory, a slash is appended to the name.
   180  func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
   181  	if fi == nil {
   182  		return nil, errors.New("tar: FileInfo is nil")
   183  	}
   184  	fm := fi.Mode()
   185  	h := &Header{
   186  		Name:    fi.Name(),
   187  		ModTime: fi.ModTime(),
   188  		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
   189  	}
   190  	switch {
   191  	case fm.IsRegular():
   192  		h.Mode |= c_ISREG
   193  		h.Typeflag = TypeReg
   194  		h.Size = fi.Size()
   195  	case fi.IsDir():
   196  		h.Typeflag = TypeDir
   197  		h.Mode |= c_ISDIR
   198  		h.Name += "/"
   199  	case fm&os.ModeSymlink != 0:
   200  		h.Typeflag = TypeSymlink
   201  		h.Mode |= c_ISLNK
   202  		h.Linkname = link
   203  	case fm&os.ModeDevice != 0:
   204  		if fm&os.ModeCharDevice != 0 {
   205  			h.Mode |= c_ISCHR
   206  			h.Typeflag = TypeChar
   207  		} else {
   208  			h.Mode |= c_ISBLK
   209  			h.Typeflag = TypeBlock
   210  		}
   211  	case fm&os.ModeNamedPipe != 0:
   212  		h.Typeflag = TypeFifo
   213  		h.Mode |= c_ISFIFO
   214  	case fm&os.ModeSocket != 0:
   215  		h.Mode |= c_ISSOCK
   216  	default:
   217  		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
   218  	}
   219  	if fm&os.ModeSetuid != 0 {
   220  		h.Mode |= c_ISUID
   221  	}
   222  	if fm&os.ModeSetgid != 0 {
   223  		h.Mode |= c_ISGID
   224  	}
   225  	if fm&os.ModeSticky != 0 {
   226  		h.Mode |= c_ISVTX
   227  	}
   228  	if sysStat != nil {
   229  		return h, sysStat(fi, h)
   230  	}
   231  	return h, nil
   232  }
   233  
   234  var zeroBlock = make([]byte, blockSize)
   235  
   236  // POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
   237  // We compute and return both.
   238  func checksum(header []byte) (unsigned int64, signed int64) {
   239  	for i := 0; i < len(header); i++ {
   240  		if i == 148 {
   241  			// The chksum field (header[148:156]) is special: it should be treated as space bytes.
   242  			unsigned += ' ' * 8
   243  			signed += ' ' * 8
   244  			i += 7
   245  			continue
   246  		}
   247  		unsigned += int64(header[i])
   248  		signed += int64(int8(header[i]))
   249  	}
   250  	return
   251  }
   252  
   253  type slicer []byte
   254  
   255  func (sp *slicer) next(n int) (b []byte) {
   256  	s := *sp
   257  	b, *sp = s[0:n], s[n:]
   258  	return
   259  }