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