github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/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 "errors" 17 "fmt" 18 "os" 19 "path" 20 "strconv" 21 "time" 22 ) 23 24 // BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit 25 // architectures. If a large value is encountered when decoding, the result 26 // stored in Header will be the truncated version. 27 28 var ( 29 ErrHeader = errors.New("tar: invalid tar header") 30 ErrWriteTooLong = errors.New("tar: write too long") 31 ErrFieldTooLong = errors.New("tar: header field too long") 32 ErrWriteAfterClose = errors.New("tar: write after close") 33 ) 34 35 // Header type flags. 36 const ( 37 TypeReg = '0' // regular file 38 TypeRegA = '\x00' // regular file 39 TypeLink = '1' // hard link 40 TypeSymlink = '2' // symbolic link 41 TypeChar = '3' // character device node 42 TypeBlock = '4' // block device node 43 TypeDir = '5' // directory 44 TypeFifo = '6' // fifo node 45 TypeCont = '7' // reserved 46 TypeXHeader = 'x' // extended header 47 TypeXGlobalHeader = 'g' // global extended header 48 TypeGNULongName = 'L' // Next file has a long name 49 TypeGNULongLink = 'K' // Next file symlinks to a file w/ a long name 50 TypeGNUSparse = 'S' // sparse file 51 ) 52 53 // A Header represents a single header in a tar archive. 54 // Some fields may not be populated. 55 type Header struct { 56 Name string // name of header file entry 57 Mode int64 // permission and mode bits 58 Uid int // user id of owner 59 Gid int // group id of owner 60 Size int64 // length in bytes 61 ModTime time.Time // modified time 62 Typeflag byte // type of header entry 63 Linkname string // target name of link 64 Uname string // user name of owner 65 Gname string // group name of owner 66 Devmajor int64 // major number of character or block device 67 Devminor int64 // minor number of character or block device 68 AccessTime time.Time // access time 69 ChangeTime time.Time // status change time 70 Xattrs map[string]string 71 } 72 73 // FileInfo returns an os.FileInfo for the Header. 74 func (h *Header) FileInfo() os.FileInfo { 75 return headerFileInfo{h} 76 } 77 78 // allowedFormats determines which formats can be used. The value returned 79 // is the logical OR of multiple possible formats. If the value is 80 // formatUnknown, then the input Header cannot be encoded. 81 // 82 // As a by-product of checking the fields, this function returns paxHdrs, which 83 // contain all fields that could not be directly encoded. 84 func (h *Header) allowedFormats() (format int, paxHdrs map[string]string) { 85 format = formatUSTAR | formatPAX | formatGNU 86 paxHdrs = make(map[string]string) 87 88 verifyString := func(s string, size int, paxKey string) { 89 // NUL-terminator is optional for path and linkpath. 90 // Technically, it is required for uname and gname, 91 // but neither GNU nor BSD tar checks for it. 92 tooLong := len(s) > size 93 allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath 94 if hasNUL(s) || (tooLong && !allowLongGNU) { 95 format &^= formatGNU // No GNU 96 } 97 if !isASCII(s) || tooLong { 98 canSplitUSTAR := paxKey == paxPath 99 if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok { 100 format &^= formatUSTAR // No USTAR 101 } 102 if paxKey == paxNone { 103 format &^= formatPAX // No PAX 104 } else { 105 paxHdrs[paxKey] = s 106 } 107 } 108 } 109 verifyNumeric := func(n int64, size int, paxKey string) { 110 if !fitsInBase256(size, n) { 111 format &^= formatGNU // No GNU 112 } 113 if !fitsInOctal(size, n) { 114 format &^= formatUSTAR // No USTAR 115 if paxKey == paxNone { 116 format &^= formatPAX // No PAX 117 } else { 118 paxHdrs[paxKey] = strconv.FormatInt(n, 10) 119 } 120 } 121 } 122 verifyTime := func(ts time.Time, size int, paxKey string) { 123 if ts.IsZero() { 124 return // Always okay 125 } 126 needsNano := ts.Nanosecond() != 0 127 hasFieldUSTAR := paxKey == paxMtime 128 if !fitsInBase256(size, ts.Unix()) || needsNano { 129 format &^= formatGNU // No GNU 130 } 131 if !fitsInOctal(size, ts.Unix()) || needsNano || !hasFieldUSTAR { 132 format &^= formatUSTAR // No USTAR 133 if paxKey == paxNone { 134 format &^= formatPAX // No PAX 135 } else { 136 paxHdrs[paxKey] = formatPAXTime(ts) 137 } 138 } 139 } 140 141 var blk block 142 v7 := blk.V7() 143 ustar := blk.USTAR() 144 gnu := blk.GNU() 145 verifyString(h.Name, len(v7.Name()), paxPath) 146 verifyString(h.Linkname, len(v7.LinkName()), paxLinkpath) 147 verifyString(h.Uname, len(ustar.UserName()), paxUname) 148 verifyString(h.Gname, len(ustar.GroupName()), paxGname) 149 verifyNumeric(h.Mode, len(v7.Mode()), paxNone) 150 verifyNumeric(int64(h.Uid), len(v7.UID()), paxUid) 151 verifyNumeric(int64(h.Gid), len(v7.GID()), paxGid) 152 verifyNumeric(h.Size, len(v7.Size()), paxSize) 153 verifyNumeric(h.Devmajor, len(ustar.DevMajor()), paxNone) 154 verifyNumeric(h.Devminor, len(ustar.DevMinor()), paxNone) 155 verifyTime(h.ModTime, len(v7.ModTime()), paxMtime) 156 verifyTime(h.AccessTime, len(gnu.AccessTime()), paxAtime) 157 verifyTime(h.ChangeTime, len(gnu.ChangeTime()), paxCtime) 158 159 if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 { 160 return formatUnknown, nil 161 } 162 if len(h.Xattrs) > 0 { 163 for k, v := range h.Xattrs { 164 paxHdrs[paxXattr+k] = v 165 } 166 format &= formatPAX // PAX only 167 } 168 for k, v := range paxHdrs { 169 // Forbid empty values (which represent deletion) since usage of 170 // them are non-sensible without global PAX record support. 171 if !validPAXRecord(k, v) || v == "" { 172 return formatUnknown, nil // Invalid PAX key 173 } 174 } 175 return format, paxHdrs 176 } 177 178 // headerFileInfo implements os.FileInfo. 179 type headerFileInfo struct { 180 h *Header 181 } 182 183 func (fi headerFileInfo) Size() int64 { return fi.h.Size } 184 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } 185 func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } 186 func (fi headerFileInfo) Sys() interface{} { return fi.h } 187 188 // Name returns the base name of the file. 189 func (fi headerFileInfo) Name() string { 190 if fi.IsDir() { 191 return path.Base(path.Clean(fi.h.Name)) 192 } 193 return path.Base(fi.h.Name) 194 } 195 196 // Mode returns the permission and mode bits for the headerFileInfo. 197 func (fi headerFileInfo) Mode() (mode os.FileMode) { 198 // Set file permission bits. 199 mode = os.FileMode(fi.h.Mode).Perm() 200 201 // Set setuid, setgid and sticky bits. 202 if fi.h.Mode&c_ISUID != 0 { 203 // setuid 204 mode |= os.ModeSetuid 205 } 206 if fi.h.Mode&c_ISGID != 0 { 207 // setgid 208 mode |= os.ModeSetgid 209 } 210 if fi.h.Mode&c_ISVTX != 0 { 211 // sticky 212 mode |= os.ModeSticky 213 } 214 215 // Set file mode bits. 216 // clear perm, setuid, setgid and sticky bits. 217 m := os.FileMode(fi.h.Mode) &^ 07777 218 if m == c_ISDIR { 219 // directory 220 mode |= os.ModeDir 221 } 222 if m == c_ISFIFO { 223 // named pipe (FIFO) 224 mode |= os.ModeNamedPipe 225 } 226 if m == c_ISLNK { 227 // symbolic link 228 mode |= os.ModeSymlink 229 } 230 if m == c_ISBLK { 231 // device file 232 mode |= os.ModeDevice 233 } 234 if m == c_ISCHR { 235 // Unix character device 236 mode |= os.ModeDevice 237 mode |= os.ModeCharDevice 238 } 239 if m == c_ISSOCK { 240 // Unix domain socket 241 mode |= os.ModeSocket 242 } 243 244 switch fi.h.Typeflag { 245 case TypeSymlink: 246 // symbolic link 247 mode |= os.ModeSymlink 248 case TypeChar: 249 // character device node 250 mode |= os.ModeDevice 251 mode |= os.ModeCharDevice 252 case TypeBlock: 253 // block device node 254 mode |= os.ModeDevice 255 case TypeDir: 256 // directory 257 mode |= os.ModeDir 258 case TypeFifo: 259 // fifo node 260 mode |= os.ModeNamedPipe 261 } 262 263 return mode 264 } 265 266 // sysStat, if non-nil, populates h from system-dependent fields of fi. 267 var sysStat func(fi os.FileInfo, h *Header) error 268 269 const ( 270 // Mode constants from the USTAR spec: 271 // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 272 c_ISUID = 04000 // Set uid 273 c_ISGID = 02000 // Set gid 274 c_ISVTX = 01000 // Save text (sticky bit) 275 276 // Common Unix mode constants; these are not defined in any common tar standard. 277 // Header.FileInfo understands these, but FileInfoHeader will never produce these. 278 c_ISDIR = 040000 // Directory 279 c_ISFIFO = 010000 // FIFO 280 c_ISREG = 0100000 // Regular file 281 c_ISLNK = 0120000 // Symbolic link 282 c_ISBLK = 060000 // Block special file 283 c_ISCHR = 020000 // Character special file 284 c_ISSOCK = 0140000 // Socket 285 ) 286 287 // Keywords for the PAX Extended Header 288 const ( 289 paxAtime = "atime" 290 paxCharset = "charset" 291 paxComment = "comment" 292 paxCtime = "ctime" // please note that ctime is not a valid pax header. 293 paxGid = "gid" 294 paxGname = "gname" 295 paxLinkpath = "linkpath" 296 paxMtime = "mtime" 297 paxPath = "path" 298 paxSize = "size" 299 paxUid = "uid" 300 paxUname = "uname" 301 paxXattr = "SCHILY.xattr." 302 paxNone = "" 303 ) 304 305 // FileInfoHeader creates a partially-populated Header from fi. 306 // If fi describes a symlink, FileInfoHeader records link as the link target. 307 // If fi describes a directory, a slash is appended to the name. 308 // Because os.FileInfo's Name method returns only the base name of 309 // the file it describes, it may be necessary to modify the Name field 310 // of the returned header to provide the full path name of the file. 311 func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { 312 if fi == nil { 313 return nil, errors.New("tar: FileInfo is nil") 314 } 315 fm := fi.Mode() 316 h := &Header{ 317 Name: fi.Name(), 318 ModTime: fi.ModTime(), 319 Mode: int64(fm.Perm()), // or'd with c_IS* constants later 320 } 321 switch { 322 case fm.IsRegular(): 323 h.Typeflag = TypeReg 324 h.Size = fi.Size() 325 case fi.IsDir(): 326 h.Typeflag = TypeDir 327 h.Name += "/" 328 case fm&os.ModeSymlink != 0: 329 h.Typeflag = TypeSymlink 330 h.Linkname = link 331 case fm&os.ModeDevice != 0: 332 if fm&os.ModeCharDevice != 0 { 333 h.Typeflag = TypeChar 334 } else { 335 h.Typeflag = TypeBlock 336 } 337 case fm&os.ModeNamedPipe != 0: 338 h.Typeflag = TypeFifo 339 case fm&os.ModeSocket != 0: 340 return nil, fmt.Errorf("tar: sockets not supported") 341 default: 342 return nil, fmt.Errorf("tar: unknown file mode %v", fm) 343 } 344 if fm&os.ModeSetuid != 0 { 345 h.Mode |= c_ISUID 346 } 347 if fm&os.ModeSetgid != 0 { 348 h.Mode |= c_ISGID 349 } 350 if fm&os.ModeSticky != 0 { 351 h.Mode |= c_ISVTX 352 } 353 // If possible, populate additional fields from OS-specific 354 // FileInfo fields. 355 if sys, ok := fi.Sys().(*Header); ok { 356 // This FileInfo came from a Header (not the OS). Use the 357 // original Header to populate all remaining fields. 358 h.Uid = sys.Uid 359 h.Gid = sys.Gid 360 h.Uname = sys.Uname 361 h.Gname = sys.Gname 362 h.AccessTime = sys.AccessTime 363 h.ChangeTime = sys.ChangeTime 364 if sys.Xattrs != nil { 365 h.Xattrs = make(map[string]string) 366 for k, v := range sys.Xattrs { 367 h.Xattrs[k] = v 368 } 369 } 370 if sys.Typeflag == TypeLink { 371 // hard link 372 h.Typeflag = TypeLink 373 h.Size = 0 374 h.Linkname = sys.Linkname 375 } 376 } 377 if sysStat != nil { 378 return h, sysStat(fi, h) 379 } 380 return h, nil 381 } 382 383 // isHeaderOnlyType checks if the given type flag is of the type that has no 384 // data section even if a size is specified. 385 func isHeaderOnlyType(flag byte) bool { 386 switch flag { 387 case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: 388 return true 389 default: 390 return false 391 } 392 }