This way we can reuse it for Untar()
Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"os/exec" |
| 14 | 14 |
"path" |
| 15 | 15 |
"path/filepath" |
| 16 |
+ "syscall" |
|
| 16 | 17 |
) |
| 17 | 18 |
|
| 18 | 19 |
type Archive io.Reader |
| ... | ... |
@@ -124,6 +125,84 @@ func (compression *Compression) Extension() string {
|
| 124 | 124 |
return "" |
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 |
+func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader) error {
|
|
| 128 |
+ switch hdr.Typeflag {
|
|
| 129 |
+ case tar.TypeDir: |
|
| 130 |
+ // Create directory unless it exists as a directory already. |
|
| 131 |
+ // In that case we just want to merge the two |
|
| 132 |
+ if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
|
|
| 133 |
+ if err := os.Mkdir(path, os.FileMode(hdr.Mode)); err != nil {
|
|
| 134 |
+ return err |
|
| 135 |
+ } |
|
| 136 |
+ } |
|
| 137 |
+ |
|
| 138 |
+ case tar.TypeReg, tar.TypeRegA: |
|
| 139 |
+ // Source is regular file |
|
| 140 |
+ file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode)) |
|
| 141 |
+ if err != nil {
|
|
| 142 |
+ return err |
|
| 143 |
+ } |
|
| 144 |
+ if _, err := io.Copy(file, reader); err != nil {
|
|
| 145 |
+ file.Close() |
|
| 146 |
+ return err |
|
| 147 |
+ } |
|
| 148 |
+ file.Close() |
|
| 149 |
+ |
|
| 150 |
+ case tar.TypeBlock, tar.TypeChar, tar.TypeFifo: |
|
| 151 |
+ mode := uint32(hdr.Mode & 07777) |
|
| 152 |
+ switch hdr.Typeflag {
|
|
| 153 |
+ case tar.TypeBlock: |
|
| 154 |
+ mode |= syscall.S_IFBLK |
|
| 155 |
+ case tar.TypeChar: |
|
| 156 |
+ mode |= syscall.S_IFCHR |
|
| 157 |
+ case tar.TypeFifo: |
|
| 158 |
+ mode |= syscall.S_IFIFO |
|
| 159 |
+ } |
|
| 160 |
+ |
|
| 161 |
+ if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
|
|
| 162 |
+ return err |
|
| 163 |
+ } |
|
| 164 |
+ |
|
| 165 |
+ case tar.TypeLink: |
|
| 166 |
+ if err := os.Link(filepath.Join(extractDir, hdr.Linkname), path); err != nil {
|
|
| 167 |
+ return err |
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 170 |
+ case tar.TypeSymlink: |
|
| 171 |
+ if err := os.Symlink(hdr.Linkname, path); err != nil {
|
|
| 172 |
+ return err |
|
| 173 |
+ } |
|
| 174 |
+ |
|
| 175 |
+ default: |
|
| 176 |
+ return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag)
|
|
| 177 |
+ } |
|
| 178 |
+ |
|
| 179 |
+ if err := syscall.Lchown(path, hdr.Uid, hdr.Gid); err != nil {
|
|
| 180 |
+ return err |
|
| 181 |
+ } |
|
| 182 |
+ |
|
| 183 |
+ // There is no LChmod, so ignore mode for symlink. Also, this |
|
| 184 |
+ // must happen after chown, as that can modify the file mode |
|
| 185 |
+ if hdr.Typeflag != tar.TypeSymlink {
|
|
| 186 |
+ if err := syscall.Chmod(path, uint32(hdr.Mode&07777)); err != nil {
|
|
| 187 |
+ return err |
|
| 188 |
+ } |
|
| 189 |
+ } |
|
| 190 |
+ |
|
| 191 |
+ ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
|
| 192 |
+ // syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and |
|
| 193 |
+ if hdr.Typeflag != tar.TypeSymlink {
|
|
| 194 |
+ if err := syscall.UtimesNano(path, ts); err != nil {
|
|
| 195 |
+ return err |
|
| 196 |
+ } |
|
| 197 |
+ } else {
|
|
| 198 |
+ if err := LUtimesNano(path, ts); err != nil {
|
|
| 199 |
+ return err |
|
| 200 |
+ } |
|
| 201 |
+ } |
|
| 202 |
+ return nil |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 127 | 205 |
// Tar creates an archive from the directory at `path`, and returns it as a |
| 128 | 206 |
// stream of bytes. |
| 129 | 207 |
func Tar(path string, compression Compression) (io.Reader, error) {
|
| ... | ... |
@@ -2,7 +2,6 @@ package archive |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"archive/tar" |
| 5 |
- "github.com/dotcloud/docker/utils" |
|
| 6 | 5 |
"io" |
| 7 | 6 |
"os" |
| 8 | 7 |
"path/filepath" |
| ... | ... |
@@ -89,95 +88,22 @@ func ApplyLayer(dest string, layer Archive) error {
|
| 89 | 89 |
// The only exception is when it is a directory *and* the file from |
| 90 | 90 |
// the layer is also a directory. Then we want to merge them (i.e. |
| 91 | 91 |
// just apply the metadata from the layer). |
| 92 |
- hasDir := false |
|
| 93 | 92 |
if fi, err := os.Lstat(path); err == nil {
|
| 94 |
- if fi.IsDir() && hdr.Typeflag == tar.TypeDir {
|
|
| 95 |
- hasDir = true |
|
| 96 |
- } else {
|
|
| 93 |
+ if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
|
|
| 97 | 94 |
if err := os.RemoveAll(path); err != nil {
|
| 98 | 95 |
return err |
| 99 | 96 |
} |
| 100 | 97 |
} |
| 101 | 98 |
} |
| 102 | 99 |
|
| 103 |
- switch hdr.Typeflag {
|
|
| 104 |
- case tar.TypeDir: |
|
| 105 |
- if !hasDir {
|
|
| 106 |
- err = os.Mkdir(path, os.FileMode(hdr.Mode)) |
|
| 107 |
- if err != nil {
|
|
| 108 |
- return err |
|
| 109 |
- } |
|
| 110 |
- } |
|
| 111 |
- dirs = append(dirs, hdr) |
|
| 112 |
- |
|
| 113 |
- case tar.TypeReg, tar.TypeRegA: |
|
| 114 |
- // Source is regular file |
|
| 115 |
- file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode)) |
|
| 116 |
- if err != nil {
|
|
| 117 |
- return err |
|
| 118 |
- } |
|
| 119 |
- if _, err := io.Copy(file, tr); err != nil {
|
|
| 120 |
- file.Close() |
|
| 121 |
- return err |
|
| 122 |
- } |
|
| 123 |
- file.Close() |
|
| 124 |
- |
|
| 125 |
- case tar.TypeBlock, tar.TypeChar, tar.TypeFifo: |
|
| 126 |
- mode := uint32(hdr.Mode & 07777) |
|
| 127 |
- switch hdr.Typeflag {
|
|
| 128 |
- case tar.TypeBlock: |
|
| 129 |
- mode |= syscall.S_IFBLK |
|
| 130 |
- case tar.TypeChar: |
|
| 131 |
- mode |= syscall.S_IFCHR |
|
| 132 |
- case tar.TypeFifo: |
|
| 133 |
- mode |= syscall.S_IFIFO |
|
| 134 |
- } |
|
| 135 |
- |
|
| 136 |
- if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
|
|
| 137 |
- return err |
|
| 138 |
- } |
|
| 139 |
- |
|
| 140 |
- case tar.TypeLink: |
|
| 141 |
- if err := os.Link(filepath.Join(dest, hdr.Linkname), path); err != nil {
|
|
| 142 |
- return err |
|
| 143 |
- } |
|
| 144 |
- |
|
| 145 |
- case tar.TypeSymlink: |
|
| 146 |
- if err := os.Symlink(hdr.Linkname, path); err != nil {
|
|
| 147 |
- return err |
|
| 148 |
- } |
|
| 149 |
- |
|
| 150 |
- default: |
|
| 151 |
- utils.Debugf("unhandled type %d\n", hdr.Typeflag)
|
|
| 152 |
- } |
|
| 153 |
- |
|
| 154 |
- if err = syscall.Lchown(path, hdr.Uid, hdr.Gid); err != nil {
|
|
| 100 |
+ if err := createTarFile(path, dest, hdr, tr); err != nil {
|
|
| 155 | 101 |
return err |
| 156 | 102 |
} |
| 157 | 103 |
|
| 158 |
- // There is no LChmod, so ignore mode for symlink. Also, this |
|
| 159 |
- // must happen after chown, as that can modify the file mode |
|
| 160 |
- if hdr.Typeflag != tar.TypeSymlink {
|
|
| 161 |
- err = syscall.Chmod(path, uint32(hdr.Mode&07777)) |
|
| 162 |
- if err != nil {
|
|
| 163 |
- return err |
|
| 164 |
- } |
|
| 165 |
- } |
|
| 166 |
- |
|
| 167 |
- // Directories must be handled at the end to avoid further |
|
| 168 |
- // file creation in them to modify the mtime |
|
| 169 |
- if hdr.Typeflag != tar.TypeDir {
|
|
| 170 |
- ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
|
| 171 |
- // syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and |
|
| 172 |
- if hdr.Typeflag != tar.TypeSymlink {
|
|
| 173 |
- if err := syscall.UtimesNano(path, ts); err != nil {
|
|
| 174 |
- return err |
|
| 175 |
- } |
|
| 176 |
- } else {
|
|
| 177 |
- if err := LUtimesNano(path, ts); err != nil {
|
|
| 178 |
- return err |
|
| 179 |
- } |
|
| 180 |
- } |
|
| 104 |
+ // Directory mtimes must be handled at the end to avoid further |
|
| 105 |
+ // file creation in them to modify the directory mtime |
|
| 106 |
+ if hdr.Typeflag == tar.TypeDir {
|
|
| 107 |
+ dirs = append(dirs, hdr) |
|
| 181 | 108 |
} |
| 182 | 109 |
} |
| 183 | 110 |
} |