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