github.com/rumpl/bof@v23.0.0-rc.2+incompatible/pkg/containerfs/archiver.go (about) 1 package containerfs // import "github.com/docker/docker/pkg/containerfs" 2 3 import ( 4 "archive/tar" 5 "errors" 6 "io" 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/docker/docker/pkg/archive" 12 "github.com/docker/docker/pkg/idtools" 13 "github.com/docker/docker/pkg/system" 14 "github.com/sirupsen/logrus" 15 ) 16 17 // TarFunc provides a function definition for a custom Tar function 18 type TarFunc func(string, *archive.TarOptions) (io.ReadCloser, error) 19 20 // UntarFunc provides a function definition for a custom Untar function 21 type UntarFunc func(io.Reader, string, *archive.TarOptions) error 22 23 // Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction 24 type Archiver struct { 25 SrcDriver Driver 26 DstDriver Driver 27 Tar TarFunc 28 Untar UntarFunc 29 IDMapping idtools.IdentityMapping 30 } 31 32 // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other. 33 // If either Tar or Untar fails, TarUntar aborts and returns the error. 34 func (archiver *Archiver) TarUntar(src, dst string) error { 35 logrus.Debugf("TarUntar(%s %s)", src, dst) 36 tarArchive, err := archiver.Tar(src, &archive.TarOptions{Compression: archive.Uncompressed}) 37 if err != nil { 38 return err 39 } 40 defer tarArchive.Close() 41 options := &archive.TarOptions{ 42 IDMap: archiver.IDMapping, 43 } 44 return archiver.Untar(tarArchive, dst, options) 45 } 46 47 // UntarPath untar a file from path to a destination, src is the source tar file path. 48 func (archiver *Archiver) UntarPath(src, dst string) error { 49 tarArchive, err := archiver.SrcDriver.Open(src) 50 if err != nil { 51 return err 52 } 53 defer tarArchive.Close() 54 options := &archive.TarOptions{ 55 IDMap: archiver.IDMapping, 56 } 57 return archiver.Untar(tarArchive, dst, options) 58 } 59 60 // CopyWithTar creates a tar archive of filesystem path `src`, and 61 // unpacks it at filesystem path `dst`. 62 // The archive is streamed directly with fixed buffering and no 63 // intermediary disk IO. 64 func (archiver *Archiver) CopyWithTar(src, dst string) error { 65 srcSt, err := archiver.SrcDriver.Stat(src) 66 if err != nil { 67 return err 68 } 69 if !srcSt.IsDir() { 70 return archiver.CopyFileWithTar(src, dst) 71 } 72 73 // if this archiver is set up with ID mapping we need to create 74 // the new destination directory with the remapped root UID/GID pair 75 // as owner 76 77 identity := idtools.Identity{UID: archiver.IDMapping.RootPair().UID, GID: archiver.IDMapping.RootPair().GID} 78 79 // Create dst, copy src's content into it 80 if err := idtools.MkdirAllAndChownNew(dst, 0755, identity); err != nil { 81 return err 82 } 83 logrus.Debugf("Calling TarUntar(%s, %s)", src, dst) 84 return archiver.TarUntar(src, dst) 85 } 86 87 // CopyFileWithTar emulates the behavior of the 'cp' command-line 88 // for a single file. It copies a regular file from path `src` to 89 // path `dst`, and preserves all its metadata. 90 func (archiver *Archiver) CopyFileWithTar(src, dst string) (retErr error) { 91 logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst) 92 srcDriver := archiver.SrcDriver 93 dstDriver := archiver.DstDriver 94 95 srcSt, retErr := srcDriver.Stat(src) 96 if retErr != nil { 97 return retErr 98 } 99 100 if srcSt.IsDir() { 101 return errors.New("cannot copy a directory") 102 } 103 104 // Clean up the trailing slash. This must be done in an operating 105 // system specific manner. 106 if dst[len(dst)-1] == dstDriver.Separator() { 107 dst = dstDriver.Join(dst, srcDriver.Base(src)) 108 } 109 110 // The original call was system.MkdirAll, which is just 111 // os.MkdirAll on not-Windows and changed for Windows. 112 if dstDriver.OS() == "windows" { 113 // Now we are WCOW 114 if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil { 115 return err 116 } 117 } else { 118 // We can just use the driver.MkdirAll function 119 if err := dstDriver.MkdirAll(dstDriver.Dir(dst), 0700); err != nil { 120 return err 121 } 122 } 123 124 r, w := io.Pipe() 125 errC := make(chan error, 1) 126 127 go func() { 128 defer close(errC) 129 errC <- func() error { 130 defer w.Close() 131 132 srcF, err := srcDriver.Open(src) 133 if err != nil { 134 return err 135 } 136 defer srcF.Close() 137 138 hdr, err := archive.FileInfoHeaderNoLookups(srcSt, "") 139 if err != nil { 140 return err 141 } 142 hdr.Format = tar.FormatPAX 143 hdr.ModTime = hdr.ModTime.Truncate(time.Second) 144 hdr.AccessTime = time.Time{} 145 hdr.ChangeTime = time.Time{} 146 hdr.Name = dstDriver.Base(dst) 147 if dstDriver.OS() == "windows" { 148 hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) 149 } else { 150 hdr.Mode = int64(os.FileMode(hdr.Mode)) 151 } 152 153 if err := remapIDs(archiver.IDMapping, hdr); err != nil { 154 return err 155 } 156 157 tw := tar.NewWriter(w) 158 defer tw.Close() 159 if err := tw.WriteHeader(hdr); err != nil { 160 return err 161 } 162 if _, err := io.Copy(tw, srcF); err != nil { 163 return err 164 } 165 return nil 166 }() 167 }() 168 defer func() { 169 if err := <-errC; retErr == nil && err != nil { 170 retErr = err 171 } 172 }() 173 174 retErr = archiver.Untar(r, dstDriver.Dir(dst), nil) 175 if retErr != nil { 176 r.CloseWithError(retErr) 177 } 178 return retErr 179 } 180 181 // IdentityMapping returns the IdentityMapping of the archiver. 182 func (archiver *Archiver) IdentityMapping() idtools.IdentityMapping { 183 return archiver.IDMapping 184 } 185 186 func remapIDs(idMapping idtools.IdentityMapping, hdr *tar.Header) error { 187 ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid}) 188 hdr.Uid, hdr.Gid = ids.UID, ids.GID 189 return err 190 } 191 192 // chmodTarEntry is used to adjust the file permissions used in tar header based 193 // on the platform the archival is done. 194 func chmodTarEntry(perm os.FileMode) os.FileMode { 195 // perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.) 196 permPart := perm & os.ModePerm 197 noPermPart := perm &^ os.ModePerm 198 // Add the x bit: make everything +x from windows 199 permPart |= 0111 200 permPart &= 0755 201 202 return noPermPart | permPart 203 }