github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/daemon/archive_unix.go (about) 1 //go:build !windows 2 3 package daemon // import "github.com/docker/docker/daemon" 4 5 import ( 6 "context" 7 "io" 8 "os" 9 "path/filepath" 10 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/events" 13 "github.com/docker/docker/container" 14 "github.com/docker/docker/errdefs" 15 "github.com/docker/docker/pkg/archive" 16 "github.com/docker/docker/pkg/ioutils" 17 volumemounts "github.com/docker/docker/volume/mounts" 18 "github.com/pkg/errors" 19 ) 20 21 // containerStatPath stats the filesystem resource at the specified path in this 22 // container. Returns stat info about the resource. 23 func (daemon *Daemon) containerStatPath(container *container.Container, path string) (stat *types.ContainerPathStat, err error) { 24 container.Lock() 25 defer container.Unlock() 26 27 cfs, err := daemon.openContainerFS(container) 28 if err != nil { 29 return nil, err 30 } 31 defer cfs.Close() 32 33 return cfs.Stat(context.TODO(), path) 34 } 35 36 // containerArchivePath creates an archive of the filesystem resource at the specified 37 // path in this container. Returns a tar archive of the resource and stat info 38 // about the resource. 39 func (daemon *Daemon) containerArchivePath(container *container.Container, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) { 40 container.Lock() 41 42 defer func() { 43 if err != nil { 44 // Wait to unlock the container until the archive is fully read 45 // (see the ReadCloseWrapper func below) or if there is an error 46 // before that occurs. 47 container.Unlock() 48 } 49 }() 50 51 cfs, err := daemon.openContainerFS(container) 52 if err != nil { 53 return nil, nil, err 54 } 55 56 defer func() { 57 if err != nil { 58 cfs.Close() 59 } 60 }() 61 62 absPath := archive.PreserveTrailingDotOrSeparator(filepath.Join("/", path), path) 63 64 stat, err = cfs.Stat(context.TODO(), absPath) 65 if err != nil { 66 return nil, nil, err 67 } 68 69 sourceDir, sourceBase := absPath, "." 70 if stat.Mode&os.ModeDir == 0 { // not dir 71 sourceDir, sourceBase = filepath.Split(absPath) 72 } 73 opts := archive.TarResourceRebaseOpts(sourceBase, filepath.Base(absPath)) 74 75 tb, err := archive.NewTarballer(sourceDir, opts) 76 if err != nil { 77 return nil, nil, err 78 } 79 80 cfs.GoInFS(context.TODO(), tb.Do) 81 data := tb.Reader() 82 content = ioutils.NewReadCloserWrapper(data, func() error { 83 err := data.Close() 84 _ = cfs.Close() 85 container.Unlock() 86 return err 87 }) 88 89 daemon.LogContainerEvent(container, events.ActionArchivePath) 90 91 return content, stat, nil 92 } 93 94 // containerExtractToDir extracts the given tar archive to the specified location in the 95 // filesystem of this container. The given path must be of a directory in the 96 // container. If it is not, the error will be an errdefs.InvalidParameter. If 97 // noOverwriteDirNonDir is true then it will be an error if unpacking the 98 // given content would cause an existing directory to be replaced with a non- 99 // directory and vice versa. 100 func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) (err error) { 101 container.Lock() 102 defer container.Unlock() 103 104 cfs, err := daemon.openContainerFS(container) 105 if err != nil { 106 return err 107 } 108 defer cfs.Close() 109 110 err = cfs.RunInFS(context.TODO(), func() error { 111 // The destination path needs to be resolved with all symbolic links 112 // followed. Note that we need to also evaluate the last path element if 113 // it is a symlink. This is so that you can extract an archive to a 114 // symlink that points to a directory. 115 absPath, err := filepath.EvalSymlinks(filepath.Join("/", path)) 116 if err != nil { 117 return err 118 } 119 absPath = archive.PreserveTrailingDotOrSeparator(absPath, path) 120 121 stat, err := os.Lstat(absPath) 122 if err != nil { 123 return err 124 } 125 if !stat.IsDir() { 126 return errdefs.InvalidParameter(errors.New("extraction point is not a directory")) 127 } 128 129 // Need to check if the path is in a volume. If it is, it cannot be in a 130 // read-only volume. If it is not in a volume, the container cannot be 131 // configured with a read-only rootfs. 132 toVolume, err := checkIfPathIsInAVolume(container, absPath) 133 if err != nil { 134 return err 135 } 136 137 if !toVolume && container.HostConfig.ReadonlyRootfs { 138 return errdefs.InvalidParameter(errors.New("container rootfs is marked read-only")) 139 } 140 141 options := daemon.defaultTarCopyOptions(noOverwriteDirNonDir) 142 143 if copyUIDGID { 144 var err error 145 // tarCopyOptions will appropriately pull in the right uid/gid for the 146 // user/group and will set the options. 147 options, err = daemon.tarCopyOptions(container, noOverwriteDirNonDir) 148 if err != nil { 149 return err 150 } 151 } 152 153 return archive.Untar(content, absPath, options) 154 }) 155 if err != nil { 156 return err 157 } 158 159 daemon.LogContainerEvent(container, events.ActionExtractToDir) 160 161 return nil 162 } 163 164 // checkIfPathIsInAVolume checks if the path is in a volume. If it is, it 165 // cannot be in a read-only volume. If it is not in a volume, the container 166 // cannot be configured with a read-only rootfs. 167 func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) { 168 var toVolume bool 169 parser := volumemounts.NewParser() 170 for _, mnt := range container.MountPoints { 171 if toVolume = parser.HasResource(mnt, absPath); toVolume { 172 if mnt.RW { 173 break 174 } 175 return false, errdefs.InvalidParameter(errors.New("mounted volume is marked read-only")) 176 } 177 } 178 return toVolume, nil 179 }