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