github.com/kdevb0x/go@v0.0.0-20180115030120-39687051e9e7/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("archive/tar: invalid tar header") 31 ErrWriteTooLong = errors.New("archive/tar: write too long") 32 ErrFieldTooLong = errors.New("archive/tar: header field too long") 33 ErrWriteAfterClose = errors.New("archive/tar: write after close") 34 errMissData = errors.New("archive/tar: sparse file references non-existent data") 35 errUnrefData = errors.New("archive/tar: sparse file contains unreferenced data") 36 errWriteHole = errors.New("archive/tar: write non-NUL byte in sparse hole") 37 ) 38 39 type headerError []string 40 41 func (he headerError) Error() string { 42 const prefix = "archive/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 TypeGNUSparse = 'S' 85 86 // Types 'L' and 'K' are used by the GNU format for a meta file 87 // used to store the path or link name for the next file. 88 // This package transparently handles these types. 89 TypeGNULongName = 'L' 90 TypeGNULongLink = 'K' 91 ) 92 93 // Keywords for PAX extended header records. 94 const ( 95 paxNone = "" // Indicates that no PAX key is suitable 96 paxPath = "path" 97 paxLinkpath = "linkpath" 98 paxSize = "size" 99 paxUid = "uid" 100 paxGid = "gid" 101 paxUname = "uname" 102 paxGname = "gname" 103 paxMtime = "mtime" 104 paxAtime = "atime" 105 paxCtime = "ctime" // Removed from later revision of PAX spec, but was valid 106 paxCharset = "charset" // Currently unused 107 paxComment = "comment" // Currently unused 108 109 paxSchilyXattr = "SCHILY.xattr." 110 111 // Keywords for GNU sparse files in a PAX extended header. 112 paxGNUSparse = "GNU.sparse." 113 paxGNUSparseNumBlocks = "GNU.sparse.numblocks" 114 paxGNUSparseOffset = "GNU.sparse.offset" 115 paxGNUSparseNumBytes = "GNU.sparse.numbytes" 116 paxGNUSparseMap = "GNU.sparse.map" 117 paxGNUSparseName = "GNU.sparse.name" 118 paxGNUSparseMajor = "GNU.sparse.major" 119 paxGNUSparseMinor = "GNU.sparse.minor" 120 paxGNUSparseSize = "GNU.sparse.size" 121 paxGNUSparseRealSize = "GNU.sparse.realsize" 122 ) 123 124 // basicKeys is a set of the PAX keys for which we have built-in support. 125 // This does not contain "charset" or "comment", which are both PAX-specific, 126 // so adding them as first-class features of Header is unlikely. 127 // Users can use the PAXRecords field to set it themselves. 128 var basicKeys = map[string]bool{ 129 paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true, 130 paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true, 131 } 132 133 // A Header represents a single header in a tar archive. 134 // Some fields may not be populated. 135 // 136 // For forward compatibility, users that retrieve a Header from Reader.Next, 137 // mutate it in some ways, and then pass it back to Writer.WriteHeader 138 // should do so by creating a new Header and copying the fields 139 // that they are interested in preserving. 140 type Header struct { 141 Typeflag byte // Type of header entry (should be TypeReg for most files) 142 143 Name string // Name of file entry 144 Linkname string // Target name of link (valid for TypeLink or TypeSymlink) 145 146 Size int64 // Logical file size in bytes 147 Mode int64 // Permission and mode bits 148 Uid int // User ID of owner 149 Gid int // Group ID of owner 150 Uname string // User name of owner 151 Gname string // Group name of owner 152 153 // If the Format is unspecified, then Writer.WriteHeader rounds ModTime 154 // to the nearest second and ignores the AccessTime and ChangeTime fields. 155 // 156 // To use AccessTime or ChangeTime, specify the Format as PAX or GNU. 157 // To use sub-second resolution, specify the Format as PAX. 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 // Xattrs stores extended attributes as PAX records under the 166 // "SCHILY.xattr." namespace. 167 // 168 // The following are semantically equivalent: 169 // h.Xattrs[key] = value 170 // h.PAXRecords["SCHILY.xattr."+key] = value 171 // 172 // When Writer.WriteHeader is called, the contents of Xattrs will take 173 // precedence over those in PAXRecords. 174 // 175 // Deprecated: Use PAXRecords instead. 176 Xattrs map[string]string 177 178 // PAXRecords is a map of PAX extended header records. 179 // 180 // User-defined records should have keys of the following form: 181 // VENDOR.keyword 182 // Where VENDOR is some namespace in all uppercase, and keyword may 183 // not contain the '=' character (e.g., "GOLANG.pkg.version"). 184 // The key and value should be non-empty UTF-8 strings. 185 // 186 // When Writer.WriteHeader is called, PAX records derived from the 187 // the other fields in Header take precedence over PAXRecords. 188 PAXRecords map[string]string 189 190 // Format specifies the format of the tar header. 191 // 192 // This is set by Reader.Next as a best-effort guess at the format. 193 // Since the Reader liberally reads some non-compliant files, 194 // it is possible for this to be FormatUnknown. 195 // 196 // If the format is unspecified when Writer.WriteHeader is called, 197 // then it uses the first format (in the order of USTAR, PAX, GNU) 198 // capable of encoding this Header (see Format). 199 Format Format 200 } 201 202 // sparseEntry represents a Length-sized fragment at Offset in the file. 203 type sparseEntry struct{ Offset, Length int64 } 204 205 func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length } 206 207 // A sparse file can be represented as either a sparseDatas or a sparseHoles. 208 // As long as the total size is known, they are equivalent and one can be 209 // converted to the other form and back. The various tar formats with sparse 210 // file support represent sparse files in the sparseDatas form. That is, they 211 // specify the fragments in the file that has data, and treat everything else as 212 // having zero bytes. As such, the encoding and decoding logic in this package 213 // deals with sparseDatas. 214 // 215 // However, the external API uses sparseHoles instead of sparseDatas because the 216 // zero value of sparseHoles logically represents a normal file (i.e., there are 217 // no holes in it). On the other hand, the zero value of sparseDatas implies 218 // that the file has no data in it, which is rather odd. 219 // 220 // As an example, if the underlying raw file contains the 10-byte data: 221 // var compactFile = "abcdefgh" 222 // 223 // And the sparse map has the following entries: 224 // var spd sparseDatas = []sparseEntry{ 225 // {Offset: 2, Length: 5}, // Data fragment for 2..6 226 // {Offset: 18, Length: 3}, // Data fragment for 18..20 227 // } 228 // var sph sparseHoles = []sparseEntry{ 229 // {Offset: 0, Length: 2}, // Hole fragment for 0..1 230 // {Offset: 7, Length: 11}, // Hole fragment for 7..17 231 // {Offset: 21, Length: 4}, // Hole fragment for 21..24 232 // } 233 // 234 // Then the content of the resulting sparse file with a Header.Size of 25 is: 235 // var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4 236 type ( 237 sparseDatas []sparseEntry 238 sparseHoles []sparseEntry 239 ) 240 241 // validateSparseEntries reports whether sp is a valid sparse map. 242 // It does not matter whether sp represents data fragments or hole fragments. 243 func validateSparseEntries(sp []sparseEntry, size int64) bool { 244 // Validate all sparse entries. These are the same checks as performed by 245 // the BSD tar utility. 246 if size < 0 { 247 return false 248 } 249 var pre sparseEntry 250 for _, cur := range sp { 251 switch { 252 case cur.Offset < 0 || cur.Length < 0: 253 return false // Negative values are never okay 254 case cur.Offset > math.MaxInt64-cur.Length: 255 return false // Integer overflow with large length 256 case cur.endOffset() > size: 257 return false // Region extends beyond the actual size 258 case pre.endOffset() > cur.Offset: 259 return false // Regions cannot overlap and must be in order 260 } 261 pre = cur 262 } 263 return true 264 } 265 266 // alignSparseEntries mutates src and returns dst where each fragment's 267 // starting offset is aligned up to the nearest block edge, and each 268 // ending offset is aligned down to the nearest block edge. 269 // 270 // Even though the Go tar Reader and the BSD tar utility can handle entries 271 // with arbitrary offsets and lengths, the GNU tar utility can only handle 272 // offsets and lengths that are multiples of blockSize. 273 func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry { 274 dst := src[:0] 275 for _, s := range src { 276 pos, end := s.Offset, s.endOffset() 277 pos += blockPadding(+pos) // Round-up to nearest blockSize 278 if end != size { 279 end -= blockPadding(-end) // Round-down to nearest blockSize 280 } 281 if pos < end { 282 dst = append(dst, sparseEntry{Offset: pos, Length: end - pos}) 283 } 284 } 285 return dst 286 } 287 288 // invertSparseEntries converts a sparse map from one form to the other. 289 // If the input is sparseHoles, then it will output sparseDatas and vice-versa. 290 // The input must have been already validated. 291 // 292 // This function mutates src and returns a normalized map where: 293 // * adjacent fragments are coalesced together 294 // * only the last fragment may be empty 295 // * the endOffset of the last fragment is the total size 296 func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry { 297 dst := src[:0] 298 var pre sparseEntry 299 for _, cur := range src { 300 if cur.Length == 0 { 301 continue // Skip empty fragments 302 } 303 pre.Length = cur.Offset - pre.Offset 304 if pre.Length > 0 { 305 dst = append(dst, pre) // Only add non-empty fragments 306 } 307 pre.Offset = cur.endOffset() 308 } 309 pre.Length = size - pre.Offset // Possibly the only empty fragment 310 return append(dst, pre) 311 } 312 313 // fileState tracks the number of logical (includes sparse holes) and physical 314 // (actual in tar archive) bytes remaining for the current file. 315 // 316 // Invariant: LogicalRemaining >= PhysicalRemaining 317 type fileState interface { 318 LogicalRemaining() int64 319 PhysicalRemaining() int64 320 } 321 322 // allowedFormats determines which formats can be used. 323 // The value returned is the logical OR of multiple possible formats. 324 // If the value is FormatUnknown, then the input Header cannot be encoded 325 // and an error is returned explaining why. 326 // 327 // As a by-product of checking the fields, this function returns paxHdrs, which 328 // contain all fields that could not be directly encoded. 329 // A value receiver ensures that this method does not mutate the source Header. 330 func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) { 331 format = FormatUSTAR | FormatPAX | FormatGNU 332 paxHdrs = make(map[string]string) 333 334 var whyNoUSTAR, whyNoPAX, whyNoGNU string 335 var preferPAX bool // Prefer PAX over USTAR 336 verifyString := func(s string, size int, name, paxKey string) { 337 // NUL-terminator is optional for path and linkpath. 338 // Technically, it is required for uname and gname, 339 // but neither GNU nor BSD tar checks for it. 340 tooLong := len(s) > size 341 allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath 342 if hasNUL(s) || (tooLong && !allowLongGNU) { 343 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s) 344 format.mustNotBe(FormatGNU) 345 } 346 if !isASCII(s) || tooLong { 347 canSplitUSTAR := paxKey == paxPath 348 if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok { 349 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s) 350 format.mustNotBe(FormatUSTAR) 351 } 352 if paxKey == paxNone { 353 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s) 354 format.mustNotBe(FormatPAX) 355 } else { 356 paxHdrs[paxKey] = s 357 } 358 } 359 if v, ok := h.PAXRecords[paxKey]; ok && v == s { 360 paxHdrs[paxKey] = v 361 } 362 } 363 verifyNumeric := func(n int64, size int, name, paxKey string) { 364 if !fitsInBase256(size, n) { 365 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n) 366 format.mustNotBe(FormatGNU) 367 } 368 if !fitsInOctal(size, n) { 369 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n) 370 format.mustNotBe(FormatUSTAR) 371 if paxKey == paxNone { 372 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n) 373 format.mustNotBe(FormatPAX) 374 } else { 375 paxHdrs[paxKey] = strconv.FormatInt(n, 10) 376 } 377 } 378 if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) { 379 paxHdrs[paxKey] = v 380 } 381 } 382 verifyTime := func(ts time.Time, size int, name, paxKey string) { 383 if ts.IsZero() { 384 return // Always okay 385 } 386 if !fitsInBase256(size, ts.Unix()) { 387 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts) 388 format.mustNotBe(FormatGNU) 389 } 390 isMtime := paxKey == paxMtime 391 fitsOctal := fitsInOctal(size, ts.Unix()) 392 if (isMtime && !fitsOctal) || !isMtime { 393 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts) 394 format.mustNotBe(FormatUSTAR) 395 } 396 needsNano := ts.Nanosecond() != 0 397 if !isMtime || !fitsOctal || needsNano { 398 preferPAX = true // USTAR may truncate sub-second measurements 399 if paxKey == paxNone { 400 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts) 401 format.mustNotBe(FormatPAX) 402 } else { 403 paxHdrs[paxKey] = formatPAXTime(ts) 404 } 405 } 406 if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) { 407 paxHdrs[paxKey] = v 408 } 409 } 410 411 // Check basic fields. 412 var blk block 413 v7 := blk.V7() 414 ustar := blk.USTAR() 415 gnu := blk.GNU() 416 verifyString(h.Name, len(v7.Name()), "Name", paxPath) 417 verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath) 418 verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname) 419 verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname) 420 verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone) 421 verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid) 422 verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid) 423 verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize) 424 verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone) 425 verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone) 426 verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime) 427 verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime) 428 verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime) 429 430 // Check for header-only types. 431 var whyOnlyPAX, whyOnlyGNU string 432 switch h.Typeflag { 433 case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse: 434 // Exclude TypeLink and TypeSymlink, since they may reference directories. 435 if strings.HasSuffix(h.Name, "/") { 436 return FormatUnknown, nil, headerError{"filename may not have trailing slash"} 437 } 438 case TypeXHeader, TypeGNULongName, TypeGNULongLink: 439 return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"} 440 case TypeXGlobalHeader: 441 h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format} 442 if !reflect.DeepEqual(h, h2) { 443 return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"} 444 } 445 whyOnlyPAX = "only PAX supports TypeXGlobalHeader" 446 format.mayOnlyBe(FormatPAX) 447 } 448 if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 { 449 return FormatUnknown, nil, headerError{"negative size on header-only type"} 450 } 451 452 // Check PAX records. 453 if len(h.Xattrs) > 0 { 454 for k, v := range h.Xattrs { 455 paxHdrs[paxSchilyXattr+k] = v 456 } 457 whyOnlyPAX = "only PAX supports Xattrs" 458 format.mayOnlyBe(FormatPAX) 459 } 460 if len(h.PAXRecords) > 0 { 461 for k, v := range h.PAXRecords { 462 switch _, exists := paxHdrs[k]; { 463 case exists: 464 continue // Do not overwrite existing records 465 case h.Typeflag == TypeXGlobalHeader: 466 paxHdrs[k] = v // Copy all records 467 case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse): 468 paxHdrs[k] = v // Ignore local records that may conflict 469 } 470 } 471 whyOnlyPAX = "only PAX supports PAXRecords" 472 format.mayOnlyBe(FormatPAX) 473 } 474 for k, v := range paxHdrs { 475 if !validPAXRecord(k, v) { 476 return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)} 477 } 478 } 479 480 // TODO(dsnet): Re-enable this when adding sparse support. 481 // See https://golang.org/issue/22735 482 /* 483 // Check sparse files. 484 if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse { 485 if isHeaderOnlyType(h.Typeflag) { 486 return FormatUnknown, nil, headerError{"header-only type cannot be sparse"} 487 } 488 if !validateSparseEntries(h.SparseHoles, h.Size) { 489 return FormatUnknown, nil, headerError{"invalid sparse holes"} 490 } 491 if h.Typeflag == TypeGNUSparse { 492 whyOnlyGNU = "only GNU supports TypeGNUSparse" 493 format.mayOnlyBe(FormatGNU) 494 } else { 495 whyNoGNU = "GNU supports sparse files only with TypeGNUSparse" 496 format.mustNotBe(FormatGNU) 497 } 498 whyNoUSTAR = "USTAR does not support sparse files" 499 format.mustNotBe(FormatUSTAR) 500 } 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 func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { 627 if fi == nil { 628 return nil, errors.New("archive/tar: FileInfo is nil") 629 } 630 fm := fi.Mode() 631 h := &Header{ 632 Name: fi.Name(), 633 ModTime: fi.ModTime(), 634 Mode: int64(fm.Perm()), // or'd with c_IS* constants later 635 } 636 switch { 637 case fm.IsRegular(): 638 h.Typeflag = TypeReg 639 h.Size = fi.Size() 640 case fi.IsDir(): 641 h.Typeflag = TypeDir 642 h.Name += "/" 643 case fm&os.ModeSymlink != 0: 644 h.Typeflag = TypeSymlink 645 h.Linkname = link 646 case fm&os.ModeDevice != 0: 647 if fm&os.ModeCharDevice != 0 { 648 h.Typeflag = TypeChar 649 } else { 650 h.Typeflag = TypeBlock 651 } 652 case fm&os.ModeNamedPipe != 0: 653 h.Typeflag = TypeFifo 654 case fm&os.ModeSocket != 0: 655 return nil, fmt.Errorf("archive/tar: sockets not supported") 656 default: 657 return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) 658 } 659 if fm&os.ModeSetuid != 0 { 660 h.Mode |= c_ISUID 661 } 662 if fm&os.ModeSetgid != 0 { 663 h.Mode |= c_ISGID 664 } 665 if fm&os.ModeSticky != 0 { 666 h.Mode |= c_ISVTX 667 } 668 // If possible, populate additional fields from OS-specific 669 // FileInfo fields. 670 if sys, ok := fi.Sys().(*Header); ok { 671 // This FileInfo came from a Header (not the OS). Use the 672 // original Header to populate all remaining fields. 673 h.Uid = sys.Uid 674 h.Gid = sys.Gid 675 h.Uname = sys.Uname 676 h.Gname = sys.Gname 677 h.AccessTime = sys.AccessTime 678 h.ChangeTime = sys.ChangeTime 679 if sys.Xattrs != nil { 680 h.Xattrs = make(map[string]string) 681 for k, v := range sys.Xattrs { 682 h.Xattrs[k] = v 683 } 684 } 685 if sys.Typeflag == TypeLink { 686 // hard link 687 h.Typeflag = TypeLink 688 h.Size = 0 689 h.Linkname = sys.Linkname 690 } 691 if sys.PAXRecords != nil { 692 h.PAXRecords = make(map[string]string) 693 for k, v := range sys.PAXRecords { 694 h.PAXRecords[k] = v 695 } 696 } 697 } 698 if sysStat != nil { 699 return h, sysStat(fi, h) 700 } 701 return h, nil 702 } 703 704 // isHeaderOnlyType checks if the given type flag is of the type that has no 705 // data section even if a size is specified. 706 func isHeaderOnlyType(flag byte) bool { 707 switch flag { 708 case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: 709 return true 710 default: 711 return false 712 } 713 } 714 715 func min(a, b int64) int64 { 716 if a < b { 717 return a 718 } 719 return b 720 }