Browse code

archive: Extract createTarFile() from ApplyLayer

This way we can reuse it for Untar()

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)

Alexander Larsson authored on 2013/12/20 19:30:45
Showing 2 changed files
... ...
@@ -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
 	}