github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/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 // 7 // Tape archives (tar) are a file format for storing a sequence of files that 8 // can be read and written in a streaming manner. 9 // This package aims to cover most variations of the format, 10 // including those produced by GNU and BSD tar tools. 11 package tar 12 13 import ( 14 "errors" 15 "fmt" 16 "math" 17 "os" 18 "path" 19 "reflect" 20 "strconv" 21 "strings" 22 "time" 23 ) 24 25 // BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit 26 // architectures. If a large value is encountered when decoding, the result 27 // stored in Header will be the truncated version. 28 29 var ( 30 ErrHeader = errors.New("tar: invalid tar header") 31 ErrWriteTooLong = errors.New("tar: write too long") 32 ErrFieldTooLong = errors.New("tar: header field too long") 33 ErrWriteAfterClose = errors.New("tar: write after close") 34 errMissData = errors.New("tar: sparse file references non-existent data") 35 errUnrefData = errors.New("tar: sparse file contains unreferenced data") 36 errWriteHole = errors.New("tar: write non-NUL byte in sparse hole") 37 ) 38 39 type headerError []string 40 41 func (he headerError) Error() string { 42 const prefix = "tar: cannot encode header" 43 var ss []string 44 for _, s := range he { 45 if s != "" { 46 ss = append(ss, s) 47 } 48 } 49 if len(ss) == 0 { 50 return prefix 51 } 52 return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and ")) 53 } 54 55 // Type flags for Header.Typeflag. 56 const ( 57 // Type '0' indicates a regular file. 58 TypeReg = '0' 59 TypeRegA = '\x00' // For legacy support; use TypeReg instead 60 61 // Type '1' to '6' are header-only flags and may not have a data body. 62 TypeLink = '1' // Hard link 63 TypeSymlink = '2' // Symbolic link 64 TypeChar = '3' // Character device node 65 TypeBlock = '4' // Block device node 66 TypeDir = '5' // Directory 67 TypeFifo = '6' // FIFO node 68 69 // Type '7' is reserved. 70 TypeCont = '7' 71 72 // Type 'x' is used by the PAX format to store key-value records that 73 // are only relevant to the next file. 74 // This package transparently handles these types. 75 TypeXHeader = 'x' 76 77 // Type 'g' is used by the PAX format to store key-value records that 78 // are relevant to all subsequent files. 79 // This package only supports parsing and composing such headers, 80 // but does not currently support persisting the global state across files. 81 TypeXGlobalHeader = 'g' 82 83 // Type 'S' indicates a sparse file in the GNU format. 84 // Header.SparseHoles should be populated when using this type. 85 TypeGNUSparse = 'S' 86 87 // Types 'L' and 'K' are used by the GNU format for a meta file 88 // used to store the path or link name for the next file. 89 // This package transparently handles these types. 90 TypeGNULongName = 'L' 91 TypeGNULongLink = 'K' 92 ) 93 94 // Keywords for PAX extended header records. 95 const ( 96 paxNone = "" // Indicates that no PAX key is suitable 97 paxPath = "path" 98 paxLinkpath = "linkpath" 99 paxSize = "size" 100 paxUid = "uid" 101 paxGid = "gid" 102 paxUname = "uname" 103 paxGname = "gname" 104 paxMtime = "mtime" 105 paxAtime = "atime" 106 paxCtime = "ctime" // Removed from later revision of PAX spec, but was valid 107 paxCharset = "charset" // Currently unused 108 paxComment = "comment" // Currently unused 109 110 paxSchilyXattr = "SCHILY.xattr." 111 112 // Keywords for GNU sparse files in a PAX extended header. 113 paxGNUSparse = "GNU.sparse." 114 paxGNUSparseNumBlocks = "GNU.sparse.numblocks" 115 paxGNUSparseOffset = "GNU.sparse.offset" 116 paxGNUSparseNumBytes = "GNU.sparse.numbytes" 117 paxGNUSparseMap = "GNU.sparse.map" 118 paxGNUSparseName = "GNU.sparse.name" 119 paxGNUSparseMajor = "GNU.sparse.major" 120 paxGNUSparseMinor = "GNU.sparse.minor" 121 paxGNUSparseSize = "GNU.sparse.size" 122 paxGNUSparseRealSize = "GNU.sparse.realsize" 123 ) 124 125 // basicKeys is a set of the PAX keys for which we have built-in support. 126 // This does not contain "charset" or "comment", which are both PAX-specific, 127 // so adding them as first-class features of Header is unlikely. 128 // Users can use the PAXRecords field to set it themselves. 129 var basicKeys = map[string]bool{ 130 paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true, 131 paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true, 132 } 133 134 // A Header represents a single header in a tar archive. 135 // Some fields may not be populated. 136 // 137 // For forward compatibility, users that retrieve a Header from Reader.Next, 138 // mutate it in some ways, and then pass it back to Writer.WriteHeader 139 // should do so by creating a new Header and copying the fields 140 // that they are interested in preserving. 141 type Header struct { 142 Typeflag byte // Type of header entry (should be TypeReg for most files) 143 144 Name string // Name of file entry 145 Linkname string // Target name of link (valid for TypeLink or TypeSymlink) 146 147 Size int64 // Logical file size in bytes 148 Mode int64 // Permission and mode bits 149 Uid int // User ID of owner 150 Gid int // Group ID of owner 151 Uname string // User name of owner 152 Gname string // Group name of owner 153 154 // The PAX format encodes the timestamps with sub-second resolution, 155 // while the other formats (USTAR and GNU) truncate to the nearest second. 156 // If the Format is unspecified, then Writer.WriteHeader ignores 157 // AccessTime and ChangeTime when using the USTAR format. 158 ModTime time.Time // Modification time 159 AccessTime time.Time // Access time (requires either PAX or GNU support) 160 ChangeTime time.Time // Change time (requires either PAX or GNU support) 161 162 Devmajor int64 // Major device number (valid for TypeChar or TypeBlock) 163 Devminor int64 // Minor device number (valid for TypeChar or TypeBlock) 164 165 // SparseHoles represents a sequence of holes in a sparse file. 166 // 167 // A file is sparse if len(SparseHoles) > 0 or Typeflag is TypeGNUSparse. 168 // If TypeGNUSparse is set, then the format is GNU, otherwise 169 // the format is PAX (by using GNU-specific PAX records). 170 // 171 // A sparse file consists of fragments of data, intermixed with holes 172 // (described by this field). A hole is semantically a block of NUL-bytes, 173 // but does not actually exist within the tar file. 174 // The holes must be sorted in ascending order, 175 // not overlap with each other, and not extend past the specified Size. 176 SparseHoles []SparseEntry 177 178 // Xattrs stores extended attributes as PAX records under the 179 // "SCHILY.xattr." namespace. 180 // 181 // The following are semantically equivalent: 182 // h.Xattrs[key] = value 183 // h.PAXRecords["SCHILY.xattr."+key] = value 184 // 185 // When Writer.WriteHeader is called, the contents of Xattrs will take 186 // precedence over those in PAXRecords. 187 // 188 // Deprecated: Use PAXRecords instead. 189 Xattrs map[string]string 190 191 // PAXRecords is a map of PAX extended header records. 192 // 193 // User-defined records should have keys of the following form: 194 // VENDOR.keyword 195 // Where VENDOR is some namespace in all uppercase, and keyword may 196 // not contain the '=' character (e.g., "GOLANG.pkg.version"). 197 // The key and value should be non-empty UTF-8 strings. 198 // 199 // When Writer.WriteHeader is called, PAX records derived from the 200 // the other fields in Header take precedence over PAXRecords. 201 PAXRecords map[string]string 202 203 // Format specifies the format of the tar header. 204 // 205 // This is set by Reader.Next as a best-effort guess at the format. 206 // Since the Reader liberally reads some non-compliant files, 207 // it is possible for this to be FormatUnknown. 208 // 209 // If the format is unspecified when Writer.WriteHeader is called, 210 // then it uses the first format (in the order of USTAR, PAX, GNU) 211 // capable of encoding this Header (see Format). 212 Format Format 213 } 214 215 // SparseEntry represents a Length-sized fragment at Offset in the file. 216 type SparseEntry struct{ Offset, Length int64 } 217 218 func (s SparseEntry) endOffset() int64 { return s.Offset + s.Length } 219 220 // A sparse file can be represented as either a sparseDatas or a sparseHoles. 221 // As long as the total size is known, they are equivalent and one can be 222 // converted to the other form and back. The various tar formats with sparse 223 // file support represent sparse files in the sparseDatas form. That is, they 224 // specify the fragments in the file that has data, and treat everything else as 225 // having zero bytes. As such, the encoding and decoding logic in this package 226 // deals with sparseDatas. 227 // 228 // However, the external API uses sparseHoles instead of sparseDatas because the 229 // zero value of sparseHoles logically represents a normal file (i.e., there are 230 // no holes in it). On the other hand, the zero value of sparseDatas implies 231 // that the file has no data in it, which is rather odd. 232 // 233 // As an example, if the underlying raw file contains the 10-byte data: 234 // var compactFile = "abcdefgh" 235 // 236 // And the sparse map has the following entries: 237 // var spd sparseDatas = []sparseEntry{ 238 // {Offset: 2, Length: 5}, // Data fragment for 2..6 239 // {Offset: 18, Length: 3}, // Data fragment for 18..20 240 // } 241 // var sph sparseHoles = []SparseEntry{ 242 // {Offset: 0, Length: 2}, // Hole fragment for 0..1 243 // {Offset: 7, Length: 11}, // Hole fragment for 7..17 244 // {Offset: 21, Length: 4}, // Hole fragment for 21..24 245 // } 246 // 247 // Then the content of the resulting sparse file with a Header.Size of 25 is: 248 // var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4 249 type ( 250 sparseDatas []SparseEntry 251 sparseHoles []SparseEntry 252 ) 253 254 // validateSparseEntries reports whether sp is a valid sparse map. 255 // It does not matter whether sp represents data fragments or hole fragments. 256 func validateSparseEntries(sp []SparseEntry, size int64) bool { 257 // Validate all sparse entries. These are the same checks as performed by 258 // the BSD tar utility. 259 if size < 0 { 260 return false 261 } 262 var pre SparseEntry 263 for _, cur := range sp { 264 switch { 265 case cur.Offset < 0 || cur.Length < 0: 266 return false // Negative values are never okay 267 case cur.Offset > math.MaxInt64-cur.Length: 268 return false // Integer overflow with large length 269 case cur.endOffset() > size: 270 return false // Region extends beyond the actual size 271 case pre.endOffset() > cur.Offset: 272 return false // Regions cannot overlap and must be in order 273 } 274 pre = cur 275 } 276 return true 277 } 278 279 // alignSparseEntries mutates src and returns dst where each fragment's 280 // starting offset is aligned up to the nearest block edge, and each 281 // ending offset is aligned down to the nearest block edge. 282 // 283 // Even though the Go tar Reader and the BSD tar utility can handle entries 284 // with arbitrary offsets and lengths, the GNU tar utility can only handle 285 // offsets and lengths that are multiples of blockSize. 286 func alignSparseEntries(src []SparseEntry, size int64) []SparseEntry { 287 dst := src[:0] 288 for _, s := range src { 289 pos, end := s.Offset, s.endOffset() 290 pos += blockPadding(+pos) // Round-up to nearest blockSize 291 if end != size { 292 end -= blockPadding(-end) // Round-down to nearest blockSize 293 } 294 if pos < end { 295 dst = append(dst, SparseEntry{Offset: pos, Length: end - pos}) 296 } 297 } 298 return dst 299 } 300 301 // invertSparseEntries converts a sparse map from one form to the other. 302 // If the input is sparseHoles, then it will output sparseDatas and vice-versa. 303 // The input must have been already validated. 304 // 305 // This function mutates src and returns a normalized map where: 306 // * adjacent fragments are coalesced together 307 // * only the last fragment may be empty 308 // * the endOffset of the last fragment is the total size 309 func invertSparseEntries(src []SparseEntry, size int64) []SparseEntry { 310 dst := src[:0] 311 var pre SparseEntry 312 for _, cur := range src { 313 if cur.Length == 0 { 314 continue // Skip empty fragments 315 } 316 pre.Length = cur.Offset - pre.Offset 317 if pre.Length > 0 { 318 dst = append(dst, pre) // Only add non-empty fragments 319 } 320 pre.Offset = cur.endOffset() 321 } 322 pre.Length = size - pre.Offset // Possibly the only empty fragment 323 return append(dst, pre) 324 } 325 326 type fileState interface { 327 // Remaining reports the number of remaining bytes in the current file. 328 // This count includes any sparse holes that may exist. 329 Remaining() int64 330 } 331 332 // allowedFormats determines which formats can be used. 333 // The value returned is the logical OR of multiple possible formats. 334 // If the value is FormatUnknown, then the input Header cannot be encoded 335 // and an error is returned explaining why. 336 // 337 // As a by-product of checking the fields, this function returns paxHdrs, which 338 // contain all fields that could not be directly encoded. 339 func (h *Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) { 340 format = FormatUSTAR | FormatPAX | FormatGNU 341 paxHdrs = make(map[string]string) 342 343 var whyNoUSTAR, whyNoPAX, whyNoGNU string 344 var preferPAX bool // Prefer PAX over USTAR 345 verifyString := func(s string, size int, name, paxKey string) { 346 // NUL-terminator is optional for path and linkpath. 347 // Technically, it is required for uname and gname, 348 // but neither GNU nor BSD tar checks for it. 349 tooLong := len(s) > size 350 allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath 351 if hasNUL(s) || (tooLong && !allowLongGNU) { 352 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s) 353 format.mustNotBe(FormatGNU) 354 } 355 if !isASCII(s) || tooLong { 356 canSplitUSTAR := paxKey == paxPath 357 if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok { 358 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s) 359 format.mustNotBe(FormatUSTAR) 360 } 361 if paxKey == paxNone { 362 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s) 363 format.mustNotBe(FormatPAX) 364 } else { 365 paxHdrs[paxKey] = s 366 } 367 } 368 if v, ok := h.PAXRecords[paxKey]; ok && v == s { 369 paxHdrs[paxKey] = v 370 } 371 } 372 verifyNumeric := func(n int64, size int, name, paxKey string) { 373 if !fitsInBase256(size, n) { 374 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n) 375 format.mustNotBe(FormatGNU) 376 } 377 if !fitsInOctal(size, n) { 378 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n) 379 format.mustNotBe(FormatUSTAR) 380 if paxKey == paxNone { 381 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n) 382 format.mustNotBe(FormatPAX) 383 } else { 384 paxHdrs[paxKey] = strconv.FormatInt(n, 10) 385 } 386 } 387 if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) { 388 paxHdrs[paxKey] = v 389 } 390 } 391 verifyTime := func(ts time.Time, size int, name, paxKey string) { 392 if ts.IsZero() { 393 return // Always okay 394 } 395 if !fitsInBase256(size, ts.Unix()) { 396 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts) 397 format.mustNotBe(FormatGNU) 398 } 399 isMtime := paxKey == paxMtime 400 fitsOctal := fitsInOctal(size, ts.Unix()) 401 noACTime := !isMtime && h.Format != FormatUnknown 402 if (isMtime && !fitsOctal) || noACTime { 403 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts) 404 format.mustNotBe(FormatUSTAR) 405 } 406 needsNano := ts.Nanosecond() != 0 407 if !isMtime || !fitsOctal || needsNano { 408 preferPAX = true // USTAR may truncate sub-second measurements 409 if paxKey == paxNone { 410 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts) 411 format.mustNotBe(FormatPAX) 412 } else { 413 paxHdrs[paxKey] = formatPAXTime(ts) 414 } 415 } 416 if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) { 417 paxHdrs[paxKey] = v 418 } 419 } 420 421 // Check basic fields. 422 var blk block 423 v7 := blk.V7() 424 ustar := blk.USTAR() 425 gnu := blk.GNU() 426 verifyString(h.Name, len(v7.Name()), "Name", paxPath) 427 verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath) 428 verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname) 429 verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname) 430 verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone) 431 verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid) 432 verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid) 433 verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize) 434 verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone) 435 verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone) 436 verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime) 437 verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime) 438 verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime) 439 440 // Check for header-only types. 441 var whyOnlyPAX, whyOnlyGNU string 442 switch h.Typeflag { 443 case TypeXHeader, TypeGNULongName, TypeGNULongLink: 444 return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"} 445 case TypeXGlobalHeader: 446 if !reflect.DeepEqual(h, &Header{Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format}) { 447 return FormatUnknown, nil, headerError{"only PAXRecords may be set for TypeXGlobalHeader"} 448 } 449 whyOnlyPAX = "only PAX supports TypeXGlobalHeader" 450 format.mayOnlyBe(FormatPAX) 451 } 452 if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 { 453 return FormatUnknown, nil, headerError{"negative size on header-only type"} 454 } 455 456 // Check PAX records. 457 if len(h.Xattrs) > 0 { 458 for k, v := range h.Xattrs { 459 paxHdrs[paxSchilyXattr+k] = v 460 } 461 whyOnlyPAX = "only PAX supports Xattrs" 462 format.mayOnlyBe(FormatPAX) 463 } 464 if len(h.PAXRecords) > 0 { 465 for k, v := range h.PAXRecords { 466 switch _, exists := paxHdrs[k]; { 467 case exists: 468 continue // Do not overwrite existing records 469 case h.Typeflag == TypeXGlobalHeader: 470 paxHdrs[k] = v // Copy all records 471 case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse): 472 paxHdrs[k] = v // Ignore local records that may conflict 473 } 474 } 475 whyOnlyPAX = "only PAX supports PAXRecords" 476 format.mayOnlyBe(FormatPAX) 477 } 478 for k, v := range paxHdrs { 479 if !validPAXRecord(k, v) { 480 return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)} 481 } 482 } 483 484 // Check sparse files. 485 if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse { 486 if isHeaderOnlyType(h.Typeflag) { 487 return FormatUnknown, nil, headerError{"header-only type cannot be sparse"} 488 } 489 if !validateSparseEntries(h.SparseHoles, h.Size) { 490 return FormatUnknown, nil, headerError{"invalid sparse holes"} 491 } 492 if h.Typeflag == TypeGNUSparse { 493 whyOnlyGNU = "only GNU supports TypeGNUSparse" 494 format.mayOnlyBe(FormatGNU) 495 } else { 496 whyNoGNU = "GNU supports sparse files only with TypeGNUSparse" 497 format.mustNotBe(FormatGNU) 498 } 499 whyNoUSTAR = "USTAR does not support sparse files" 500 format.mustNotBe(FormatUSTAR) 501 } 502 503 // Check desired format. 504 if wantFormat := h.Format; wantFormat != FormatUnknown { 505 if wantFormat.has(FormatPAX) && !preferPAX { 506 wantFormat.mayBe(FormatUSTAR) // PAX implies USTAR allowed too 507 } 508 format.mayOnlyBe(wantFormat) // Set union of formats allowed and format wanted 509 } 510 if format == FormatUnknown { 511 switch h.Format { 512 case FormatUSTAR: 513 err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU} 514 case FormatPAX: 515 err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU} 516 case FormatGNU: 517 err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX} 518 default: 519 err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU} 520 } 521 } 522 return format, paxHdrs, err 523 } 524 525 // FileInfo returns an os.FileInfo for the Header. 526 func (h *Header) FileInfo() os.FileInfo { 527 return headerFileInfo{h} 528 } 529 530 // headerFileInfo implements os.FileInfo. 531 type headerFileInfo struct { 532 h *Header 533 } 534 535 func (fi headerFileInfo) Size() int64 { return fi.h.Size } 536 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } 537 func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } 538 func (fi headerFileInfo) Sys() interface{} { return fi.h } 539 540 // Name returns the base name of the file. 541 func (fi headerFileInfo) Name() string { 542 if fi.IsDir() { 543 return path.Base(path.Clean(fi.h.Name)) 544 } 545 return path.Base(fi.h.Name) 546 } 547 548 // Mode returns the permission and mode bits for the headerFileInfo. 549 func (fi headerFileInfo) Mode() (mode os.FileMode) { 550 // Set file permission bits. 551 mode = os.FileMode(fi.h.Mode).Perm() 552 553 // Set setuid, setgid and sticky bits. 554 if fi.h.Mode&c_ISUID != 0 { 555 mode |= os.ModeSetuid 556 } 557 if fi.h.Mode&c_ISGID != 0 { 558 mode |= os.ModeSetgid 559 } 560 if fi.h.Mode&c_ISVTX != 0 { 561 mode |= os.ModeSticky 562 } 563 564 // Set file mode bits; clear perm, setuid, setgid, and sticky bits. 565 switch m := os.FileMode(fi.h.Mode) &^ 07777; m { 566 case c_ISDIR: 567 mode |= os.ModeDir 568 case c_ISFIFO: 569 mode |= os.ModeNamedPipe 570 case c_ISLNK: 571 mode |= os.ModeSymlink 572 case c_ISBLK: 573 mode |= os.ModeDevice 574 case c_ISCHR: 575 mode |= os.ModeDevice 576 mode |= os.ModeCharDevice 577 case c_ISSOCK: 578 mode |= os.ModeSocket 579 } 580 581 switch fi.h.Typeflag { 582 case TypeSymlink: 583 mode |= os.ModeSymlink 584 case TypeChar: 585 mode |= os.ModeDevice 586 mode |= os.ModeCharDevice 587 case TypeBlock: 588 mode |= os.ModeDevice 589 case TypeDir: 590 mode |= os.ModeDir 591 case TypeFifo: 592 mode |= os.ModeNamedPipe 593 } 594 595 return mode 596 } 597 598 // sysStat, if non-nil, populates h from system-dependent fields of fi. 599 var sysStat func(fi os.FileInfo, h *Header) error 600 601 const ( 602 // Mode constants from the USTAR spec: 603 // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 604 c_ISUID = 04000 // Set uid 605 c_ISGID = 02000 // Set gid 606 c_ISVTX = 01000 // Save text (sticky bit) 607 608 // Common Unix mode constants; these are not defined in any common tar standard. 609 // Header.FileInfo understands these, but FileInfoHeader will never produce these. 610 c_ISDIR = 040000 // Directory 611 c_ISFIFO = 010000 // FIFO 612 c_ISREG = 0100000 // Regular file 613 c_ISLNK = 0120000 // Symbolic link 614 c_ISBLK = 060000 // Block special file 615 c_ISCHR = 020000 // Character special file 616 c_ISSOCK = 0140000 // Socket 617 ) 618 619 // FileInfoHeader creates a partially-populated Header from fi. 620 // If fi describes a symlink, FileInfoHeader records link as the link target. 621 // If fi describes a directory, a slash is appended to the name. 622 // 623 // Since os.FileInfo's Name method only returns the base name of 624 // the file it describes, it may be necessary to modify Header.Name 625 // to provide the full path name of the file. 626 // 627 // This function does not populate Header.SparseHoles. 628 func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { 629 if fi == nil { 630 return nil, errors.New("tar: FileInfo is nil") 631 } 632 fm := fi.Mode() 633 h := &Header{ 634 Name: fi.Name(), 635 ModTime: fi.ModTime(), 636 Mode: int64(fm.Perm()), // or'd with c_IS* constants later 637 } 638 switch { 639 case fm.IsRegular(): 640 h.Typeflag = TypeReg 641 h.Size = fi.Size() 642 case fi.IsDir(): 643 h.Typeflag = TypeDir 644 h.Name += "/" 645 case fm&os.ModeSymlink != 0: 646 h.Typeflag = TypeSymlink 647 h.Linkname = link 648 case fm&os.ModeDevice != 0: 649 if fm&os.ModeCharDevice != 0 { 650 h.Typeflag = TypeChar 651 } else { 652 h.Typeflag = TypeBlock 653 } 654 case fm&os.ModeNamedPipe != 0: 655 h.Typeflag = TypeFifo 656 case fm&os.ModeSocket != 0: 657 return nil, fmt.Errorf("tar: sockets not supported") 658 default: 659 return nil, fmt.Errorf("tar: unknown file mode %v", fm) 660 } 661 if fm&os.ModeSetuid != 0 { 662 h.Mode |= c_ISUID 663 } 664 if fm&os.ModeSetgid != 0 { 665 h.Mode |= c_ISGID 666 } 667 if fm&os.ModeSticky != 0 { 668 h.Mode |= c_ISVTX 669 } 670 // If possible, populate additional fields from OS-specific 671 // FileInfo fields. 672 if sys, ok := fi.Sys().(*Header); ok { 673 // This FileInfo came from a Header (not the OS). Use the 674 // original Header to populate all remaining fields. 675 h.Uid = sys.Uid 676 h.Gid = sys.Gid 677 h.Uname = sys.Uname 678 h.Gname = sys.Gname 679 h.AccessTime = sys.AccessTime 680 h.ChangeTime = sys.ChangeTime 681 if sys.Xattrs != nil { 682 h.Xattrs = make(map[string]string) 683 for k, v := range sys.Xattrs { 684 h.Xattrs[k] = v 685 } 686 } 687 if sys.Typeflag == TypeLink { 688 // hard link 689 h.Typeflag = TypeLink 690 h.Size = 0 691 h.Linkname = sys.Linkname 692 } 693 if sys.SparseHoles != nil { 694 h.SparseHoles = append([]SparseEntry{}, sys.SparseHoles...) 695 } 696 if sys.PAXRecords != nil { 697 h.PAXRecords = make(map[string]string) 698 for k, v := range sys.PAXRecords { 699 h.PAXRecords[k] = v 700 } 701 } 702 } 703 if sysStat != nil { 704 return h, sysStat(fi, h) 705 } 706 return h, nil 707 } 708 709 // isHeaderOnlyType checks if the given type flag is of the type that has no 710 // data section even if a size is specified. 711 func isHeaderOnlyType(flag byte) bool { 712 switch flag { 713 case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: 714 return true 715 default: 716 return false 717 } 718 } 719 720 func min(a, b int64) int64 { 721 if a < b { 722 return a 723 } 724 return b 725 }