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