123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 |
- // Copyright 2009 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Package tar implements access to tar archives.
- //
- // Tape archives (tar) are a file format for storing a sequence of files that
- // can be read and written in a streaming manner.
- // This package aims to cover most variations of the format,
- // including those produced by GNU and BSD tar tools.
- package tar
- import (
- "errors"
- "fmt"
- "math"
- "os"
- "path"
- "reflect"
- "strconv"
- "strings"
- "time"
- )
- // BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit
- // architectures. If a large value is encountered when decoding, the result
- // stored in Header will be the truncated version.
- var (
- ErrHeader = errors.New("archive/tar: invalid tar header")
- ErrWriteTooLong = errors.New("archive/tar: write too long")
- ErrFieldTooLong = errors.New("archive/tar: header field too long")
- ErrWriteAfterClose = errors.New("archive/tar: write after close")
- errMissData = errors.New("archive/tar: sparse file references non-existent data")
- errUnrefData = errors.New("archive/tar: sparse file contains unreferenced data")
- errWriteHole = errors.New("archive/tar: write non-NUL byte in sparse hole")
- )
- type headerError []string
- func (he headerError) Error() string {
- const prefix = "archive/tar: cannot encode header"
- var ss []string
- for _, s := range he {
- if s != "" {
- ss = append(ss, s)
- }
- }
- if len(ss) == 0 {
- return prefix
- }
- return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and "))
- }
- // Type flags for Header.Typeflag.
- const (
- // Type '0' indicates a regular file.
- TypeReg = '0'
- TypeRegA = '\x00' // Deprecated: Use TypeReg instead.
- // Type '1' to '6' are header-only flags and may not have a data body.
- TypeLink = '1' // Hard link
- TypeSymlink = '2' // Symbolic link
- TypeChar = '3' // Character device node
- TypeBlock = '4' // Block device node
- TypeDir = '5' // Directory
- TypeFifo = '6' // FIFO node
- // Type '7' is reserved.
- TypeCont = '7'
- // Type 'x' is used by the PAX format to store key-value records that
- // are only relevant to the next file.
- // This package transparently handles these types.
- TypeXHeader = 'x'
- // Type 'g' is used by the PAX format to store key-value records that
- // are relevant to all subsequent files.
- // This package only supports parsing and composing such headers,
- // but does not currently support persisting the global state across files.
- TypeXGlobalHeader = 'g'
- // Type 'S' indicates a sparse file in the GNU format.
- TypeGNUSparse = 'S'
- // Types 'L' and 'K' are used by the GNU format for a meta file
- // used to store the path or link name for the next file.
- // This package transparently handles these types.
- TypeGNULongName = 'L'
- TypeGNULongLink = 'K'
- )
- // Keywords for PAX extended header records.
- const (
- paxNone = "" // Indicates that no PAX key is suitable
- paxPath = "path"
- paxLinkpath = "linkpath"
- paxSize = "size"
- paxUid = "uid"
- paxGid = "gid"
- paxUname = "uname"
- paxGname = "gname"
- paxMtime = "mtime"
- paxAtime = "atime"
- paxCtime = "ctime" // Removed from later revision of PAX spec, but was valid
- paxCharset = "charset" // Currently unused
- paxComment = "comment" // Currently unused
- paxSchilyXattr = "SCHILY.xattr."
- // Keywords for GNU sparse files in a PAX extended header.
- paxGNUSparse = "GNU.sparse."
- paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
- paxGNUSparseOffset = "GNU.sparse.offset"
- paxGNUSparseNumBytes = "GNU.sparse.numbytes"
- paxGNUSparseMap = "GNU.sparse.map"
- paxGNUSparseName = "GNU.sparse.name"
- paxGNUSparseMajor = "GNU.sparse.major"
- paxGNUSparseMinor = "GNU.sparse.minor"
- paxGNUSparseSize = "GNU.sparse.size"
- paxGNUSparseRealSize = "GNU.sparse.realsize"
- )
- // basicKeys is a set of the PAX keys for which we have built-in support.
- // This does not contain "charset" or "comment", which are both PAX-specific,
- // so adding them as first-class features of Header is unlikely.
- // Users can use the PAXRecords field to set it themselves.
- var basicKeys = map[string]bool{
- paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true,
- paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true,
- }
- // A Header represents a single header in a tar archive.
- // Some fields may not be populated.
- //
- // For forward compatibility, users that retrieve a Header from Reader.Next,
- // mutate it in some ways, and then pass it back to Writer.WriteHeader
- // should do so by creating a new Header and copying the fields
- // that they are interested in preserving.
- type Header struct {
- // Typeflag is the type of header entry.
- // The zero value is automatically promoted to either TypeReg or TypeDir
- // depending on the presence of a trailing slash in Name.
- Typeflag byte
- Name string // Name of file entry
- Linkname string // Target name of link (valid for TypeLink or TypeSymlink)
- Size int64 // Logical file size in bytes
- Mode int64 // Permission and mode bits
- Uid int // User ID of owner
- Gid int // Group ID of owner
- Uname string // User name of owner
- Gname string // Group name of owner
- // If the Format is unspecified, then Writer.WriteHeader rounds ModTime
- // to the nearest second and ignores the AccessTime and ChangeTime fields.
- //
- // To use AccessTime or ChangeTime, specify the Format as PAX or GNU.
- // To use sub-second resolution, specify the Format as PAX.
- ModTime time.Time // Modification time
- AccessTime time.Time // Access time (requires either PAX or GNU support)
- ChangeTime time.Time // Change time (requires either PAX or GNU support)
- Devmajor int64 // Major device number (valid for TypeChar or TypeBlock)
- Devminor int64 // Minor device number (valid for TypeChar or TypeBlock)
- // Xattrs stores extended attributes as PAX records under the
- // "SCHILY.xattr." namespace.
- //
- // The following are semantically equivalent:
- // h.Xattrs[key] = value
- // h.PAXRecords["SCHILY.xattr."+key] = value
- //
- // When Writer.WriteHeader is called, the contents of Xattrs will take
- // precedence over those in PAXRecords.
- //
- // Deprecated: Use PAXRecords instead.
- Xattrs map[string]string
- // PAXRecords is a map of PAX extended header records.
- //
- // User-defined records should have keys of the following form:
- // VENDOR.keyword
- // Where VENDOR is some namespace in all uppercase, and keyword may
- // not contain the '=' character (e.g., "GOLANG.pkg.version").
- // The key and value should be non-empty UTF-8 strings.
- //
- // When Writer.WriteHeader is called, PAX records derived from the
- // other fields in Header take precedence over PAXRecords.
- PAXRecords map[string]string
- // Format specifies the format of the tar header.
- //
- // This is set by Reader.Next as a best-effort guess at the format.
- // Since the Reader liberally reads some non-compliant files,
- // it is possible for this to be FormatUnknown.
- //
- // If the format is unspecified when Writer.WriteHeader is called,
- // then it uses the first format (in the order of USTAR, PAX, GNU)
- // capable of encoding this Header (see Format).
- Format Format
- }
- // sparseEntry represents a Length-sized fragment at Offset in the file.
- type sparseEntry struct{ Offset, Length int64 }
- func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length }
- // A sparse file can be represented as either a sparseDatas or a sparseHoles.
- // As long as the total size is known, they are equivalent and one can be
- // converted to the other form and back. The various tar formats with sparse
- // file support represent sparse files in the sparseDatas form. That is, they
- // specify the fragments in the file that has data, and treat everything else as
- // having zero bytes. As such, the encoding and decoding logic in this package
- // deals with sparseDatas.
- //
- // However, the external API uses sparseHoles instead of sparseDatas because the
- // zero value of sparseHoles logically represents a normal file (i.e., there are
- // no holes in it). On the other hand, the zero value of sparseDatas implies
- // that the file has no data in it, which is rather odd.
- //
- // As an example, if the underlying raw file contains the 10-byte data:
- // var compactFile = "abcdefgh"
- //
- // And the sparse map has the following entries:
- // var spd sparseDatas = []sparseEntry{
- // {Offset: 2, Length: 5}, // Data fragment for 2..6
- // {Offset: 18, Length: 3}, // Data fragment for 18..20
- // }
- // var sph sparseHoles = []sparseEntry{
- // {Offset: 0, Length: 2}, // Hole fragment for 0..1
- // {Offset: 7, Length: 11}, // Hole fragment for 7..17
- // {Offset: 21, Length: 4}, // Hole fragment for 21..24
- // }
- //
- // Then the content of the resulting sparse file with a Header.Size of 25 is:
- // var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4
- type (
- sparseDatas []sparseEntry
- sparseHoles []sparseEntry
- )
- // validateSparseEntries reports whether sp is a valid sparse map.
- // It does not matter whether sp represents data fragments or hole fragments.
- func validateSparseEntries(sp []sparseEntry, size int64) bool {
- // Validate all sparse entries. These are the same checks as performed by
- // the BSD tar utility.
- if size < 0 {
- return false
- }
- var pre sparseEntry
- for _, cur := range sp {
- switch {
- case cur.Offset < 0 || cur.Length < 0:
- return false // Negative values are never okay
- case cur.Offset > math.MaxInt64-cur.Length:
- return false // Integer overflow with large length
- case cur.endOffset() > size:
- return false // Region extends beyond the actual size
- case pre.endOffset() > cur.Offset:
- return false // Regions cannot overlap and must be in order
- }
- pre = cur
- }
- return true
- }
- // alignSparseEntries mutates src and returns dst where each fragment's
- // starting offset is aligned up to the nearest block edge, and each
- // ending offset is aligned down to the nearest block edge.
- //
- // Even though the Go tar Reader and the BSD tar utility can handle entries
- // with arbitrary offsets and lengths, the GNU tar utility can only handle
- // offsets and lengths that are multiples of blockSize.
- func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry {
- dst := src[:0]
- for _, s := range src {
- pos, end := s.Offset, s.endOffset()
- pos += blockPadding(+pos) // Round-up to nearest blockSize
- if end != size {
- end -= blockPadding(-end) // Round-down to nearest blockSize
- }
- if pos < end {
- dst = append(dst, sparseEntry{Offset: pos, Length: end - pos})
- }
- }
- return dst
- }
- // invertSparseEntries converts a sparse map from one form to the other.
- // If the input is sparseHoles, then it will output sparseDatas and vice-versa.
- // The input must have been already validated.
- //
- // This function mutates src and returns a normalized map where:
- // * adjacent fragments are coalesced together
- // * only the last fragment may be empty
- // * the endOffset of the last fragment is the total size
- func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry {
- dst := src[:0]
- var pre sparseEntry
- for _, cur := range src {
- if cur.Length == 0 {
- continue // Skip empty fragments
- }
- pre.Length = cur.Offset - pre.Offset
- if pre.Length > 0 {
- dst = append(dst, pre) // Only add non-empty fragments
- }
- pre.Offset = cur.endOffset()
- }
- pre.Length = size - pre.Offset // Possibly the only empty fragment
- return append(dst, pre)
- }
- // fileState tracks the number of logical (includes sparse holes) and physical
- // (actual in tar archive) bytes remaining for the current file.
- //
- // Invariant: LogicalRemaining >= PhysicalRemaining
- type fileState interface {
- LogicalRemaining() int64
- PhysicalRemaining() int64
- }
- // allowedFormats determines which formats can be used.
- // The value returned is the logical OR of multiple possible formats.
- // If the value is FormatUnknown, then the input Header cannot be encoded
- // and an error is returned explaining why.
- //
- // As a by-product of checking the fields, this function returns paxHdrs, which
- // contain all fields that could not be directly encoded.
- // A value receiver ensures that this method does not mutate the source Header.
- func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) {
- format = FormatUSTAR | FormatPAX | FormatGNU
- paxHdrs = make(map[string]string)
- var whyNoUSTAR, whyNoPAX, whyNoGNU string
- var preferPAX bool // Prefer PAX over USTAR
- verifyString := func(s string, size int, name, paxKey string) {
- // NUL-terminator is optional for path and linkpath.
- // Technically, it is required for uname and gname,
- // but neither GNU nor BSD tar checks for it.
- tooLong := len(s) > size
- allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath
- if hasNUL(s) || (tooLong && !allowLongGNU) {
- whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s)
- format.mustNotBe(FormatGNU)
- }
- if !isASCII(s) || tooLong {
- canSplitUSTAR := paxKey == paxPath
- if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok {
- whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s)
- format.mustNotBe(FormatUSTAR)
- }
- if paxKey == paxNone {
- whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s)
- format.mustNotBe(FormatPAX)
- } else {
- paxHdrs[paxKey] = s
- }
- }
- if v, ok := h.PAXRecords[paxKey]; ok && v == s {
- paxHdrs[paxKey] = v
- }
- }
- verifyNumeric := func(n int64, size int, name, paxKey string) {
- if !fitsInBase256(size, n) {
- whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n)
- format.mustNotBe(FormatGNU)
- }
- if !fitsInOctal(size, n) {
- whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n)
- format.mustNotBe(FormatUSTAR)
- if paxKey == paxNone {
- whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n)
- format.mustNotBe(FormatPAX)
- } else {
- paxHdrs[paxKey] = strconv.FormatInt(n, 10)
- }
- }
- if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) {
- paxHdrs[paxKey] = v
- }
- }
- verifyTime := func(ts time.Time, size int, name, paxKey string) {
- if ts.IsZero() {
- return // Always okay
- }
- if !fitsInBase256(size, ts.Unix()) {
- whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts)
- format.mustNotBe(FormatGNU)
- }
- isMtime := paxKey == paxMtime
- fitsOctal := fitsInOctal(size, ts.Unix())
- if (isMtime && !fitsOctal) || !isMtime {
- whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts)
- format.mustNotBe(FormatUSTAR)
- }
- needsNano := ts.Nanosecond() != 0
- if !isMtime || !fitsOctal || needsNano {
- preferPAX = true // USTAR may truncate sub-second measurements
- if paxKey == paxNone {
- whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts)
- format.mustNotBe(FormatPAX)
- } else {
- paxHdrs[paxKey] = formatPAXTime(ts)
- }
- }
- if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) {
- paxHdrs[paxKey] = v
- }
- }
- // Check basic fields.
- var blk block
- v7 := blk.V7()
- ustar := blk.USTAR()
- gnu := blk.GNU()
- verifyString(h.Name, len(v7.Name()), "Name", paxPath)
- verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath)
- verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname)
- verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname)
- verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone)
- verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid)
- verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid)
- verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize)
- verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone)
- verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone)
- verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime)
- verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime)
- verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime)
- // Check for header-only types.
- var whyOnlyPAX, whyOnlyGNU string
- switch h.Typeflag {
- case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse:
- // Exclude TypeLink and TypeSymlink, since they may reference directories.
- if strings.HasSuffix(h.Name, "/") {
- return FormatUnknown, nil, headerError{"filename may not have trailing slash"}
- }
- case TypeXHeader, TypeGNULongName, TypeGNULongLink:
- return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"}
- case TypeXGlobalHeader:
- h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format}
- if !reflect.DeepEqual(h, h2) {
- return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"}
- }
- whyOnlyPAX = "only PAX supports TypeXGlobalHeader"
- format.mayOnlyBe(FormatPAX)
- }
- if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 {
- return FormatUnknown, nil, headerError{"negative size on header-only type"}
- }
- // Check PAX records.
- if len(h.Xattrs) > 0 {
- for k, v := range h.Xattrs {
- paxHdrs[paxSchilyXattr+k] = v
- }
- whyOnlyPAX = "only PAX supports Xattrs"
- format.mayOnlyBe(FormatPAX)
- }
- if len(h.PAXRecords) > 0 {
- for k, v := range h.PAXRecords {
- switch _, exists := paxHdrs[k]; {
- case exists:
- continue // Do not overwrite existing records
- case h.Typeflag == TypeXGlobalHeader:
- paxHdrs[k] = v // Copy all records
- case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse):
- paxHdrs[k] = v // Ignore local records that may conflict
- }
- }
- whyOnlyPAX = "only PAX supports PAXRecords"
- format.mayOnlyBe(FormatPAX)
- }
- for k, v := range paxHdrs {
- if !validPAXRecord(k, v) {
- return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)}
- }
- }
- // TODO(dsnet): Re-enable this when adding sparse support.
- // See https://golang.org/issue/22735
- /*
- // Check sparse files.
- if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse {
- if isHeaderOnlyType(h.Typeflag) {
- return FormatUnknown, nil, headerError{"header-only type cannot be sparse"}
- }
- if !validateSparseEntries(h.SparseHoles, h.Size) {
- return FormatUnknown, nil, headerError{"invalid sparse holes"}
- }
- if h.Typeflag == TypeGNUSparse {
- whyOnlyGNU = "only GNU supports TypeGNUSparse"
- format.mayOnlyBe(FormatGNU)
- } else {
- whyNoGNU = "GNU supports sparse files only with TypeGNUSparse"
- format.mustNotBe(FormatGNU)
- }
- whyNoUSTAR = "USTAR does not support sparse files"
- format.mustNotBe(FormatUSTAR)
- }
- */
- // Check desired format.
- if wantFormat := h.Format; wantFormat != FormatUnknown {
- if wantFormat.has(FormatPAX) && !preferPAX {
- wantFormat.mayBe(FormatUSTAR) // PAX implies USTAR allowed too
- }
- format.mayOnlyBe(wantFormat) // Set union of formats allowed and format wanted
- }
- if format == FormatUnknown {
- switch h.Format {
- case FormatUSTAR:
- err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU}
- case FormatPAX:
- err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU}
- case FormatGNU:
- err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX}
- default:
- err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU}
- }
- }
- return format, paxHdrs, err
- }
- // FileInfo returns an os.FileInfo for the Header.
- func (h *Header) FileInfo() os.FileInfo {
- return headerFileInfo{h}
- }
- // headerFileInfo implements os.FileInfo.
- type headerFileInfo struct {
- h *Header
- }
- func (fi headerFileInfo) Size() int64 { return fi.h.Size }
- func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
- func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
- func (fi headerFileInfo) Sys() interface{} { return fi.h }
- // Name returns the base name of the file.
- func (fi headerFileInfo) Name() string {
- if fi.IsDir() {
- return path.Base(path.Clean(fi.h.Name))
- }
- return path.Base(fi.h.Name)
- }
- // Mode returns the permission and mode bits for the headerFileInfo.
- func (fi headerFileInfo) Mode() (mode os.FileMode) {
- // Set file permission bits.
- mode = os.FileMode(fi.h.Mode).Perm()
- // Set setuid, setgid and sticky bits.
- if fi.h.Mode&c_ISUID != 0 {
- mode |= os.ModeSetuid
- }
- if fi.h.Mode&c_ISGID != 0 {
- mode |= os.ModeSetgid
- }
- if fi.h.Mode&c_ISVTX != 0 {
- mode |= os.ModeSticky
- }
- // Set file mode bits; clear perm, setuid, setgid, and sticky bits.
- switch m := os.FileMode(fi.h.Mode) &^ 07777; m {
- case c_ISDIR:
- mode |= os.ModeDir
- case c_ISFIFO:
- mode |= os.ModeNamedPipe
- case c_ISLNK:
- mode |= os.ModeSymlink
- case c_ISBLK:
- mode |= os.ModeDevice
- case c_ISCHR:
- mode |= os.ModeDevice
- mode |= os.ModeCharDevice
- case c_ISSOCK:
- mode |= os.ModeSocket
- }
- switch fi.h.Typeflag {
- case TypeSymlink:
- mode |= os.ModeSymlink
- case TypeChar:
- mode |= os.ModeDevice
- mode |= os.ModeCharDevice
- case TypeBlock:
- mode |= os.ModeDevice
- case TypeDir:
- mode |= os.ModeDir
- case TypeFifo:
- mode |= os.ModeNamedPipe
- }
- return mode
- }
- // sysStat, if non-nil, populates h from system-dependent fields of fi.
- var sysStat func(fi os.FileInfo, h *Header) error
- const (
- // Mode constants from the USTAR spec:
- // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
- c_ISUID = 04000 // Set uid
- c_ISGID = 02000 // Set gid
- c_ISVTX = 01000 // Save text (sticky bit)
- // Common Unix mode constants; these are not defined in any common tar standard.
- // Header.FileInfo understands these, but FileInfoHeader will never produce these.
- c_ISDIR = 040000 // Directory
- c_ISFIFO = 010000 // FIFO
- c_ISREG = 0100000 // Regular file
- c_ISLNK = 0120000 // Symbolic link
- c_ISBLK = 060000 // Block special file
- c_ISCHR = 020000 // Character special file
- c_ISSOCK = 0140000 // Socket
- )
- // FileInfoHeader creates a partially-populated Header from fi.
- // If fi describes a symlink, FileInfoHeader records link as the link target.
- // If fi describes a directory, a slash is appended to the name.
- //
- // Since os.FileInfo's Name method only returns the base name of
- // the file it describes, it may be necessary to modify Header.Name
- // to provide the full path name of the file.
- func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
- if fi == nil {
- return nil, errors.New("archive/tar: FileInfo is nil")
- }
- fm := fi.Mode()
- h := &Header{
- Name: fi.Name(),
- ModTime: fi.ModTime(),
- Mode: int64(fm.Perm()), // or'd with c_IS* constants later
- }
- switch {
- case fm.IsRegular():
- h.Typeflag = TypeReg
- h.Size = fi.Size()
- case fi.IsDir():
- h.Typeflag = TypeDir
- h.Name += "/"
- case fm&os.ModeSymlink != 0:
- h.Typeflag = TypeSymlink
- h.Linkname = link
- case fm&os.ModeDevice != 0:
- if fm&os.ModeCharDevice != 0 {
- h.Typeflag = TypeChar
- } else {
- h.Typeflag = TypeBlock
- }
- case fm&os.ModeNamedPipe != 0:
- h.Typeflag = TypeFifo
- case fm&os.ModeSocket != 0:
- return nil, fmt.Errorf("archive/tar: sockets not supported")
- default:
- return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
- }
- if fm&os.ModeSetuid != 0 {
- h.Mode |= c_ISUID
- }
- if fm&os.ModeSetgid != 0 {
- h.Mode |= c_ISGID
- }
- if fm&os.ModeSticky != 0 {
- h.Mode |= c_ISVTX
- }
- // If possible, populate additional fields from OS-specific
- // FileInfo fields.
- if sys, ok := fi.Sys().(*Header); ok {
- // This FileInfo came from a Header (not the OS). Use the
- // original Header to populate all remaining fields.
- h.Uid = sys.Uid
- h.Gid = sys.Gid
- h.Uname = sys.Uname
- h.Gname = sys.Gname
- h.AccessTime = sys.AccessTime
- h.ChangeTime = sys.ChangeTime
- if sys.Xattrs != nil {
- h.Xattrs = make(map[string]string)
- for k, v := range sys.Xattrs {
- h.Xattrs[k] = v
- }
- }
- if sys.Typeflag == TypeLink {
- // hard link
- h.Typeflag = TypeLink
- h.Size = 0
- h.Linkname = sys.Linkname
- }
- if sys.PAXRecords != nil {
- h.PAXRecords = make(map[string]string)
- for k, v := range sys.PAXRecords {
- h.PAXRecords[k] = v
- }
- }
- }
- if sysStat != nil {
- return h, sysStat(fi, h)
- }
- return h, nil
- }
- // isHeaderOnlyType checks if the given type flag is of the type that has no
- // data section even if a size is specified.
- func isHeaderOnlyType(flag byte) bool {
- switch flag {
- case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
- return true
- default:
- return false
- }
- }
- func min(a, b int64) int64 {
- if a < b {
- return a
- }
- return b
- }
|