archive.go 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454
  1. package archive // import "github.com/docker/docker/pkg/archive"
  2. import (
  3. "archive/tar"
  4. "bufio"
  5. "bytes"
  6. "compress/bzip2"
  7. "compress/gzip"
  8. "context"
  9. "encoding/binary"
  10. "fmt"
  11. "io"
  12. "os"
  13. "path/filepath"
  14. "runtime"
  15. "strconv"
  16. "strings"
  17. "syscall"
  18. "time"
  19. "github.com/containerd/containerd/pkg/userns"
  20. "github.com/docker/docker/pkg/fileutils"
  21. "github.com/docker/docker/pkg/idtools"
  22. "github.com/docker/docker/pkg/ioutils"
  23. "github.com/docker/docker/pkg/pools"
  24. "github.com/docker/docker/pkg/system"
  25. "github.com/klauspost/compress/zstd"
  26. "github.com/pkg/errors"
  27. "github.com/sirupsen/logrus"
  28. exec "golang.org/x/sys/execabs"
  29. )
  30. type (
  31. // Compression is the state represents if compressed or not.
  32. Compression int
  33. // WhiteoutFormat is the format of whiteouts unpacked
  34. WhiteoutFormat int
  35. // TarOptions wraps the tar options.
  36. TarOptions struct {
  37. IncludeFiles []string
  38. ExcludePatterns []string
  39. Compression Compression
  40. NoLchown bool
  41. IDMap idtools.IdentityMapping
  42. ChownOpts *idtools.Identity
  43. IncludeSourceDir bool
  44. // WhiteoutFormat is the expected on disk format for whiteout files.
  45. // This format will be converted to the standard format on pack
  46. // and from the standard format on unpack.
  47. WhiteoutFormat WhiteoutFormat
  48. // When unpacking, specifies whether overwriting a directory with a
  49. // non-directory is allowed and vice versa.
  50. NoOverwriteDirNonDir bool
  51. // For each include when creating an archive, the included name will be
  52. // replaced with the matching name from this map.
  53. RebaseNames map[string]string
  54. InUserNS bool
  55. }
  56. )
  57. // Archiver implements the Archiver interface and allows the reuse of most utility functions of
  58. // this package with a pluggable Untar function. Also, to facilitate the passing of specific id
  59. // mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations.
  60. type Archiver struct {
  61. Untar func(io.Reader, string, *TarOptions) error
  62. IDMapping idtools.IdentityMapping
  63. }
  64. // NewDefaultArchiver returns a new Archiver without any IdentityMapping
  65. func NewDefaultArchiver() *Archiver {
  66. return &Archiver{Untar: Untar}
  67. }
  68. // breakoutError is used to differentiate errors related to breaking out
  69. // When testing archive breakout in the unit tests, this error is expected
  70. // in order for the test to pass.
  71. type breakoutError error
  72. const (
  73. // Uncompressed represents the uncompressed.
  74. Uncompressed Compression = iota
  75. // Bzip2 is bzip2 compression algorithm.
  76. Bzip2
  77. // Gzip is gzip compression algorithm.
  78. Gzip
  79. // Xz is xz compression algorithm.
  80. Xz
  81. // Zstd is zstd compression algorithm.
  82. Zstd
  83. )
  84. const (
  85. // AUFSWhiteoutFormat is the default format for whiteouts
  86. AUFSWhiteoutFormat WhiteoutFormat = iota
  87. // OverlayWhiteoutFormat formats whiteout according to the overlay
  88. // standard.
  89. OverlayWhiteoutFormat
  90. )
  91. const (
  92. modeISDIR = 040000 // Directory
  93. modeISFIFO = 010000 // FIFO
  94. modeISREG = 0100000 // Regular file
  95. modeISLNK = 0120000 // Symbolic link
  96. modeISBLK = 060000 // Block special file
  97. modeISCHR = 020000 // Character special file
  98. modeISSOCK = 0140000 // Socket
  99. )
  100. // IsArchivePath checks if the (possibly compressed) file at the given path
  101. // starts with a tar file header.
  102. func IsArchivePath(path string) bool {
  103. file, err := os.Open(path)
  104. if err != nil {
  105. return false
  106. }
  107. defer file.Close()
  108. rdr, err := DecompressStream(file)
  109. if err != nil {
  110. return false
  111. }
  112. defer rdr.Close()
  113. r := tar.NewReader(rdr)
  114. _, err = r.Next()
  115. return err == nil
  116. }
  117. const (
  118. zstdMagicSkippableStart = 0x184D2A50
  119. zstdMagicSkippableMask = 0xFFFFFFF0
  120. )
  121. var (
  122. bzip2Magic = []byte{0x42, 0x5A, 0x68}
  123. gzipMagic = []byte{0x1F, 0x8B, 0x08}
  124. xzMagic = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
  125. zstdMagic = []byte{0x28, 0xb5, 0x2f, 0xfd}
  126. )
  127. type matcher = func([]byte) bool
  128. func magicNumberMatcher(m []byte) matcher {
  129. return func(source []byte) bool {
  130. return bytes.HasPrefix(source, m)
  131. }
  132. }
  133. // zstdMatcher detects zstd compression algorithm.
  134. // Zstandard compressed data is made of one or more frames.
  135. // There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames.
  136. // See https://tools.ietf.org/id/draft-kucherawy-dispatch-zstd-00.html#rfc.section.2 for more details.
  137. func zstdMatcher() matcher {
  138. return func(source []byte) bool {
  139. if bytes.HasPrefix(source, zstdMagic) {
  140. // Zstandard frame
  141. return true
  142. }
  143. // skippable frame
  144. if len(source) < 8 {
  145. return false
  146. }
  147. // magic number from 0x184D2A50 to 0x184D2A5F.
  148. if binary.LittleEndian.Uint32(source[:4])&zstdMagicSkippableMask == zstdMagicSkippableStart {
  149. return true
  150. }
  151. return false
  152. }
  153. }
  154. // DetectCompression detects the compression algorithm of the source.
  155. func DetectCompression(source []byte) Compression {
  156. compressionMap := map[Compression]matcher{
  157. Bzip2: magicNumberMatcher(bzip2Magic),
  158. Gzip: magicNumberMatcher(gzipMagic),
  159. Xz: magicNumberMatcher(xzMagic),
  160. Zstd: zstdMatcher(),
  161. }
  162. for _, compression := range []Compression{Bzip2, Gzip, Xz, Zstd} {
  163. fn := compressionMap[compression]
  164. if fn(source) {
  165. return compression
  166. }
  167. }
  168. return Uncompressed
  169. }
  170. func xzDecompress(ctx context.Context, archive io.Reader) (io.ReadCloser, error) {
  171. args := []string{"xz", "-d", "-c", "-q"}
  172. return cmdStream(exec.CommandContext(ctx, args[0], args[1:]...), archive)
  173. }
  174. func gzDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) {
  175. if noPigzEnv := os.Getenv("MOBY_DISABLE_PIGZ"); noPigzEnv != "" {
  176. noPigz, err := strconv.ParseBool(noPigzEnv)
  177. if err != nil {
  178. logrus.WithError(err).Warn("invalid value in MOBY_DISABLE_PIGZ env var")
  179. }
  180. if noPigz {
  181. logrus.Debugf("Use of pigz is disabled due to MOBY_DISABLE_PIGZ=%s", noPigzEnv)
  182. return gzip.NewReader(buf)
  183. }
  184. }
  185. unpigzPath, err := exec.LookPath("unpigz")
  186. if err != nil {
  187. logrus.Debugf("unpigz binary not found, falling back to go gzip library")
  188. return gzip.NewReader(buf)
  189. }
  190. logrus.Debugf("Using %s to decompress", unpigzPath)
  191. return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf)
  192. }
  193. func wrapReadCloser(readBuf io.ReadCloser, cancel context.CancelFunc) io.ReadCloser {
  194. return ioutils.NewReadCloserWrapper(readBuf, func() error {
  195. cancel()
  196. return readBuf.Close()
  197. })
  198. }
  199. // DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
  200. func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
  201. p := pools.BufioReader32KPool
  202. buf := p.Get(archive)
  203. bs, err := buf.Peek(10)
  204. if err != nil && err != io.EOF {
  205. // Note: we'll ignore any io.EOF error because there are some odd
  206. // cases where the layer.tar file will be empty (zero bytes) and
  207. // that results in an io.EOF from the Peek() call. So, in those
  208. // cases we'll just treat it as a non-compressed stream and
  209. // that means just create an empty layer.
  210. // See Issue 18170
  211. return nil, err
  212. }
  213. compression := DetectCompression(bs)
  214. switch compression {
  215. case Uncompressed:
  216. readBufWrapper := p.NewReadCloserWrapper(buf, buf)
  217. return readBufWrapper, nil
  218. case Gzip:
  219. ctx, cancel := context.WithCancel(context.Background())
  220. gzReader, err := gzDecompress(ctx, buf)
  221. if err != nil {
  222. cancel()
  223. return nil, err
  224. }
  225. readBufWrapper := p.NewReadCloserWrapper(buf, gzReader)
  226. return wrapReadCloser(readBufWrapper, cancel), nil
  227. case Bzip2:
  228. bz2Reader := bzip2.NewReader(buf)
  229. readBufWrapper := p.NewReadCloserWrapper(buf, bz2Reader)
  230. return readBufWrapper, nil
  231. case Xz:
  232. ctx, cancel := context.WithCancel(context.Background())
  233. xzReader, err := xzDecompress(ctx, buf)
  234. if err != nil {
  235. cancel()
  236. return nil, err
  237. }
  238. readBufWrapper := p.NewReadCloserWrapper(buf, xzReader)
  239. return wrapReadCloser(readBufWrapper, cancel), nil
  240. case Zstd:
  241. zstdReader, err := zstd.NewReader(buf)
  242. if err != nil {
  243. return nil, err
  244. }
  245. readBufWrapper := p.NewReadCloserWrapper(buf, zstdReader)
  246. return readBufWrapper, nil
  247. default:
  248. return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
  249. }
  250. }
  251. // CompressStream compresses the dest with specified compression algorithm.
  252. func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) {
  253. p := pools.BufioWriter32KPool
  254. buf := p.Get(dest)
  255. switch compression {
  256. case Uncompressed:
  257. writeBufWrapper := p.NewWriteCloserWrapper(buf, buf)
  258. return writeBufWrapper, nil
  259. case Gzip:
  260. gzWriter := gzip.NewWriter(dest)
  261. writeBufWrapper := p.NewWriteCloserWrapper(buf, gzWriter)
  262. return writeBufWrapper, nil
  263. case Bzip2, Xz:
  264. // archive/bzip2 does not support writing, and there is no xz support at all
  265. // However, this is not a problem as docker only currently generates gzipped tars
  266. return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
  267. default:
  268. return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
  269. }
  270. }
  271. // TarModifierFunc is a function that can be passed to ReplaceFileTarWrapper to
  272. // modify the contents or header of an entry in the archive. If the file already
  273. // exists in the archive the TarModifierFunc will be called with the Header and
  274. // a reader which will return the files content. If the file does not exist both
  275. // header and content will be nil.
  276. type TarModifierFunc func(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error)
  277. // ReplaceFileTarWrapper converts inputTarStream to a new tar stream. Files in the
  278. // tar stream are modified if they match any of the keys in mods.
  279. func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModifierFunc) io.ReadCloser {
  280. pipeReader, pipeWriter := io.Pipe()
  281. go func() {
  282. tarReader := tar.NewReader(inputTarStream)
  283. tarWriter := tar.NewWriter(pipeWriter)
  284. defer inputTarStream.Close()
  285. defer tarWriter.Close()
  286. modify := func(name string, original *tar.Header, modifier TarModifierFunc, tarReader io.Reader) error {
  287. header, data, err := modifier(name, original, tarReader)
  288. switch {
  289. case err != nil:
  290. return err
  291. case header == nil:
  292. return nil
  293. }
  294. if header.Name == "" {
  295. header.Name = name
  296. }
  297. header.Size = int64(len(data))
  298. if err := tarWriter.WriteHeader(header); err != nil {
  299. return err
  300. }
  301. if len(data) != 0 {
  302. if _, err := tarWriter.Write(data); err != nil {
  303. return err
  304. }
  305. }
  306. return nil
  307. }
  308. var err error
  309. var originalHeader *tar.Header
  310. for {
  311. originalHeader, err = tarReader.Next()
  312. if err == io.EOF {
  313. break
  314. }
  315. if err != nil {
  316. pipeWriter.CloseWithError(err)
  317. return
  318. }
  319. modifier, ok := mods[originalHeader.Name]
  320. if !ok {
  321. // No modifiers for this file, copy the header and data
  322. if err := tarWriter.WriteHeader(originalHeader); err != nil {
  323. pipeWriter.CloseWithError(err)
  324. return
  325. }
  326. if _, err := pools.Copy(tarWriter, tarReader); err != nil {
  327. pipeWriter.CloseWithError(err)
  328. return
  329. }
  330. continue
  331. }
  332. delete(mods, originalHeader.Name)
  333. if err := modify(originalHeader.Name, originalHeader, modifier, tarReader); err != nil {
  334. pipeWriter.CloseWithError(err)
  335. return
  336. }
  337. }
  338. // Apply the modifiers that haven't matched any files in the archive
  339. for name, modifier := range mods {
  340. if err := modify(name, nil, modifier, nil); err != nil {
  341. pipeWriter.CloseWithError(err)
  342. return
  343. }
  344. }
  345. pipeWriter.Close()
  346. }()
  347. return pipeReader
  348. }
  349. // Extension returns the extension of a file that uses the specified compression algorithm.
  350. func (compression *Compression) Extension() string {
  351. switch *compression {
  352. case Uncompressed:
  353. return "tar"
  354. case Bzip2:
  355. return "tar.bz2"
  356. case Gzip:
  357. return "tar.gz"
  358. case Xz:
  359. return "tar.xz"
  360. case Zstd:
  361. return "tar.zst"
  362. }
  363. return ""
  364. }
  365. // nosysFileInfo hides the system-dependent info of the wrapped FileInfo to
  366. // prevent tar.FileInfoHeader from introspecting it and potentially calling into
  367. // glibc.
  368. type nosysFileInfo struct {
  369. os.FileInfo
  370. }
  371. func (fi nosysFileInfo) Sys() interface{} {
  372. // A Sys value of type *tar.Header is safe as it is system-independent.
  373. // The tar.FileInfoHeader function copies the fields into the returned
  374. // header without performing any OS lookups.
  375. if sys, ok := fi.FileInfo.Sys().(*tar.Header); ok {
  376. return sys
  377. }
  378. return nil
  379. }
  380. // sysStat, if non-nil, populates hdr from system-dependent fields of fi.
  381. var sysStat func(fi os.FileInfo, hdr *tar.Header) error
  382. // FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi.
  383. //
  384. // Compared to the archive/tar.FileInfoHeader function, this function is safe to
  385. // call from a chrooted process as it does not populate fields which would
  386. // require operating system lookups. It behaves identically to
  387. // tar.FileInfoHeader when fi is a FileInfo value returned from
  388. // tar.Header.FileInfo().
  389. //
  390. // When fi is a FileInfo for a native file, such as returned from os.Stat() and
  391. // os.Lstat(), the returned Header value differs from one returned from
  392. // tar.FileInfoHeader in the following ways. The Uname and Gname fields are not
  393. // set as OS lookups would be required to populate them. The AccessTime and
  394. // ChangeTime fields are not currently set (not yet implemented) although that
  395. // is subject to change. Callers which require the AccessTime or ChangeTime
  396. // fields to be zeroed should explicitly zero them out in the returned Header
  397. // value to avoid any compatibility issues in the future.
  398. func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
  399. hdr, err := tar.FileInfoHeader(nosysFileInfo{fi}, link)
  400. if err != nil {
  401. return nil, err
  402. }
  403. if sysStat != nil {
  404. return hdr, sysStat(fi, hdr)
  405. }
  406. return hdr, nil
  407. }
  408. // FileInfoHeader creates a populated Header from fi.
  409. //
  410. // Compared to the archive/tar package, this function fills in less information
  411. // but is safe to call from a chrooted process. The AccessTime and ChangeTime
  412. // fields are not set in the returned header, ModTime is truncated to one-second
  413. // precision, and the Uname and Gname fields are only set when fi is a FileInfo
  414. // value returned from tar.Header.FileInfo(). Also, regardless of Go version,
  415. // this function fills file type bits (e.g. hdr.Mode |= modeISDIR), which have
  416. // been deleted since Go 1.9 archive/tar.
  417. func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) {
  418. hdr, err := FileInfoHeaderNoLookups(fi, link)
  419. if err != nil {
  420. return nil, err
  421. }
  422. hdr.Format = tar.FormatPAX
  423. hdr.ModTime = hdr.ModTime.Truncate(time.Second)
  424. hdr.AccessTime = time.Time{}
  425. hdr.ChangeTime = time.Time{}
  426. hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), fi)
  427. hdr.Name = canonicalTarName(name, fi.IsDir())
  428. return hdr, nil
  429. }
  430. // fillGo18FileTypeBits fills type bits which have been removed on Go 1.9 archive/tar
  431. // https://github.com/golang/go/commit/66b5a2f
  432. func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 {
  433. fm := fi.Mode()
  434. switch {
  435. case fm.IsRegular():
  436. mode |= modeISREG
  437. case fi.IsDir():
  438. mode |= modeISDIR
  439. case fm&os.ModeSymlink != 0:
  440. mode |= modeISLNK
  441. case fm&os.ModeDevice != 0:
  442. if fm&os.ModeCharDevice != 0 {
  443. mode |= modeISCHR
  444. } else {
  445. mode |= modeISBLK
  446. }
  447. case fm&os.ModeNamedPipe != 0:
  448. mode |= modeISFIFO
  449. case fm&os.ModeSocket != 0:
  450. mode |= modeISSOCK
  451. }
  452. return mode
  453. }
  454. // ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
  455. // to a tar header
  456. func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
  457. const (
  458. // Values based on linux/include/uapi/linux/capability.h
  459. xattrCapsSz2 = 20
  460. versionOffset = 3
  461. vfsCapRevision2 = 2
  462. vfsCapRevision3 = 3
  463. )
  464. capability, _ := system.Lgetxattr(path, "security.capability")
  465. if capability != nil {
  466. length := len(capability)
  467. if capability[versionOffset] == vfsCapRevision3 {
  468. // Convert VFS_CAP_REVISION_3 to VFS_CAP_REVISION_2 as root UID makes no
  469. // sense outside the user namespace the archive is built in.
  470. capability[versionOffset] = vfsCapRevision2
  471. length = xattrCapsSz2
  472. }
  473. hdr.Xattrs = make(map[string]string)
  474. hdr.Xattrs["security.capability"] = string(capability[:length])
  475. }
  476. return nil
  477. }
  478. type tarWhiteoutConverter interface {
  479. ConvertWrite(*tar.Header, string, os.FileInfo) (*tar.Header, error)
  480. ConvertRead(*tar.Header, string) (bool, error)
  481. }
  482. type tarAppender struct {
  483. TarWriter *tar.Writer
  484. Buffer *bufio.Writer
  485. // for hardlink mapping
  486. SeenFiles map[uint64]string
  487. IdentityMapping idtools.IdentityMapping
  488. ChownOpts *idtools.Identity
  489. // For packing and unpacking whiteout files in the
  490. // non standard format. The whiteout files defined
  491. // by the AUFS standard are used as the tar whiteout
  492. // standard.
  493. WhiteoutConverter tarWhiteoutConverter
  494. }
  495. func newTarAppender(idMapping idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender {
  496. return &tarAppender{
  497. SeenFiles: make(map[uint64]string),
  498. TarWriter: tar.NewWriter(writer),
  499. Buffer: pools.BufioWriter32KPool.Get(nil),
  500. IdentityMapping: idMapping,
  501. ChownOpts: chownOpts,
  502. }
  503. }
  504. // CanonicalTarNameForPath canonicalizes relativePath to a POSIX-style path using
  505. // forward slashes. It is an alias for filepath.ToSlash, which is a no-op on
  506. // Linux and Unix.
  507. func CanonicalTarNameForPath(relativePath string) string {
  508. return filepath.ToSlash(relativePath)
  509. }
  510. // canonicalTarName provides a platform-independent and consistent POSIX-style
  511. // path for files and directories to be archived regardless of the platform.
  512. func canonicalTarName(name string, isDir bool) string {
  513. name = filepath.ToSlash(name)
  514. // suffix with '/' for directories
  515. if isDir && !strings.HasSuffix(name, "/") {
  516. name += "/"
  517. }
  518. return name
  519. }
  520. // addTarFile adds to the tar archive a file from `path` as `name`
  521. func (ta *tarAppender) addTarFile(path, name string) error {
  522. fi, err := os.Lstat(path)
  523. if err != nil {
  524. return err
  525. }
  526. var link string
  527. if fi.Mode()&os.ModeSymlink != 0 {
  528. var err error
  529. link, err = os.Readlink(path)
  530. if err != nil {
  531. return err
  532. }
  533. }
  534. hdr, err := FileInfoHeader(name, fi, link)
  535. if err != nil {
  536. return err
  537. }
  538. if err := ReadSecurityXattrToTarHeader(path, hdr); err != nil {
  539. return err
  540. }
  541. // if it's not a directory and has more than 1 link,
  542. // it's hard linked, so set the type flag accordingly
  543. if !fi.IsDir() && hasHardlinks(fi) {
  544. inode, err := getInodeFromStat(fi.Sys())
  545. if err != nil {
  546. return err
  547. }
  548. // a link should have a name that it links too
  549. // and that linked name should be first in the tar archive
  550. if oldpath, ok := ta.SeenFiles[inode]; ok {
  551. hdr.Typeflag = tar.TypeLink
  552. hdr.Linkname = oldpath
  553. hdr.Size = 0 // This Must be here for the writer math to add up!
  554. } else {
  555. ta.SeenFiles[inode] = name
  556. }
  557. }
  558. // check whether the file is overlayfs whiteout
  559. // if yes, skip re-mapping container ID mappings.
  560. isOverlayWhiteout := fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0
  561. // handle re-mapping container ID mappings back to host ID mappings before
  562. // writing tar headers/files. We skip whiteout files because they were written
  563. // by the kernel and already have proper ownership relative to the host
  564. if !isOverlayWhiteout && !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IdentityMapping.Empty() {
  565. fileIDPair, err := getFileUIDGID(fi.Sys())
  566. if err != nil {
  567. return err
  568. }
  569. hdr.Uid, hdr.Gid, err = ta.IdentityMapping.ToContainer(fileIDPair)
  570. if err != nil {
  571. return err
  572. }
  573. }
  574. // explicitly override with ChownOpts
  575. if ta.ChownOpts != nil {
  576. hdr.Uid = ta.ChownOpts.UID
  577. hdr.Gid = ta.ChownOpts.GID
  578. }
  579. if ta.WhiteoutConverter != nil {
  580. wo, err := ta.WhiteoutConverter.ConvertWrite(hdr, path, fi)
  581. if err != nil {
  582. return err
  583. }
  584. // If a new whiteout file exists, write original hdr, then
  585. // replace hdr with wo to be written after. Whiteouts should
  586. // always be written after the original. Note the original
  587. // hdr may have been updated to be a whiteout with returning
  588. // a whiteout header
  589. if wo != nil {
  590. if err := ta.TarWriter.WriteHeader(hdr); err != nil {
  591. return err
  592. }
  593. if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
  594. return fmt.Errorf("tar: cannot use whiteout for non-empty file")
  595. }
  596. hdr = wo
  597. }
  598. }
  599. if err := ta.TarWriter.WriteHeader(hdr); err != nil {
  600. return err
  601. }
  602. if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
  603. // We use system.OpenSequential to ensure we use sequential file
  604. // access on Windows to avoid depleting the standby list.
  605. // On Linux, this equates to a regular os.Open.
  606. file, err := system.OpenSequential(path)
  607. if err != nil {
  608. return err
  609. }
  610. ta.Buffer.Reset(ta.TarWriter)
  611. defer ta.Buffer.Reset(nil)
  612. _, err = io.Copy(ta.Buffer, file)
  613. file.Close()
  614. if err != nil {
  615. return err
  616. }
  617. err = ta.Buffer.Flush()
  618. if err != nil {
  619. return err
  620. }
  621. }
  622. return nil
  623. }
  624. func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.Identity, inUserns bool) error {
  625. // hdr.Mode is in linux format, which we can use for sycalls,
  626. // but for os.Foo() calls we need the mode converted to os.FileMode,
  627. // so use hdrInfo.Mode() (they differ for e.g. setuid bits)
  628. hdrInfo := hdr.FileInfo()
  629. switch hdr.Typeflag {
  630. case tar.TypeDir:
  631. // Create directory unless it exists as a directory already.
  632. // In that case we just want to merge the two
  633. if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
  634. if err := os.Mkdir(path, hdrInfo.Mode()); err != nil {
  635. return err
  636. }
  637. }
  638. case tar.TypeReg, tar.TypeRegA:
  639. // Source is regular file. We use system.OpenFileSequential to use sequential
  640. // file access to avoid depleting the standby list on Windows.
  641. // On Linux, this equates to a regular os.OpenFile
  642. file, err := system.OpenFileSequential(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
  643. if err != nil {
  644. return err
  645. }
  646. if _, err := io.Copy(file, reader); err != nil {
  647. file.Close()
  648. return err
  649. }
  650. file.Close()
  651. case tar.TypeBlock, tar.TypeChar:
  652. if inUserns { // cannot create devices in a userns
  653. return nil
  654. }
  655. // Handle this is an OS-specific way
  656. if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
  657. return err
  658. }
  659. case tar.TypeFifo:
  660. // Handle this is an OS-specific way
  661. if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
  662. return err
  663. }
  664. case tar.TypeLink:
  665. // #nosec G305 -- The target path is checked for path traversal.
  666. targetPath := filepath.Join(extractDir, hdr.Linkname)
  667. // check for hardlink breakout
  668. if !strings.HasPrefix(targetPath, extractDir) {
  669. return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname))
  670. }
  671. if err := os.Link(targetPath, path); err != nil {
  672. return err
  673. }
  674. case tar.TypeSymlink:
  675. // path -> hdr.Linkname = targetPath
  676. // e.g. /extractDir/path/to/symlink -> ../2/file = /extractDir/path/2/file
  677. targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname) // #nosec G305 -- The target path is checked for path traversal.
  678. // the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because
  679. // that symlink would first have to be created, which would be caught earlier, at this very check:
  680. if !strings.HasPrefix(targetPath, extractDir) {
  681. return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname))
  682. }
  683. if err := os.Symlink(hdr.Linkname, path); err != nil {
  684. return err
  685. }
  686. case tar.TypeXGlobalHeader:
  687. logrus.Debug("PAX Global Extended Headers found and ignored")
  688. return nil
  689. default:
  690. return fmt.Errorf("unhandled tar header type %d", hdr.Typeflag)
  691. }
  692. // Lchown is not supported on Windows.
  693. if Lchown && runtime.GOOS != "windows" {
  694. if chownOpts == nil {
  695. chownOpts = &idtools.Identity{UID: hdr.Uid, GID: hdr.Gid}
  696. }
  697. if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
  698. msg := "failed to Lchown %q for UID %d, GID %d"
  699. if errors.Is(err, syscall.EINVAL) && userns.RunningInUserNS() {
  700. msg += " (try increasing the number of subordinate IDs in /etc/subuid and /etc/subgid)"
  701. }
  702. return errors.Wrapf(err, msg, path, hdr.Uid, hdr.Gid)
  703. }
  704. }
  705. var errors []string
  706. for key, value := range hdr.Xattrs {
  707. if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil {
  708. if err == syscall.ENOTSUP || err == syscall.EPERM {
  709. // We ignore errors here because not all graphdrivers support
  710. // xattrs *cough* old versions of AUFS *cough*. However only
  711. // ENOTSUP should be emitted in that case, otherwise we still
  712. // bail.
  713. // EPERM occurs if modifying xattrs is not allowed. This can
  714. // happen when running in userns with restrictions (ChromeOS).
  715. errors = append(errors, err.Error())
  716. continue
  717. }
  718. return err
  719. }
  720. }
  721. if len(errors) > 0 {
  722. logrus.WithFields(logrus.Fields{
  723. "errors": errors,
  724. }).Warn("ignored xattrs in archive: underlying filesystem doesn't support them")
  725. }
  726. // There is no LChmod, so ignore mode for symlink. Also, this
  727. // must happen after chown, as that can modify the file mode
  728. if err := handleLChmod(hdr, path, hdrInfo); err != nil {
  729. return err
  730. }
  731. aTime := hdr.AccessTime
  732. if aTime.Before(hdr.ModTime) {
  733. // Last access time should never be before last modified time.
  734. aTime = hdr.ModTime
  735. }
  736. // system.Chtimes doesn't support a NOFOLLOW flag atm
  737. if hdr.Typeflag == tar.TypeLink {
  738. if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
  739. if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil {
  740. return err
  741. }
  742. }
  743. } else if hdr.Typeflag != tar.TypeSymlink {
  744. if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil {
  745. return err
  746. }
  747. } else {
  748. ts := []syscall.Timespec{timeToTimespec(aTime), timeToTimespec(hdr.ModTime)}
  749. if err := system.LUtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform {
  750. return err
  751. }
  752. }
  753. return nil
  754. }
  755. // Tar creates an archive from the directory at `path`, and returns it as a
  756. // stream of bytes.
  757. func Tar(path string, compression Compression) (io.ReadCloser, error) {
  758. return TarWithOptions(path, &TarOptions{Compression: compression})
  759. }
  760. // TarWithOptions creates an archive from the directory at `path`, only including files whose relative
  761. // paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
  762. func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
  763. // Fix the source path to work with long path names. This is a no-op
  764. // on platforms other than Windows.
  765. srcPath = fixVolumePathPrefix(srcPath)
  766. pm, err := fileutils.NewPatternMatcher(options.ExcludePatterns)
  767. if err != nil {
  768. return nil, err
  769. }
  770. pipeReader, pipeWriter := io.Pipe()
  771. compressWriter, err := CompressStream(pipeWriter, options.Compression)
  772. if err != nil {
  773. return nil, err
  774. }
  775. whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS)
  776. if err != nil {
  777. return nil, err
  778. }
  779. go func() {
  780. ta := newTarAppender(
  781. options.IDMap,
  782. compressWriter,
  783. options.ChownOpts,
  784. )
  785. ta.WhiteoutConverter = whiteoutConverter
  786. defer func() {
  787. // Make sure to check the error on Close.
  788. if err := ta.TarWriter.Close(); err != nil {
  789. logrus.Errorf("Can't close tar writer: %s", err)
  790. }
  791. if err := compressWriter.Close(); err != nil {
  792. logrus.Errorf("Can't close compress writer: %s", err)
  793. }
  794. if err := pipeWriter.Close(); err != nil {
  795. logrus.Errorf("Can't close pipe writer: %s", err)
  796. }
  797. }()
  798. // this buffer is needed for the duration of this piped stream
  799. defer pools.BufioWriter32KPool.Put(ta.Buffer)
  800. // In general we log errors here but ignore them because
  801. // during e.g. a diff operation the container can continue
  802. // mutating the filesystem and we can see transient errors
  803. // from this
  804. stat, err := os.Lstat(srcPath)
  805. if err != nil {
  806. return
  807. }
  808. if !stat.IsDir() {
  809. // We can't later join a non-dir with any includes because the
  810. // 'walk' will error if "file/." is stat-ed and "file" is not a
  811. // directory. So, we must split the source path and use the
  812. // basename as the include.
  813. if len(options.IncludeFiles) > 0 {
  814. logrus.Warn("Tar: Can't archive a file with includes")
  815. }
  816. dir, base := SplitPathDirEntry(srcPath)
  817. srcPath = dir
  818. options.IncludeFiles = []string{base}
  819. }
  820. if len(options.IncludeFiles) == 0 {
  821. options.IncludeFiles = []string{"."}
  822. }
  823. seen := make(map[string]bool)
  824. for _, include := range options.IncludeFiles {
  825. rebaseName := options.RebaseNames[include]
  826. var (
  827. parentMatchInfo []fileutils.MatchInfo
  828. parentDirs []string
  829. )
  830. walkRoot := getWalkRoot(srcPath, include)
  831. filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
  832. if err != nil {
  833. logrus.Errorf("Tar: Can't stat file %s to tar: %s", srcPath, err)
  834. return nil
  835. }
  836. relFilePath, err := filepath.Rel(srcPath, filePath)
  837. if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
  838. // Error getting relative path OR we are looking
  839. // at the source directory path. Skip in both situations.
  840. return nil
  841. }
  842. if options.IncludeSourceDir && include == "." && relFilePath != "." {
  843. relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
  844. }
  845. skip := false
  846. // If "include" is an exact match for the current file
  847. // then even if there's an "excludePatterns" pattern that
  848. // matches it, don't skip it. IOW, assume an explicit 'include'
  849. // is asking for that file no matter what - which is true
  850. // for some files, like .dockerignore and Dockerfile (sometimes)
  851. if include != relFilePath {
  852. for len(parentDirs) != 0 {
  853. lastParentDir := parentDirs[len(parentDirs)-1]
  854. if strings.HasPrefix(relFilePath, lastParentDir+string(os.PathSeparator)) {
  855. break
  856. }
  857. parentDirs = parentDirs[:len(parentDirs)-1]
  858. parentMatchInfo = parentMatchInfo[:len(parentMatchInfo)-1]
  859. }
  860. var matchInfo fileutils.MatchInfo
  861. if len(parentMatchInfo) != 0 {
  862. skip, matchInfo, err = pm.MatchesUsingParentResults(relFilePath, parentMatchInfo[len(parentMatchInfo)-1])
  863. } else {
  864. skip, matchInfo, err = pm.MatchesUsingParentResults(relFilePath, fileutils.MatchInfo{})
  865. }
  866. if err != nil {
  867. logrus.Errorf("Error matching %s: %v", relFilePath, err)
  868. return err
  869. }
  870. if f.IsDir() {
  871. parentDirs = append(parentDirs, relFilePath)
  872. parentMatchInfo = append(parentMatchInfo, matchInfo)
  873. }
  874. }
  875. if skip {
  876. // If we want to skip this file and its a directory
  877. // then we should first check to see if there's an
  878. // excludes pattern (e.g. !dir/file) that starts with this
  879. // dir. If so then we can't skip this dir.
  880. // Its not a dir then so we can just return/skip.
  881. if !f.IsDir() {
  882. return nil
  883. }
  884. // No exceptions (!...) in patterns so just skip dir
  885. if !pm.Exclusions() {
  886. return filepath.SkipDir
  887. }
  888. dirSlash := relFilePath + string(filepath.Separator)
  889. for _, pat := range pm.Patterns() {
  890. if !pat.Exclusion() {
  891. continue
  892. }
  893. if strings.HasPrefix(pat.String()+string(filepath.Separator), dirSlash) {
  894. // found a match - so can't skip this dir
  895. return nil
  896. }
  897. }
  898. // No matching exclusion dir so just skip dir
  899. return filepath.SkipDir
  900. }
  901. if seen[relFilePath] {
  902. return nil
  903. }
  904. seen[relFilePath] = true
  905. // Rename the base resource.
  906. if rebaseName != "" {
  907. var replacement string
  908. if rebaseName != string(filepath.Separator) {
  909. // Special case the root directory to replace with an
  910. // empty string instead so that we don't end up with
  911. // double slashes in the paths.
  912. replacement = rebaseName
  913. }
  914. relFilePath = strings.Replace(relFilePath, include, replacement, 1)
  915. }
  916. if err := ta.addTarFile(filePath, relFilePath); err != nil {
  917. logrus.Errorf("Can't add file %s to tar: %s", filePath, err)
  918. // if pipe is broken, stop writing tar stream to it
  919. if err == io.ErrClosedPipe {
  920. return err
  921. }
  922. }
  923. return nil
  924. })
  925. }
  926. }()
  927. return pipeReader, nil
  928. }
  929. // Unpack unpacks the decompressedArchive to dest with options.
  930. func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) error {
  931. tr := tar.NewReader(decompressedArchive)
  932. trBuf := pools.BufioReader32KPool.Get(nil)
  933. defer pools.BufioReader32KPool.Put(trBuf)
  934. var dirs []*tar.Header
  935. rootIDs := options.IDMap.RootPair()
  936. whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS)
  937. if err != nil {
  938. return err
  939. }
  940. // Iterate through the files in the archive.
  941. loop:
  942. for {
  943. hdr, err := tr.Next()
  944. if err == io.EOF {
  945. // end of tar archive
  946. break
  947. }
  948. if err != nil {
  949. return err
  950. }
  951. // ignore XGlobalHeader early to avoid creating parent directories for them
  952. if hdr.Typeflag == tar.TypeXGlobalHeader {
  953. logrus.Debugf("PAX Global Extended Headers found for %s and ignored", hdr.Name)
  954. continue
  955. }
  956. // Normalize name, for safety and for a simple is-root check
  957. // This keeps "../" as-is, but normalizes "/../" to "/". Or Windows:
  958. // This keeps "..\" as-is, but normalizes "\..\" to "\".
  959. hdr.Name = filepath.Clean(hdr.Name)
  960. for _, exclude := range options.ExcludePatterns {
  961. if strings.HasPrefix(hdr.Name, exclude) {
  962. continue loop
  963. }
  964. }
  965. // After calling filepath.Clean(hdr.Name) above, hdr.Name will now be in
  966. // the filepath format for the OS on which the daemon is running. Hence
  967. // the check for a slash-suffix MUST be done in an OS-agnostic way.
  968. if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) {
  969. // Not the root directory, ensure that the parent directory exists
  970. parent := filepath.Dir(hdr.Name)
  971. parentPath := filepath.Join(dest, parent)
  972. if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
  973. err = idtools.MkdirAllAndChownNew(parentPath, 0755, rootIDs)
  974. if err != nil {
  975. return err
  976. }
  977. }
  978. }
  979. // #nosec G305 -- The joined path is checked for path traversal.
  980. path := filepath.Join(dest, hdr.Name)
  981. rel, err := filepath.Rel(dest, path)
  982. if err != nil {
  983. return err
  984. }
  985. if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
  986. return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
  987. }
  988. // If path exits we almost always just want to remove and replace it
  989. // The only exception is when it is a directory *and* the file from
  990. // the layer is also a directory. Then we want to merge them (i.e.
  991. // just apply the metadata from the layer).
  992. if fi, err := os.Lstat(path); err == nil {
  993. if options.NoOverwriteDirNonDir && fi.IsDir() && hdr.Typeflag != tar.TypeDir {
  994. // If NoOverwriteDirNonDir is true then we cannot replace
  995. // an existing directory with a non-directory from the archive.
  996. return fmt.Errorf("cannot overwrite directory %q with non-directory %q", path, dest)
  997. }
  998. if options.NoOverwriteDirNonDir && !fi.IsDir() && hdr.Typeflag == tar.TypeDir {
  999. // If NoOverwriteDirNonDir is true then we cannot replace
  1000. // an existing non-directory with a directory from the archive.
  1001. return fmt.Errorf("cannot overwrite non-directory %q with directory %q", path, dest)
  1002. }
  1003. if fi.IsDir() && hdr.Name == "." {
  1004. continue
  1005. }
  1006. if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
  1007. if err := os.RemoveAll(path); err != nil {
  1008. return err
  1009. }
  1010. }
  1011. }
  1012. trBuf.Reset(tr)
  1013. if err := remapIDs(options.IDMap, hdr); err != nil {
  1014. return err
  1015. }
  1016. if whiteoutConverter != nil {
  1017. writeFile, err := whiteoutConverter.ConvertRead(hdr, path)
  1018. if err != nil {
  1019. return err
  1020. }
  1021. if !writeFile {
  1022. continue
  1023. }
  1024. }
  1025. if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts, options.InUserNS); err != nil {
  1026. return err
  1027. }
  1028. // Directory mtimes must be handled at the end to avoid further
  1029. // file creation in them to modify the directory mtime
  1030. if hdr.Typeflag == tar.TypeDir {
  1031. dirs = append(dirs, hdr)
  1032. }
  1033. }
  1034. for _, hdr := range dirs {
  1035. // #nosec G305 -- The header was checked for path traversal before it was appended to the dirs slice.
  1036. path := filepath.Join(dest, hdr.Name)
  1037. if err := system.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil {
  1038. return err
  1039. }
  1040. }
  1041. return nil
  1042. }
  1043. // Untar reads a stream of bytes from `archive`, parses it as a tar archive,
  1044. // and unpacks it into the directory at `dest`.
  1045. // The archive may be compressed with one of the following algorithms:
  1046. // identity (uncompressed), gzip, bzip2, xz.
  1047. //
  1048. // FIXME: specify behavior when target path exists vs. doesn't exist.
  1049. func Untar(tarArchive io.Reader, dest string, options *TarOptions) error {
  1050. return untarHandler(tarArchive, dest, options, true)
  1051. }
  1052. // UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive,
  1053. // and unpacks it into the directory at `dest`.
  1054. // The archive must be an uncompressed stream.
  1055. func UntarUncompressed(tarArchive io.Reader, dest string, options *TarOptions) error {
  1056. return untarHandler(tarArchive, dest, options, false)
  1057. }
  1058. // Handler for teasing out the automatic decompression
  1059. func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decompress bool) error {
  1060. if tarArchive == nil {
  1061. return fmt.Errorf("Empty archive")
  1062. }
  1063. dest = filepath.Clean(dest)
  1064. if options == nil {
  1065. options = &TarOptions{}
  1066. }
  1067. if options.ExcludePatterns == nil {
  1068. options.ExcludePatterns = []string{}
  1069. }
  1070. r := tarArchive
  1071. if decompress {
  1072. decompressedArchive, err := DecompressStream(tarArchive)
  1073. if err != nil {
  1074. return err
  1075. }
  1076. defer decompressedArchive.Close()
  1077. r = decompressedArchive
  1078. }
  1079. return Unpack(r, dest, options)
  1080. }
  1081. // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
  1082. // If either Tar or Untar fails, TarUntar aborts and returns the error.
  1083. func (archiver *Archiver) TarUntar(src, dst string) error {
  1084. archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed})
  1085. if err != nil {
  1086. return err
  1087. }
  1088. defer archive.Close()
  1089. options := &TarOptions{
  1090. IDMap: archiver.IDMapping,
  1091. }
  1092. return archiver.Untar(archive, dst, options)
  1093. }
  1094. // UntarPath untar a file from path to a destination, src is the source tar file path.
  1095. func (archiver *Archiver) UntarPath(src, dst string) error {
  1096. archive, err := os.Open(src)
  1097. if err != nil {
  1098. return err
  1099. }
  1100. defer archive.Close()
  1101. options := &TarOptions{
  1102. IDMap: archiver.IDMapping,
  1103. }
  1104. return archiver.Untar(archive, dst, options)
  1105. }
  1106. // CopyWithTar creates a tar archive of filesystem path `src`, and
  1107. // unpacks it at filesystem path `dst`.
  1108. // The archive is streamed directly with fixed buffering and no
  1109. // intermediary disk IO.
  1110. func (archiver *Archiver) CopyWithTar(src, dst string) error {
  1111. srcSt, err := os.Stat(src)
  1112. if err != nil {
  1113. return err
  1114. }
  1115. if !srcSt.IsDir() {
  1116. return archiver.CopyFileWithTar(src, dst)
  1117. }
  1118. // if this Archiver is set up with ID mapping we need to create
  1119. // the new destination directory with the remapped root UID/GID pair
  1120. // as owner
  1121. rootIDs := archiver.IDMapping.RootPair()
  1122. // Create dst, copy src's content into it
  1123. if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
  1124. return err
  1125. }
  1126. return archiver.TarUntar(src, dst)
  1127. }
  1128. // CopyFileWithTar emulates the behavior of the 'cp' command-line
  1129. // for a single file. It copies a regular file from path `src` to
  1130. // path `dst`, and preserves all its metadata.
  1131. func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
  1132. srcSt, err := os.Stat(src)
  1133. if err != nil {
  1134. return err
  1135. }
  1136. if srcSt.IsDir() {
  1137. return fmt.Errorf("Can't copy a directory")
  1138. }
  1139. // Clean up the trailing slash. This must be done in an operating
  1140. // system specific manner.
  1141. if dst[len(dst)-1] == os.PathSeparator {
  1142. dst = filepath.Join(dst, filepath.Base(src))
  1143. }
  1144. // Create the holding directory if necessary
  1145. if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil {
  1146. return err
  1147. }
  1148. r, w := io.Pipe()
  1149. errC := make(chan error, 1)
  1150. go func() {
  1151. defer close(errC)
  1152. errC <- func() error {
  1153. defer w.Close()
  1154. srcF, err := os.Open(src)
  1155. if err != nil {
  1156. return err
  1157. }
  1158. defer srcF.Close()
  1159. hdr, err := FileInfoHeaderNoLookups(srcSt, "")
  1160. if err != nil {
  1161. return err
  1162. }
  1163. hdr.Format = tar.FormatPAX
  1164. hdr.ModTime = hdr.ModTime.Truncate(time.Second)
  1165. hdr.AccessTime = time.Time{}
  1166. hdr.ChangeTime = time.Time{}
  1167. hdr.Name = filepath.Base(dst)
  1168. hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
  1169. if err := remapIDs(archiver.IDMapping, hdr); err != nil {
  1170. return err
  1171. }
  1172. tw := tar.NewWriter(w)
  1173. defer tw.Close()
  1174. if err := tw.WriteHeader(hdr); err != nil {
  1175. return err
  1176. }
  1177. if _, err := io.Copy(tw, srcF); err != nil {
  1178. return err
  1179. }
  1180. return nil
  1181. }()
  1182. }()
  1183. defer func() {
  1184. if er := <-errC; err == nil && er != nil {
  1185. err = er
  1186. }
  1187. }()
  1188. err = archiver.Untar(r, filepath.Dir(dst), nil)
  1189. if err != nil {
  1190. r.CloseWithError(err)
  1191. }
  1192. return err
  1193. }
  1194. // IdentityMapping returns the IdentityMapping of the archiver.
  1195. func (archiver *Archiver) IdentityMapping() idtools.IdentityMapping {
  1196. return archiver.IDMapping
  1197. }
  1198. func remapIDs(idMapping idtools.IdentityMapping, hdr *tar.Header) error {
  1199. ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
  1200. hdr.Uid, hdr.Gid = ids.UID, ids.GID
  1201. return err
  1202. }
  1203. // cmdStream executes a command, and returns its stdout as a stream.
  1204. // If the command fails to run or doesn't complete successfully, an error
  1205. // will be returned, including anything written on stderr.
  1206. func cmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) {
  1207. cmd.Stdin = input
  1208. pipeR, pipeW := io.Pipe()
  1209. cmd.Stdout = pipeW
  1210. var errBuf bytes.Buffer
  1211. cmd.Stderr = &errBuf
  1212. // Run the command and return the pipe
  1213. if err := cmd.Start(); err != nil {
  1214. return nil, err
  1215. }
  1216. // Ensure the command has exited before we clean anything up
  1217. done := make(chan struct{})
  1218. // Copy stdout to the returned pipe
  1219. go func() {
  1220. if err := cmd.Wait(); err != nil {
  1221. pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errBuf.String()))
  1222. } else {
  1223. pipeW.Close()
  1224. }
  1225. close(done)
  1226. }()
  1227. return ioutils.NewReadCloserWrapper(pipeR, func() error {
  1228. // Close pipeR, and then wait for the command to complete before returning. We have to close pipeR first, as
  1229. // cmd.Wait waits for any non-file stdout/stderr/stdin to close.
  1230. err := pipeR.Close()
  1231. <-done
  1232. return err
  1233. }), nil
  1234. }
  1235. // NewTempArchive reads the content of src into a temporary file, and returns the contents
  1236. // of that file as an archive. The archive can only be read once - as soon as reading completes,
  1237. // the file will be deleted.
  1238. func NewTempArchive(src io.Reader, dir string) (*TempArchive, error) {
  1239. f, err := os.CreateTemp(dir, "")
  1240. if err != nil {
  1241. return nil, err
  1242. }
  1243. if _, err := io.Copy(f, src); err != nil {
  1244. return nil, err
  1245. }
  1246. if _, err := f.Seek(0, 0); err != nil {
  1247. return nil, err
  1248. }
  1249. st, err := f.Stat()
  1250. if err != nil {
  1251. return nil, err
  1252. }
  1253. size := st.Size()
  1254. return &TempArchive{File: f, Size: size}, nil
  1255. }
  1256. // TempArchive is a temporary archive. The archive can only be read once - as soon as reading completes,
  1257. // the file will be deleted.
  1258. type TempArchive struct {
  1259. *os.File
  1260. Size int64 // Pre-computed from Stat().Size() as a convenience
  1261. read int64
  1262. closed bool
  1263. }
  1264. // Close closes the underlying file if it's still open, or does a no-op
  1265. // to allow callers to try to close the TempArchive multiple times safely.
  1266. func (archive *TempArchive) Close() error {
  1267. if archive.closed {
  1268. return nil
  1269. }
  1270. archive.closed = true
  1271. return archive.File.Close()
  1272. }
  1273. func (archive *TempArchive) Read(data []byte) (int, error) {
  1274. n, err := archive.File.Read(data)
  1275. archive.read += int64(n)
  1276. if err != nil || archive.read == archive.Size {
  1277. archive.Close()
  1278. os.Remove(archive.File.Name())
  1279. }
  1280. return n, err
  1281. }