archive.go 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  1. package archive
  2. import (
  3. "archive/tar"
  4. "bufio"
  5. "bytes"
  6. "compress/bzip2"
  7. "compress/gzip"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "io/ioutil"
  12. "os"
  13. "os/exec"
  14. "path/filepath"
  15. "runtime"
  16. "strings"
  17. "syscall"
  18. "github.com/Sirupsen/logrus"
  19. "github.com/docker/docker/pkg/fileutils"
  20. "github.com/docker/docker/pkg/idtools"
  21. "github.com/docker/docker/pkg/ioutils"
  22. "github.com/docker/docker/pkg/pools"
  23. "github.com/docker/docker/pkg/promise"
  24. "github.com/docker/docker/pkg/system"
  25. )
  26. type (
  27. // Archive is a type of io.ReadCloser which has two interfaces Read and Closer.
  28. Archive io.ReadCloser
  29. // Reader is a type of io.Reader.
  30. Reader io.Reader
  31. // Compression is the state represtents if compressed or not.
  32. Compression int
  33. // TarChownOptions wraps the chown options UID and GID.
  34. TarChownOptions struct {
  35. UID, GID int
  36. }
  37. // TarOptions wraps the tar options.
  38. TarOptions struct {
  39. IncludeFiles []string
  40. ExcludePatterns []string
  41. Compression Compression
  42. NoLchown bool
  43. UIDMaps []idtools.IDMap
  44. GIDMaps []idtools.IDMap
  45. ChownOpts *TarChownOptions
  46. IncludeSourceDir bool
  47. // When unpacking, specifies whether overwriting a directory with a
  48. // non-directory is allowed and vice versa.
  49. NoOverwriteDirNonDir bool
  50. // For each include when creating an archive, the included name will be
  51. // replaced with the matching name from this map.
  52. RebaseNames map[string]string
  53. }
  54. // Archiver allows the reuse of most utility functions of this package
  55. // with a pluggable Untar function. Also, to facilitate the passing of
  56. // specific id mappings for untar, an archiver can be created with maps
  57. // which will then be passed to Untar operations
  58. Archiver struct {
  59. Untar func(io.Reader, string, *TarOptions) error
  60. UIDMaps []idtools.IDMap
  61. GIDMaps []idtools.IDMap
  62. }
  63. // breakoutError is used to differentiate errors related to breaking out
  64. // When testing archive breakout in the unit tests, this error is expected
  65. // in order for the test to pass.
  66. breakoutError error
  67. )
  68. var (
  69. // ErrNotImplemented is the error message of function not implemented.
  70. ErrNotImplemented = errors.New("Function not implemented")
  71. defaultArchiver = &Archiver{Untar: Untar, UIDMaps: nil, GIDMaps: nil}
  72. )
  73. const (
  74. // Uncompressed represents the uncompressed.
  75. Uncompressed Compression = iota
  76. // Bzip2 is bzip2 compression algorithm.
  77. Bzip2
  78. // Gzip is gzip compression algorithm.
  79. Gzip
  80. // Xz is xz compression algorithm.
  81. Xz
  82. )
  83. // IsArchive checks if it is a archive by the header.
  84. func IsArchive(header []byte) bool {
  85. compression := DetectCompression(header)
  86. if compression != Uncompressed {
  87. return true
  88. }
  89. r := tar.NewReader(bytes.NewBuffer(header))
  90. _, err := r.Next()
  91. return err == nil
  92. }
  93. // DetectCompression detects the compression algorithm of the source.
  94. func DetectCompression(source []byte) Compression {
  95. for compression, m := range map[Compression][]byte{
  96. Bzip2: {0x42, 0x5A, 0x68},
  97. Gzip: {0x1F, 0x8B, 0x08},
  98. Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
  99. } {
  100. if len(source) < len(m) {
  101. logrus.Debugf("Len too short")
  102. continue
  103. }
  104. if bytes.Compare(m, source[:len(m)]) == 0 {
  105. return compression
  106. }
  107. }
  108. return Uncompressed
  109. }
  110. func xzDecompress(archive io.Reader) (io.ReadCloser, <-chan struct{}, error) {
  111. args := []string{"xz", "-d", "-c", "-q"}
  112. return cmdStream(exec.Command(args[0], args[1:]...), archive)
  113. }
  114. // DecompressStream decompress the archive and returns a ReaderCloser with the decompressed archive.
  115. func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
  116. p := pools.BufioReader32KPool
  117. buf := p.Get(archive)
  118. bs, err := buf.Peek(10)
  119. if err != nil {
  120. return nil, err
  121. }
  122. compression := DetectCompression(bs)
  123. switch compression {
  124. case Uncompressed:
  125. readBufWrapper := p.NewReadCloserWrapper(buf, buf)
  126. return readBufWrapper, nil
  127. case Gzip:
  128. gzReader, err := gzip.NewReader(buf)
  129. if err != nil {
  130. return nil, err
  131. }
  132. readBufWrapper := p.NewReadCloserWrapper(buf, gzReader)
  133. return readBufWrapper, nil
  134. case Bzip2:
  135. bz2Reader := bzip2.NewReader(buf)
  136. readBufWrapper := p.NewReadCloserWrapper(buf, bz2Reader)
  137. return readBufWrapper, nil
  138. case Xz:
  139. xzReader, chdone, err := xzDecompress(buf)
  140. if err != nil {
  141. return nil, err
  142. }
  143. readBufWrapper := p.NewReadCloserWrapper(buf, xzReader)
  144. return ioutils.NewReadCloserWrapper(readBufWrapper, func() error {
  145. <-chdone
  146. return readBufWrapper.Close()
  147. }), nil
  148. default:
  149. return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
  150. }
  151. }
  152. // CompressStream compresses the dest with specified compression algorithm.
  153. func CompressStream(dest io.WriteCloser, compression Compression) (io.WriteCloser, error) {
  154. p := pools.BufioWriter32KPool
  155. buf := p.Get(dest)
  156. switch compression {
  157. case Uncompressed:
  158. writeBufWrapper := p.NewWriteCloserWrapper(buf, buf)
  159. return writeBufWrapper, nil
  160. case Gzip:
  161. gzWriter := gzip.NewWriter(dest)
  162. writeBufWrapper := p.NewWriteCloserWrapper(buf, gzWriter)
  163. return writeBufWrapper, nil
  164. case Bzip2, Xz:
  165. // archive/bzip2 does not support writing, and there is no xz support at all
  166. // However, this is not a problem as docker only currently generates gzipped tars
  167. return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
  168. default:
  169. return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
  170. }
  171. }
  172. // Extension returns the extension of a file that uses the specified compression algorithm.
  173. func (compression *Compression) Extension() string {
  174. switch *compression {
  175. case Uncompressed:
  176. return "tar"
  177. case Bzip2:
  178. return "tar.bz2"
  179. case Gzip:
  180. return "tar.gz"
  181. case Xz:
  182. return "tar.xz"
  183. }
  184. return ""
  185. }
  186. type tarAppender struct {
  187. TarWriter *tar.Writer
  188. Buffer *bufio.Writer
  189. // for hardlink mapping
  190. SeenFiles map[uint64]string
  191. UIDMaps []idtools.IDMap
  192. GIDMaps []idtools.IDMap
  193. }
  194. // canonicalTarName provides a platform-independent and consistent posix-style
  195. //path for files and directories to be archived regardless of the platform.
  196. func canonicalTarName(name string, isDir bool) (string, error) {
  197. name, err := CanonicalTarNameForPath(name)
  198. if err != nil {
  199. return "", err
  200. }
  201. // suffix with '/' for directories
  202. if isDir && !strings.HasSuffix(name, "/") {
  203. name += "/"
  204. }
  205. return name, nil
  206. }
  207. func (ta *tarAppender) addTarFile(path, name string) error {
  208. fi, err := os.Lstat(path)
  209. if err != nil {
  210. return err
  211. }
  212. link := ""
  213. if fi.Mode()&os.ModeSymlink != 0 {
  214. if link, err = os.Readlink(path); err != nil {
  215. return err
  216. }
  217. }
  218. hdr, err := tar.FileInfoHeader(fi, link)
  219. if err != nil {
  220. return err
  221. }
  222. hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
  223. name, err = canonicalTarName(name, fi.IsDir())
  224. if err != nil {
  225. return fmt.Errorf("tar: cannot canonicalize path: %v", err)
  226. }
  227. hdr.Name = name
  228. inode, err := setHeaderForSpecialDevice(hdr, ta, name, fi.Sys())
  229. if err != nil {
  230. return err
  231. }
  232. // if it's not a directory and has more than 1 link,
  233. // it's hardlinked, so set the type flag accordingly
  234. if !fi.IsDir() && hasHardlinks(fi) {
  235. // a link should have a name that it links too
  236. // and that linked name should be first in the tar archive
  237. if oldpath, ok := ta.SeenFiles[inode]; ok {
  238. hdr.Typeflag = tar.TypeLink
  239. hdr.Linkname = oldpath
  240. hdr.Size = 0 // This Must be here for the writer math to add up!
  241. } else {
  242. ta.SeenFiles[inode] = name
  243. }
  244. }
  245. capability, _ := system.Lgetxattr(path, "security.capability")
  246. if capability != nil {
  247. hdr.Xattrs = make(map[string]string)
  248. hdr.Xattrs["security.capability"] = string(capability)
  249. }
  250. //handle re-mapping container ID mappings back to host ID mappings before
  251. //writing tar headers/files
  252. if ta.UIDMaps != nil || ta.GIDMaps != nil {
  253. uid, gid, err := getFileUIDGID(fi.Sys())
  254. if err != nil {
  255. return err
  256. }
  257. xUID, err := idtools.ToContainer(uid, ta.UIDMaps)
  258. if err != nil {
  259. return err
  260. }
  261. xGID, err := idtools.ToContainer(gid, ta.GIDMaps)
  262. if err != nil {
  263. return err
  264. }
  265. hdr.Uid = xUID
  266. hdr.Gid = xGID
  267. }
  268. if err := ta.TarWriter.WriteHeader(hdr); err != nil {
  269. return err
  270. }
  271. if hdr.Typeflag == tar.TypeReg {
  272. file, err := os.Open(path)
  273. if err != nil {
  274. return err
  275. }
  276. ta.Buffer.Reset(ta.TarWriter)
  277. defer ta.Buffer.Reset(nil)
  278. _, err = io.Copy(ta.Buffer, file)
  279. file.Close()
  280. if err != nil {
  281. return err
  282. }
  283. err = ta.Buffer.Flush()
  284. if err != nil {
  285. return err
  286. }
  287. }
  288. return nil
  289. }
  290. func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *TarChownOptions) error {
  291. // hdr.Mode is in linux format, which we can use for sycalls,
  292. // but for os.Foo() calls we need the mode converted to os.FileMode,
  293. // so use hdrInfo.Mode() (they differ for e.g. setuid bits)
  294. hdrInfo := hdr.FileInfo()
  295. switch hdr.Typeflag {
  296. case tar.TypeDir:
  297. // Create directory unless it exists as a directory already.
  298. // In that case we just want to merge the two
  299. if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
  300. if err := os.Mkdir(path, hdrInfo.Mode()); err != nil {
  301. return err
  302. }
  303. }
  304. case tar.TypeReg, tar.TypeRegA:
  305. // Source is regular file
  306. file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
  307. if err != nil {
  308. return err
  309. }
  310. if _, err := io.Copy(file, reader); err != nil {
  311. file.Close()
  312. return err
  313. }
  314. file.Close()
  315. case tar.TypeBlock, tar.TypeChar, tar.TypeFifo:
  316. // Handle this is an OS-specific way
  317. if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
  318. return err
  319. }
  320. case tar.TypeLink:
  321. targetPath := filepath.Join(extractDir, hdr.Linkname)
  322. // check for hardlink breakout
  323. if !strings.HasPrefix(targetPath, extractDir) {
  324. return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname))
  325. }
  326. if err := os.Link(targetPath, path); err != nil {
  327. return err
  328. }
  329. case tar.TypeSymlink:
  330. // path -> hdr.Linkname = targetPath
  331. // e.g. /extractDir/path/to/symlink -> ../2/file = /extractDir/path/2/file
  332. targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname)
  333. // the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because
  334. // that symlink would first have to be created, which would be caught earlier, at this very check:
  335. if !strings.HasPrefix(targetPath, extractDir) {
  336. return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname))
  337. }
  338. if err := os.Symlink(hdr.Linkname, path); err != nil {
  339. return err
  340. }
  341. case tar.TypeXGlobalHeader:
  342. logrus.Debugf("PAX Global Extended Headers found and ignored")
  343. return nil
  344. default:
  345. return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag)
  346. }
  347. // Lchown is not supported on Windows.
  348. if Lchown && runtime.GOOS != "windows" {
  349. if chownOpts == nil {
  350. chownOpts = &TarChownOptions{UID: hdr.Uid, GID: hdr.Gid}
  351. }
  352. if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
  353. return err
  354. }
  355. }
  356. for key, value := range hdr.Xattrs {
  357. if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil {
  358. return err
  359. }
  360. }
  361. // There is no LChmod, so ignore mode for symlink. Also, this
  362. // must happen after chown, as that can modify the file mode
  363. if err := handleLChmod(hdr, path, hdrInfo); err != nil {
  364. return err
  365. }
  366. aTime := hdr.AccessTime
  367. if aTime.Before(hdr.ModTime) {
  368. // Last access time should never be before last modified time.
  369. aTime = hdr.ModTime
  370. }
  371. // system.Chtimes doesn't support a NOFOLLOW flag atm
  372. if hdr.Typeflag == tar.TypeLink {
  373. if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
  374. if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil {
  375. return err
  376. }
  377. }
  378. } else if hdr.Typeflag != tar.TypeSymlink {
  379. if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil {
  380. return err
  381. }
  382. } else {
  383. ts := []syscall.Timespec{timeToTimespec(aTime), timeToTimespec(hdr.ModTime)}
  384. if err := system.LUtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform {
  385. return err
  386. }
  387. }
  388. return nil
  389. }
  390. // Tar creates an archive from the directory at `path`, and returns it as a
  391. // stream of bytes.
  392. func Tar(path string, compression Compression) (io.ReadCloser, error) {
  393. return TarWithOptions(path, &TarOptions{Compression: compression})
  394. }
  395. // TarWithOptions creates an archive from the directory at `path`, only including files whose relative
  396. // paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
  397. func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
  398. // Fix the source path to work with long path names. This is a no-op
  399. // on platforms other than Windows.
  400. srcPath = fixVolumePathPrefix(srcPath)
  401. patterns, patDirs, exceptions, err := fileutils.CleanPatterns(options.ExcludePatterns)
  402. if err != nil {
  403. return nil, err
  404. }
  405. pipeReader, pipeWriter := io.Pipe()
  406. compressWriter, err := CompressStream(pipeWriter, options.Compression)
  407. if err != nil {
  408. return nil, err
  409. }
  410. go func() {
  411. ta := &tarAppender{
  412. TarWriter: tar.NewWriter(compressWriter),
  413. Buffer: pools.BufioWriter32KPool.Get(nil),
  414. SeenFiles: make(map[uint64]string),
  415. UIDMaps: options.UIDMaps,
  416. GIDMaps: options.GIDMaps,
  417. }
  418. defer func() {
  419. // Make sure to check the error on Close.
  420. if err := ta.TarWriter.Close(); err != nil {
  421. logrus.Debugf("Can't close tar writer: %s", err)
  422. }
  423. if err := compressWriter.Close(); err != nil {
  424. logrus.Debugf("Can't close compress writer: %s", err)
  425. }
  426. if err := pipeWriter.Close(); err != nil {
  427. logrus.Debugf("Can't close pipe writer: %s", err)
  428. }
  429. }()
  430. // this buffer is needed for the duration of this piped stream
  431. defer pools.BufioWriter32KPool.Put(ta.Buffer)
  432. // In general we log errors here but ignore them because
  433. // during e.g. a diff operation the container can continue
  434. // mutating the filesystem and we can see transient errors
  435. // from this
  436. stat, err := os.Lstat(srcPath)
  437. if err != nil {
  438. return
  439. }
  440. if !stat.IsDir() {
  441. // We can't later join a non-dir with any includes because the
  442. // 'walk' will error if "file/." is stat-ed and "file" is not a
  443. // directory. So, we must split the source path and use the
  444. // basename as the include.
  445. if len(options.IncludeFiles) > 0 {
  446. logrus.Warn("Tar: Can't archive a file with includes")
  447. }
  448. dir, base := SplitPathDirEntry(srcPath)
  449. srcPath = dir
  450. options.IncludeFiles = []string{base}
  451. }
  452. if len(options.IncludeFiles) == 0 {
  453. options.IncludeFiles = []string{"."}
  454. }
  455. seen := make(map[string]bool)
  456. for _, include := range options.IncludeFiles {
  457. rebaseName := options.RebaseNames[include]
  458. walkRoot := getWalkRoot(srcPath, include)
  459. filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
  460. if err != nil {
  461. logrus.Debugf("Tar: Can't stat file %s to tar: %s", srcPath, err)
  462. return nil
  463. }
  464. relFilePath, err := filepath.Rel(srcPath, filePath)
  465. if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
  466. // Error getting relative path OR we are looking
  467. // at the source directory path. Skip in both situations.
  468. return nil
  469. }
  470. if options.IncludeSourceDir && include == "." && relFilePath != "." {
  471. relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
  472. }
  473. skip := false
  474. // If "include" is an exact match for the current file
  475. // then even if there's an "excludePatterns" pattern that
  476. // matches it, don't skip it. IOW, assume an explicit 'include'
  477. // is asking for that file no matter what - which is true
  478. // for some files, like .dockerignore and Dockerfile (sometimes)
  479. if include != relFilePath {
  480. skip, err = fileutils.OptimizedMatches(relFilePath, patterns, patDirs)
  481. if err != nil {
  482. logrus.Debugf("Error matching %s: %v", relFilePath, err)
  483. return err
  484. }
  485. }
  486. if skip {
  487. if !exceptions && f.IsDir() {
  488. return filepath.SkipDir
  489. }
  490. return nil
  491. }
  492. if seen[relFilePath] {
  493. return nil
  494. }
  495. seen[relFilePath] = true
  496. // Rename the base resource.
  497. if rebaseName != "" {
  498. var replacement string
  499. if rebaseName != string(filepath.Separator) {
  500. // Special case the root directory to replace with an
  501. // empty string instead so that we don't end up with
  502. // double slashes in the paths.
  503. replacement = rebaseName
  504. }
  505. relFilePath = strings.Replace(relFilePath, include, replacement, 1)
  506. }
  507. if err := ta.addTarFile(filePath, relFilePath); err != nil {
  508. logrus.Debugf("Can't add file %s to tar: %s", filePath, err)
  509. }
  510. return nil
  511. })
  512. }
  513. }()
  514. return pipeReader, nil
  515. }
  516. // Unpack unpacks the decompressedArchive to dest with options.
  517. func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) error {
  518. tr := tar.NewReader(decompressedArchive)
  519. trBuf := pools.BufioReader32KPool.Get(nil)
  520. defer pools.BufioReader32KPool.Put(trBuf)
  521. var dirs []*tar.Header
  522. remappedRootUID, remappedRootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps)
  523. if err != nil {
  524. return err
  525. }
  526. // Iterate through the files in the archive.
  527. loop:
  528. for {
  529. hdr, err := tr.Next()
  530. if err == io.EOF {
  531. // end of tar archive
  532. break
  533. }
  534. if err != nil {
  535. return err
  536. }
  537. // Normalize name, for safety and for a simple is-root check
  538. // This keeps "../" as-is, but normalizes "/../" to "/". Or Windows:
  539. // This keeps "..\" as-is, but normalizes "\..\" to "\".
  540. hdr.Name = filepath.Clean(hdr.Name)
  541. for _, exclude := range options.ExcludePatterns {
  542. if strings.HasPrefix(hdr.Name, exclude) {
  543. continue loop
  544. }
  545. }
  546. // After calling filepath.Clean(hdr.Name) above, hdr.Name will now be in
  547. // the filepath format for the OS on which the daemon is running. Hence
  548. // the check for a slash-suffix MUST be done in an OS-agnostic way.
  549. if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) {
  550. // Not the root directory, ensure that the parent directory exists
  551. parent := filepath.Dir(hdr.Name)
  552. parentPath := filepath.Join(dest, parent)
  553. if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
  554. err = system.MkdirAll(parentPath, 0777)
  555. if err != nil {
  556. return err
  557. }
  558. }
  559. }
  560. path := filepath.Join(dest, hdr.Name)
  561. rel, err := filepath.Rel(dest, path)
  562. if err != nil {
  563. return err
  564. }
  565. if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
  566. return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
  567. }
  568. // If path exits we almost always just want to remove and replace it
  569. // The only exception is when it is a directory *and* the file from
  570. // the layer is also a directory. Then we want to merge them (i.e.
  571. // just apply the metadata from the layer).
  572. if fi, err := os.Lstat(path); err == nil {
  573. if options.NoOverwriteDirNonDir && fi.IsDir() && hdr.Typeflag != tar.TypeDir {
  574. // If NoOverwriteDirNonDir is true then we cannot replace
  575. // an existing directory with a non-directory from the archive.
  576. return fmt.Errorf("cannot overwrite directory %q with non-directory %q", path, dest)
  577. }
  578. if options.NoOverwriteDirNonDir && !fi.IsDir() && hdr.Typeflag == tar.TypeDir {
  579. // If NoOverwriteDirNonDir is true then we cannot replace
  580. // an existing non-directory with a directory from the archive.
  581. return fmt.Errorf("cannot overwrite non-directory %q with directory %q", path, dest)
  582. }
  583. if fi.IsDir() && hdr.Name == "." {
  584. continue
  585. }
  586. if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
  587. if err := os.RemoveAll(path); err != nil {
  588. return err
  589. }
  590. }
  591. }
  592. trBuf.Reset(tr)
  593. // if the options contain a uid & gid maps, convert header uid/gid
  594. // entries using the maps such that lchown sets the proper mapped
  595. // uid/gid after writing the file. We only perform this mapping if
  596. // the file isn't already owned by the remapped root UID or GID, as
  597. // that specific uid/gid has no mapping from container -> host, and
  598. // those files already have the proper ownership for inside the
  599. // container.
  600. if hdr.Uid != remappedRootUID {
  601. xUID, err := idtools.ToHost(hdr.Uid, options.UIDMaps)
  602. if err != nil {
  603. return err
  604. }
  605. hdr.Uid = xUID
  606. }
  607. if hdr.Gid != remappedRootGID {
  608. xGID, err := idtools.ToHost(hdr.Gid, options.GIDMaps)
  609. if err != nil {
  610. return err
  611. }
  612. hdr.Gid = xGID
  613. }
  614. if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts); err != nil {
  615. return err
  616. }
  617. // Directory mtimes must be handled at the end to avoid further
  618. // file creation in them to modify the directory mtime
  619. if hdr.Typeflag == tar.TypeDir {
  620. dirs = append(dirs, hdr)
  621. }
  622. }
  623. for _, hdr := range dirs {
  624. path := filepath.Join(dest, hdr.Name)
  625. if err := system.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil {
  626. return err
  627. }
  628. }
  629. return nil
  630. }
  631. // Untar reads a stream of bytes from `archive`, parses it as a tar archive,
  632. // and unpacks it into the directory at `dest`.
  633. // The archive may be compressed with one of the following algorithms:
  634. // identity (uncompressed), gzip, bzip2, xz.
  635. // FIXME: specify behavior when target path exists vs. doesn't exist.
  636. func Untar(tarArchive io.Reader, dest string, options *TarOptions) error {
  637. return untarHandler(tarArchive, dest, options, true)
  638. }
  639. // UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive,
  640. // and unpacks it into the directory at `dest`.
  641. // The archive must be an uncompressed stream.
  642. func UntarUncompressed(tarArchive io.Reader, dest string, options *TarOptions) error {
  643. return untarHandler(tarArchive, dest, options, false)
  644. }
  645. // Handler for teasing out the automatic decompression
  646. func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decompress bool) error {
  647. if tarArchive == nil {
  648. return fmt.Errorf("Empty archive")
  649. }
  650. dest = filepath.Clean(dest)
  651. if options == nil {
  652. options = &TarOptions{}
  653. }
  654. if options.ExcludePatterns == nil {
  655. options.ExcludePatterns = []string{}
  656. }
  657. r := tarArchive
  658. if decompress {
  659. decompressedArchive, err := DecompressStream(tarArchive)
  660. if err != nil {
  661. return err
  662. }
  663. defer decompressedArchive.Close()
  664. r = decompressedArchive
  665. }
  666. return Unpack(r, dest, options)
  667. }
  668. // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
  669. // If either Tar or Untar fails, TarUntar aborts and returns the error.
  670. func (archiver *Archiver) TarUntar(src, dst string) error {
  671. logrus.Debugf("TarUntar(%s %s)", src, dst)
  672. archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed})
  673. if err != nil {
  674. return err
  675. }
  676. defer archive.Close()
  677. var options *TarOptions
  678. if archiver.UIDMaps != nil || archiver.GIDMaps != nil {
  679. options = &TarOptions{
  680. UIDMaps: archiver.UIDMaps,
  681. GIDMaps: archiver.GIDMaps,
  682. }
  683. }
  684. return archiver.Untar(archive, dst, options)
  685. }
  686. // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
  687. // If either Tar or Untar fails, TarUntar aborts and returns the error.
  688. func TarUntar(src, dst string) error {
  689. return defaultArchiver.TarUntar(src, dst)
  690. }
  691. // UntarPath untar a file from path to a destination, src is the source tar file path.
  692. func (archiver *Archiver) UntarPath(src, dst string) error {
  693. archive, err := os.Open(src)
  694. if err != nil {
  695. return err
  696. }
  697. defer archive.Close()
  698. var options *TarOptions
  699. if archiver.UIDMaps != nil || archiver.GIDMaps != nil {
  700. options = &TarOptions{
  701. UIDMaps: archiver.UIDMaps,
  702. GIDMaps: archiver.GIDMaps,
  703. }
  704. }
  705. if err := archiver.Untar(archive, dst, options); err != nil {
  706. return err
  707. }
  708. return nil
  709. }
  710. // UntarPath is a convenience function which looks for an archive
  711. // at filesystem path `src`, and unpacks it at `dst`.
  712. func UntarPath(src, dst string) error {
  713. return defaultArchiver.UntarPath(src, dst)
  714. }
  715. // CopyWithTar creates a tar archive of filesystem path `src`, and
  716. // unpacks it at filesystem path `dst`.
  717. // The archive is streamed directly with fixed buffering and no
  718. // intermediary disk IO.
  719. func (archiver *Archiver) CopyWithTar(src, dst string) error {
  720. srcSt, err := os.Stat(src)
  721. if err != nil {
  722. return err
  723. }
  724. if !srcSt.IsDir() {
  725. return archiver.CopyFileWithTar(src, dst)
  726. }
  727. // Create dst, copy src's content into it
  728. logrus.Debugf("Creating dest directory: %s", dst)
  729. if err := system.MkdirAll(dst, 0755); err != nil {
  730. return err
  731. }
  732. logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
  733. return archiver.TarUntar(src, dst)
  734. }
  735. // CopyWithTar creates a tar archive of filesystem path `src`, and
  736. // unpacks it at filesystem path `dst`.
  737. // The archive is streamed directly with fixed buffering and no
  738. // intermediary disk IO.
  739. func CopyWithTar(src, dst string) error {
  740. return defaultArchiver.CopyWithTar(src, dst)
  741. }
  742. // CopyFileWithTar emulates the behavior of the 'cp' command-line
  743. // for a single file. It copies a regular file from path `src` to
  744. // path `dst`, and preserves all its metadata.
  745. func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
  746. logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst)
  747. srcSt, err := os.Stat(src)
  748. if err != nil {
  749. return err
  750. }
  751. if srcSt.IsDir() {
  752. return fmt.Errorf("Can't copy a directory")
  753. }
  754. // Clean up the trailing slash. This must be done in an operating
  755. // system specific manner.
  756. if dst[len(dst)-1] == os.PathSeparator {
  757. dst = filepath.Join(dst, filepath.Base(src))
  758. }
  759. // Create the holding directory if necessary
  760. if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil {
  761. return err
  762. }
  763. r, w := io.Pipe()
  764. errC := promise.Go(func() error {
  765. defer w.Close()
  766. srcF, err := os.Open(src)
  767. if err != nil {
  768. return err
  769. }
  770. defer srcF.Close()
  771. hdr, err := tar.FileInfoHeader(srcSt, "")
  772. if err != nil {
  773. return err
  774. }
  775. hdr.Name = filepath.Base(dst)
  776. hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
  777. remappedRootUID, remappedRootGID, err := idtools.GetRootUIDGID(archiver.UIDMaps, archiver.GIDMaps)
  778. if err != nil {
  779. return err
  780. }
  781. // only perform mapping if the file being copied isn't already owned by the
  782. // uid or gid of the remapped root in the container
  783. if remappedRootUID != hdr.Uid {
  784. xUID, err := idtools.ToHost(hdr.Uid, archiver.UIDMaps)
  785. if err != nil {
  786. return err
  787. }
  788. hdr.Uid = xUID
  789. }
  790. if remappedRootGID != hdr.Gid {
  791. xGID, err := idtools.ToHost(hdr.Gid, archiver.GIDMaps)
  792. if err != nil {
  793. return err
  794. }
  795. hdr.Gid = xGID
  796. }
  797. tw := tar.NewWriter(w)
  798. defer tw.Close()
  799. if err := tw.WriteHeader(hdr); err != nil {
  800. return err
  801. }
  802. if _, err := io.Copy(tw, srcF); err != nil {
  803. return err
  804. }
  805. return nil
  806. })
  807. defer func() {
  808. if er := <-errC; err != nil {
  809. err = er
  810. }
  811. }()
  812. err = archiver.Untar(r, filepath.Dir(dst), nil)
  813. if err != nil {
  814. r.CloseWithError(err)
  815. }
  816. return err
  817. }
  818. // CopyFileWithTar emulates the behavior of the 'cp' command-line
  819. // for a single file. It copies a regular file from path `src` to
  820. // path `dst`, and preserves all its metadata.
  821. //
  822. // Destination handling is in an operating specific manner depending
  823. // where the daemon is running. If `dst` ends with a trailing slash
  824. // the final destination path will be `dst/base(src)` (Linux) or
  825. // `dst\base(src)` (Windows).
  826. func CopyFileWithTar(src, dst string) (err error) {
  827. return defaultArchiver.CopyFileWithTar(src, dst)
  828. }
  829. // cmdStream executes a command, and returns its stdout as a stream.
  830. // If the command fails to run or doesn't complete successfully, an error
  831. // will be returned, including anything written on stderr.
  832. func cmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, <-chan struct{}, error) {
  833. chdone := make(chan struct{})
  834. cmd.Stdin = input
  835. pipeR, pipeW := io.Pipe()
  836. cmd.Stdout = pipeW
  837. var errBuf bytes.Buffer
  838. cmd.Stderr = &errBuf
  839. // Run the command and return the pipe
  840. if err := cmd.Start(); err != nil {
  841. return nil, nil, err
  842. }
  843. // Copy stdout to the returned pipe
  844. go func() {
  845. if err := cmd.Wait(); err != nil {
  846. pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errBuf.String()))
  847. } else {
  848. pipeW.Close()
  849. }
  850. close(chdone)
  851. }()
  852. return pipeR, chdone, nil
  853. }
  854. // NewTempArchive reads the content of src into a temporary file, and returns the contents
  855. // of that file as an archive. The archive can only be read once - as soon as reading completes,
  856. // the file will be deleted.
  857. func NewTempArchive(src Archive, dir string) (*TempArchive, error) {
  858. f, err := ioutil.TempFile(dir, "")
  859. if err != nil {
  860. return nil, err
  861. }
  862. if _, err := io.Copy(f, src); err != nil {
  863. return nil, err
  864. }
  865. if _, err := f.Seek(0, 0); err != nil {
  866. return nil, err
  867. }
  868. st, err := f.Stat()
  869. if err != nil {
  870. return nil, err
  871. }
  872. size := st.Size()
  873. return &TempArchive{File: f, Size: size}, nil
  874. }
  875. // TempArchive is a temporary archive. The archive can only be read once - as soon as reading completes,
  876. // the file will be deleted.
  877. type TempArchive struct {
  878. *os.File
  879. Size int64 // Pre-computed from Stat().Size() as a convenience
  880. read int64
  881. closed bool
  882. }
  883. // Close closes the underlying file if it's still open, or does a no-op
  884. // to allow callers to try to close the TempArchive multiple times safely.
  885. func (archive *TempArchive) Close() error {
  886. if archive.closed {
  887. return nil
  888. }
  889. archive.closed = true
  890. return archive.File.Close()
  891. }
  892. func (archive *TempArchive) Read(data []byte) (int, error) {
  893. n, err := archive.File.Read(data)
  894. archive.read += int64(n)
  895. if err != nil || archive.read == archive.Size {
  896. archive.Close()
  897. os.Remove(archive.File.Name())
  898. }
  899. return n, err
  900. }