github.com/jogo/docker@v1.7.0-rc1/pkg/archive/diff.go (about) 1 package archive 2 3 import ( 4 "archive/tar" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 "syscall" 12 13 "github.com/docker/docker/pkg/pools" 14 "github.com/docker/docker/pkg/system" 15 ) 16 17 func UnpackLayer(dest string, layer ArchiveReader) (size int64, err error) { 18 tr := tar.NewReader(layer) 19 trBuf := pools.BufioReader32KPool.Get(tr) 20 defer pools.BufioReader32KPool.Put(trBuf) 21 22 var dirs []*tar.Header 23 24 aufsTempdir := "" 25 aufsHardlinks := make(map[string]*tar.Header) 26 27 // Iterate through the files in the archive. 28 for { 29 hdr, err := tr.Next() 30 if err == io.EOF { 31 // end of tar archive 32 break 33 } 34 if err != nil { 35 return 0, err 36 } 37 38 size += hdr.Size 39 40 // Normalize name, for safety and for a simple is-root check 41 hdr.Name = filepath.Clean(hdr.Name) 42 43 if !strings.HasSuffix(hdr.Name, "/") { 44 // Not the root directory, ensure that the parent directory exists. 45 // This happened in some tests where an image had a tarfile without any 46 // parent directories. 47 parent := filepath.Dir(hdr.Name) 48 parentPath := filepath.Join(dest, parent) 49 if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { 50 err = system.MkdirAll(parentPath, 0600) 51 if err != nil { 52 return 0, err 53 } 54 } 55 } 56 57 // Skip AUFS metadata dirs 58 if strings.HasPrefix(hdr.Name, ".wh..wh.") { 59 // Regular files inside /.wh..wh.plnk can be used as hardlink targets 60 // We don't want this directory, but we need the files in them so that 61 // such hardlinks can be resolved. 62 if strings.HasPrefix(hdr.Name, ".wh..wh.plnk") && hdr.Typeflag == tar.TypeReg { 63 basename := filepath.Base(hdr.Name) 64 aufsHardlinks[basename] = hdr 65 if aufsTempdir == "" { 66 if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil { 67 return 0, err 68 } 69 defer os.RemoveAll(aufsTempdir) 70 } 71 if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true); err != nil { 72 return 0, err 73 } 74 } 75 continue 76 } 77 78 path := filepath.Join(dest, hdr.Name) 79 rel, err := filepath.Rel(dest, path) 80 if err != nil { 81 return 0, err 82 } 83 if strings.HasPrefix(rel, "../") { 84 return 0, breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest)) 85 } 86 base := filepath.Base(path) 87 88 if strings.HasPrefix(base, ".wh.") { 89 originalBase := base[len(".wh."):] 90 originalPath := filepath.Join(filepath.Dir(path), originalBase) 91 if err := os.RemoveAll(originalPath); err != nil { 92 return 0, err 93 } 94 } else { 95 // If path exits we almost always just want to remove and replace it. 96 // The only exception is when it is a directory *and* the file from 97 // the layer is also a directory. Then we want to merge them (i.e. 98 // just apply the metadata from the layer). 99 if fi, err := os.Lstat(path); err == nil { 100 if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) { 101 if err := os.RemoveAll(path); err != nil { 102 return 0, err 103 } 104 } 105 } 106 107 trBuf.Reset(tr) 108 srcData := io.Reader(trBuf) 109 srcHdr := hdr 110 111 // Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so 112 // we manually retarget these into the temporary files we extracted them into 113 if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), ".wh..wh.plnk") { 114 linkBasename := filepath.Base(hdr.Linkname) 115 srcHdr = aufsHardlinks[linkBasename] 116 if srcHdr == nil { 117 return 0, fmt.Errorf("Invalid aufs hardlink") 118 } 119 tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename)) 120 if err != nil { 121 return 0, err 122 } 123 defer tmpFile.Close() 124 srcData = tmpFile 125 } 126 127 if err := createTarFile(path, dest, srcHdr, srcData, true); err != nil { 128 return 0, err 129 } 130 131 // Directory mtimes must be handled at the end to avoid further 132 // file creation in them to modify the directory mtime 133 if hdr.Typeflag == tar.TypeDir { 134 dirs = append(dirs, hdr) 135 } 136 } 137 } 138 139 for _, hdr := range dirs { 140 path := filepath.Join(dest, hdr.Name) 141 ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} 142 if err := syscall.UtimesNano(path, ts); err != nil { 143 return 0, err 144 } 145 } 146 147 return size, nil 148 } 149 150 // ApplyLayer parses a diff in the standard layer format from `layer`, and 151 // applies it to the directory `dest`. Returns the size in bytes of the 152 // contents of the layer. 153 func ApplyLayer(dest string, layer ArchiveReader) (int64, error) { 154 dest = filepath.Clean(dest) 155 156 // We need to be able to set any perms 157 oldmask, err := system.Umask(0) 158 if err != nil { 159 return 0, err 160 } 161 defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform 162 163 layer, err = DecompressStream(layer) 164 if err != nil { 165 return 0, err 166 } 167 return UnpackLayer(dest, layer) 168 }