archive_unix.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. //go:build !windows
  2. // +build !windows
  3. package daemon // import "github.com/docker/docker/daemon"
  4. import (
  5. "io"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. "github.com/docker/docker/api/types"
  10. "github.com/docker/docker/container"
  11. "github.com/docker/docker/errdefs"
  12. "github.com/docker/docker/pkg/archive"
  13. "github.com/docker/docker/pkg/chrootarchive"
  14. "github.com/docker/docker/pkg/ioutils"
  15. "github.com/docker/docker/pkg/system"
  16. volumemounts "github.com/docker/docker/volume/mounts"
  17. "github.com/pkg/errors"
  18. )
  19. // containerStatPath stats the filesystem resource at the specified path in this
  20. // container. Returns stat info about the resource.
  21. func (daemon *Daemon) containerStatPath(container *container.Container, path string) (stat *types.ContainerPathStat, err error) {
  22. container.Lock()
  23. defer container.Unlock()
  24. if err = daemon.Mount(container); err != nil {
  25. return nil, err
  26. }
  27. defer daemon.Unmount(container)
  28. err = daemon.mountVolumes(container)
  29. defer container.DetachAndUnmount(daemon.LogVolumeEvent)
  30. if err != nil {
  31. return nil, err
  32. }
  33. // Normalize path before sending to rootfs
  34. path = filepath.FromSlash(path)
  35. resolvedPath, absPath, err := container.ResolvePath(path)
  36. if err != nil {
  37. return nil, err
  38. }
  39. return container.StatPath(resolvedPath, absPath)
  40. }
  41. // containerArchivePath creates an archive of the filesystem resource at the specified
  42. // path in this container. Returns a tar archive of the resource and stat info
  43. // about the resource.
  44. func (daemon *Daemon) containerArchivePath(container *container.Container, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) {
  45. container.Lock()
  46. defer func() {
  47. if err != nil {
  48. // Wait to unlock the container until the archive is fully read
  49. // (see the ReadCloseWrapper func below) or if there is an error
  50. // before that occurs.
  51. container.Unlock()
  52. }
  53. }()
  54. if err = daemon.Mount(container); err != nil {
  55. return nil, nil, err
  56. }
  57. defer func() {
  58. if err != nil {
  59. // unmount any volumes
  60. container.DetachAndUnmount(daemon.LogVolumeEvent)
  61. // unmount the container's rootfs
  62. daemon.Unmount(container)
  63. }
  64. }()
  65. if err = daemon.mountVolumes(container); err != nil {
  66. return nil, nil, err
  67. }
  68. // Normalize path before sending to rootfs
  69. path = filepath.FromSlash(path)
  70. resolvedPath, absPath, err := container.ResolvePath(path)
  71. if err != nil {
  72. return nil, nil, err
  73. }
  74. stat, err = container.StatPath(resolvedPath, absPath)
  75. if err != nil {
  76. return nil, nil, err
  77. }
  78. // We need to rebase the archive entries if the last element of the
  79. // resolved path was a symlink that was evaluated and is now different
  80. // than the requested path. For example, if the given path was "/foo/bar/",
  81. // but it resolved to "/var/lib/docker/containers/{id}/foo/baz/", we want
  82. // to ensure that the archive entries start with "bar" and not "baz". This
  83. // also catches the case when the root directory of the container is
  84. // requested: we want the archive entries to start with "/" and not the
  85. // container ID.
  86. // Get the source and the base paths of the container resolved path in order
  87. // to get the proper tar options for the rebase tar.
  88. resolvedPath = filepath.Clean(resolvedPath)
  89. if filepath.Base(resolvedPath) == "." {
  90. resolvedPath += string(filepath.Separator) + "."
  91. }
  92. sourceDir := resolvedPath
  93. sourceBase := "."
  94. if stat.Mode&os.ModeDir == 0 { // not dir
  95. sourceDir, sourceBase = filepath.Split(resolvedPath)
  96. }
  97. opts := archive.TarResourceRebaseOpts(sourceBase, filepath.Base(absPath))
  98. data, err := chrootarchive.Tar(sourceDir, opts, container.BaseFS)
  99. if err != nil {
  100. return nil, nil, err
  101. }
  102. content = ioutils.NewReadCloserWrapper(data, func() error {
  103. err := data.Close()
  104. container.DetachAndUnmount(daemon.LogVolumeEvent)
  105. daemon.Unmount(container)
  106. container.Unlock()
  107. return err
  108. })
  109. daemon.LogContainerEvent(container, "archive-path")
  110. return content, stat, nil
  111. }
  112. // containerExtractToDir extracts the given tar archive to the specified location in the
  113. // filesystem of this container. The given path must be of a directory in the
  114. // container. If it is not, the error will be an errdefs.InvalidParameter. If
  115. // noOverwriteDirNonDir is true then it will be an error if unpacking the
  116. // given content would cause an existing directory to be replaced with a non-
  117. // directory and vice versa.
  118. func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) (err error) {
  119. container.Lock()
  120. defer container.Unlock()
  121. if err = daemon.Mount(container); err != nil {
  122. return err
  123. }
  124. defer daemon.Unmount(container)
  125. err = daemon.mountVolumes(container)
  126. defer container.DetachAndUnmount(daemon.LogVolumeEvent)
  127. if err != nil {
  128. return err
  129. }
  130. // Normalize path before sending to rootfs'
  131. path = filepath.FromSlash(path)
  132. // Check if a drive letter supplied, it must be the system drive. No-op except on Windows
  133. path, err = system.CheckSystemDriveAndRemoveDriveLetter(path)
  134. if err != nil {
  135. return err
  136. }
  137. // The destination path needs to be resolved to a host path, with all
  138. // symbolic links followed in the scope of the container's rootfs. Note
  139. // that we do not use `container.ResolvePath(path)` here because we need
  140. // to also evaluate the last path element if it is a symlink. This is so
  141. // that you can extract an archive to a symlink that points to a directory.
  142. // Consider the given path as an absolute path in the container.
  143. absPath := archive.PreserveTrailingDotOrSeparator(filepath.Join(string(filepath.Separator), path), path)
  144. // This will evaluate the last path element if it is a symlink.
  145. resolvedPath, err := container.GetResourcePath(absPath)
  146. if err != nil {
  147. return err
  148. }
  149. stat, err := os.Lstat(resolvedPath)
  150. if err != nil {
  151. return err
  152. }
  153. if !stat.IsDir() {
  154. return errdefs.InvalidParameter(errors.New("extraction point is not a directory"))
  155. }
  156. // Need to check if the path is in a volume. If it is, it cannot be in a
  157. // read-only volume. If it is not in a volume, the container cannot be
  158. // configured with a read-only rootfs.
  159. // Use the resolved path relative to the container rootfs as the new
  160. // absPath. This way we fully follow any symlinks in a volume that may
  161. // lead back outside the volume.
  162. //
  163. // The Windows implementation of filepath.Rel in golang 1.4 does not
  164. // support volume style file path semantics. On Windows when using the
  165. // filter driver, we are guaranteed that the path will always be
  166. // a volume file path.
  167. var baseRel string
  168. if strings.HasPrefix(resolvedPath, `\\?\Volume{`) {
  169. if strings.HasPrefix(resolvedPath, container.BaseFS) {
  170. baseRel = resolvedPath[len(container.BaseFS):]
  171. if baseRel[:1] == `\` {
  172. baseRel = baseRel[1:]
  173. }
  174. }
  175. } else {
  176. baseRel, err = filepath.Rel(container.BaseFS, resolvedPath)
  177. }
  178. if err != nil {
  179. return err
  180. }
  181. // Make it an absolute path.
  182. absPath = filepath.Join(string(filepath.Separator), baseRel)
  183. toVolume, err := checkIfPathIsInAVolume(container, absPath)
  184. if err != nil {
  185. return err
  186. }
  187. if !toVolume && container.HostConfig.ReadonlyRootfs {
  188. return errdefs.InvalidParameter(errors.New("container rootfs is marked read-only"))
  189. }
  190. options := daemon.defaultTarCopyOptions(noOverwriteDirNonDir)
  191. if copyUIDGID {
  192. var err error
  193. // tarCopyOptions will appropriately pull in the right uid/gid for the
  194. // user/group and will set the options.
  195. options, err = daemon.tarCopyOptions(container, noOverwriteDirNonDir)
  196. if err != nil {
  197. return err
  198. }
  199. }
  200. if err := chrootarchive.UntarWithRoot(content, resolvedPath, options, container.BaseFS); err != nil {
  201. return err
  202. }
  203. daemon.LogContainerEvent(container, "extract-to-dir")
  204. return nil
  205. }
  206. func (daemon *Daemon) containerCopy(container *container.Container, resource string) (rc io.ReadCloser, err error) {
  207. if resource[0] == '/' || resource[0] == '\\' {
  208. resource = resource[1:]
  209. }
  210. container.Lock()
  211. defer func() {
  212. if err != nil {
  213. // Wait to unlock the container until the archive is fully read
  214. // (see the ReadCloseWrapper func below) or if there is an error
  215. // before that occurs.
  216. container.Unlock()
  217. }
  218. }()
  219. if err := daemon.Mount(container); err != nil {
  220. return nil, err
  221. }
  222. defer func() {
  223. if err != nil {
  224. // unmount any volumes
  225. container.DetachAndUnmount(daemon.LogVolumeEvent)
  226. // unmount the container's rootfs
  227. daemon.Unmount(container)
  228. }
  229. }()
  230. if err := daemon.mountVolumes(container); err != nil {
  231. return nil, err
  232. }
  233. // Normalize path before sending to rootfs
  234. resource = filepath.FromSlash(resource)
  235. basePath, err := container.GetResourcePath(resource)
  236. if err != nil {
  237. return nil, err
  238. }
  239. stat, err := os.Stat(basePath)
  240. if err != nil {
  241. return nil, err
  242. }
  243. var filter []string
  244. if !stat.IsDir() {
  245. d, f := filepath.Split(basePath)
  246. basePath = d
  247. filter = []string{f}
  248. }
  249. archv, err := chrootarchive.Tar(basePath, &archive.TarOptions{
  250. Compression: archive.Uncompressed,
  251. IncludeFiles: filter,
  252. }, container.BaseFS)
  253. if err != nil {
  254. return nil, err
  255. }
  256. reader := ioutils.NewReadCloserWrapper(archv, func() error {
  257. err := archv.Close()
  258. container.DetachAndUnmount(daemon.LogVolumeEvent)
  259. daemon.Unmount(container)
  260. container.Unlock()
  261. return err
  262. })
  263. daemon.LogContainerEvent(container, "copy")
  264. return reader, nil
  265. }
  266. // checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
  267. // cannot be in a read-only volume. If it is not in a volume, the container
  268. // cannot be configured with a read-only rootfs.
  269. func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) {
  270. var toVolume bool
  271. parser := volumemounts.NewParser()
  272. for _, mnt := range container.MountPoints {
  273. if toVolume = parser.HasResource(mnt, absPath); toVolume {
  274. if mnt.RW {
  275. break
  276. }
  277. return false, errdefs.InvalidParameter(errors.New("mounted volume is marked read-only"))
  278. }
  279. }
  280. return toVolume, nil
  281. }