| ... | ... |
@@ -23,10 +23,7 @@ type Compression int |
| 23 | 23 |
|
| 24 | 24 |
type TarOptions struct {
|
| 25 | 25 |
Includes []string |
| 26 |
- Excludes []string |
|
| 27 |
- Recursive bool |
|
| 28 | 26 |
Compression Compression |
| 29 |
- CreateFiles []string |
|
| 30 | 27 |
} |
| 31 | 28 |
|
| 32 | 29 |
const ( |
| ... | ... |
@@ -66,7 +63,7 @@ func DetectCompression(source []byte) Compression {
|
| 66 | 66 |
func xzDecompress(archive io.Reader) (io.Reader, error) {
|
| 67 | 67 |
args := []string{"xz", "-d", "-c", "-q"}
|
| 68 | 68 |
|
| 69 |
- return CmdStream(exec.Command(args[0], args[1:]...), archive, nil) |
|
| 69 |
+ return CmdStream(exec.Command(args[0], args[1:]...), archive) |
|
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 | 72 |
func DecompressStream(archive io.Reader) (io.Reader, error) {
|
| ... | ... |
@@ -100,16 +97,20 @@ func DecompressStream(archive io.Reader) (io.Reader, error) {
|
| 100 | 100 |
} |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 |
-func (compression *Compression) Flag() string {
|
|
| 104 |
- switch *compression {
|
|
| 105 |
- case Bzip2: |
|
| 106 |
- return "j" |
|
| 103 |
+func CompressStream(dest io.WriteCloser, compression Compression) (io.WriteCloser, error) {
|
|
| 104 |
+ |
|
| 105 |
+ switch compression {
|
|
| 106 |
+ case Uncompressed: |
|
| 107 |
+ return dest, nil |
|
| 107 | 108 |
case Gzip: |
| 108 |
- return "z" |
|
| 109 |
- case Xz: |
|
| 110 |
- return "J" |
|
| 109 |
+ return gzip.NewWriter(dest), nil |
|
| 110 |
+ case Bzip2, Xz: |
|
| 111 |
+ // archive/bzip2 does not support writing, and there is no xz support at all |
|
| 112 |
+ // However, this is not a problem as docker only currently generates gzipped tars |
|
| 113 |
+ return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
|
| 114 |
+ default: |
|
| 115 |
+ return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
|
| 111 | 116 |
} |
| 112 |
- return "" |
|
| 113 | 117 |
} |
| 114 | 118 |
|
| 115 | 119 |
func (compression *Compression) Extension() string {
|
| ... | ... |
@@ -126,6 +127,59 @@ func (compression *Compression) Extension() string {
|
| 126 | 126 |
return "" |
| 127 | 127 |
} |
| 128 | 128 |
|
| 129 |
+func addTarFile(path, name string, tw *tar.Writer) error {
|
|
| 130 |
+ fi, err := os.Lstat(path) |
|
| 131 |
+ if err != nil {
|
|
| 132 |
+ return err |
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ link := "" |
|
| 136 |
+ if fi.Mode()&os.ModeSymlink != 0 {
|
|
| 137 |
+ if link, err = os.Readlink(path); err != nil {
|
|
| 138 |
+ return err |
|
| 139 |
+ } |
|
| 140 |
+ } |
|
| 141 |
+ |
|
| 142 |
+ hdr, err := tar.FileInfoHeader(fi, link) |
|
| 143 |
+ if err != nil {
|
|
| 144 |
+ return err |
|
| 145 |
+ } |
|
| 146 |
+ |
|
| 147 |
+ if fi.IsDir() && !strings.HasSuffix(name, "/") {
|
|
| 148 |
+ name = name + "/" |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ hdr.Name = name |
|
| 152 |
+ |
|
| 153 |
+ stat, ok := fi.Sys().(*syscall.Stat_t) |
|
| 154 |
+ if ok {
|
|
| 155 |
+ // Currently go does not fill in the major/minors |
|
| 156 |
+ if stat.Mode&syscall.S_IFBLK == syscall.S_IFBLK || |
|
| 157 |
+ stat.Mode&syscall.S_IFCHR == syscall.S_IFCHR {
|
|
| 158 |
+ hdr.Devmajor = int64(major(uint64(stat.Rdev))) |
|
| 159 |
+ hdr.Devminor = int64(minor(uint64(stat.Rdev))) |
|
| 160 |
+ } |
|
| 161 |
+ } |
|
| 162 |
+ |
|
| 163 |
+ if err := tw.WriteHeader(hdr); err != nil {
|
|
| 164 |
+ return err |
|
| 165 |
+ } |
|
| 166 |
+ |
|
| 167 |
+ if hdr.Typeflag == tar.TypeReg {
|
|
| 168 |
+ if file, err := os.Open(path); err != nil {
|
|
| 169 |
+ return err |
|
| 170 |
+ } else {
|
|
| 171 |
+ _, err := io.Copy(tw, file) |
|
| 172 |
+ if err != nil {
|
|
| 173 |
+ return err |
|
| 174 |
+ } |
|
| 175 |
+ file.Close() |
|
| 176 |
+ } |
|
| 177 |
+ } |
|
| 178 |
+ |
|
| 179 |
+ return nil |
|
| 180 |
+} |
|
| 181 |
+ |
|
| 129 | 182 |
func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader) error {
|
| 130 | 183 |
switch hdr.Typeflag {
|
| 131 | 184 |
case tar.TypeDir: |
| ... | ... |
@@ -207,7 +261,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader) |
| 207 | 207 |
// Tar creates an archive from the directory at `path`, and returns it as a |
| 208 | 208 |
// stream of bytes. |
| 209 | 209 |
func Tar(path string, compression Compression) (io.Reader, error) {
|
| 210 |
- return TarFilter(path, &TarOptions{Recursive: true, Compression: compression})
|
|
| 210 |
+ return TarFilter(path, &TarOptions{Compression: compression})
|
|
| 211 | 211 |
} |
| 212 | 212 |
|
| 213 | 213 |
func escapeName(name string) string {
|
| ... | ... |
@@ -228,57 +282,55 @@ func escapeName(name string) string {
|
| 228 | 228 |
|
| 229 | 229 |
// Tar creates an archive from the directory at `path`, only including files whose relative |
| 230 | 230 |
// paths are included in `filter`. If `filter` is nil, then all files are included. |
| 231 |
-func TarFilter(path string, options *TarOptions) (io.Reader, error) {
|
|
| 232 |
- args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"}
|
|
| 233 |
- if options.Includes == nil {
|
|
| 234 |
- options.Includes = []string{"."}
|
|
| 235 |
- } |
|
| 236 |
- args = append(args, "-c"+options.Compression.Flag()) |
|
| 231 |
+func TarFilter(srcPath string, options *TarOptions) (io.Reader, error) {
|
|
| 232 |
+ pipeReader, pipeWriter := io.Pipe() |
|
| 237 | 233 |
|
| 238 |
- for _, exclude := range options.Excludes {
|
|
| 239 |
- args = append(args, fmt.Sprintf("--exclude=%s", exclude))
|
|
| 234 |
+ compressWriter, err := CompressStream(pipeWriter, options.Compression) |
|
| 235 |
+ if err != nil {
|
|
| 236 |
+ return nil, err |
|
| 240 | 237 |
} |
| 241 | 238 |
|
| 242 |
- if !options.Recursive {
|
|
| 243 |
- args = append(args, "--no-recursion") |
|
| 244 |
- } |
|
| 239 |
+ tw := tar.NewWriter(compressWriter) |
|
| 245 | 240 |
|
| 246 |
- files := "" |
|
| 247 |
- for _, f := range options.Includes {
|
|
| 248 |
- files = files + escapeName(f) + "\n" |
|
| 249 |
- } |
|
| 250 |
- |
|
| 251 |
- tmpDir := "" |
|
| 241 |
+ go func() {
|
|
| 242 |
+ // In general we log errors here but ignore them because |
|
| 243 |
+ // during e.g. a diff operation the container can continue |
|
| 244 |
+ // mutating the filesystem and we can see transient errors |
|
| 245 |
+ // from this |
|
| 252 | 246 |
|
| 253 |
- if options.CreateFiles != nil {
|
|
| 254 |
- var err error // Can't use := here or we override the outer tmpDir |
|
| 255 |
- tmpDir, err = ioutil.TempDir("", "docker-tar")
|
|
| 256 |
- if err != nil {
|
|
| 257 |
- return nil, err |
|
| 247 |
+ if options.Includes == nil {
|
|
| 248 |
+ options.Includes = []string{"."}
|
|
| 258 | 249 |
} |
| 259 | 250 |
|
| 260 |
- files = files + "-C" + tmpDir + "\n" |
|
| 261 |
- for _, f := range options.CreateFiles {
|
|
| 262 |
- path := filepath.Join(tmpDir, f) |
|
| 263 |
- err := os.MkdirAll(filepath.Dir(path), 0600) |
|
| 264 |
- if err != nil {
|
|
| 265 |
- return nil, err |
|
| 266 |
- } |
|
| 251 |
+ for _, include := range options.Includes {
|
|
| 252 |
+ filepath.Walk(filepath.Join(srcPath, include), func(filePath string, f os.FileInfo, err error) error {
|
|
| 253 |
+ if err != nil {
|
|
| 254 |
+ utils.Debugf("Tar: Can't stat file %s to tar: %s\n", srcPath, err)
|
|
| 255 |
+ return nil |
|
| 256 |
+ } |
|
| 267 | 257 |
|
| 268 |
- if file, err := os.OpenFile(path, os.O_CREATE, 0600); err != nil {
|
|
| 269 |
- return nil, err |
|
| 270 |
- } else {
|
|
| 271 |
- file.Close() |
|
| 272 |
- } |
|
| 273 |
- files = files + escapeName(f) + "\n" |
|
| 258 |
+ relFilePath, err := filepath.Rel(srcPath, filePath) |
|
| 259 |
+ if err != nil {
|
|
| 260 |
+ return nil |
|
| 261 |
+ } |
|
| 262 |
+ |
|
| 263 |
+ if err := addTarFile(filePath, relFilePath, tw); err != nil {
|
|
| 264 |
+ utils.Debugf("Can't add file %s to tar: %s\n", srcPath, err)
|
|
| 265 |
+ } |
|
| 266 |
+ return nil |
|
| 267 |
+ }) |
|
| 274 | 268 |
} |
| 275 |
- } |
|
| 276 | 269 |
|
| 277 |
- return CmdStream(exec.Command(args[0], args[1:]...), bytes.NewBufferString(files), func() {
|
|
| 278 |
- if tmpDir != "" {
|
|
| 279 |
- _ = os.RemoveAll(tmpDir) |
|
| 270 |
+ // Make sure to check the error on Close. |
|
| 271 |
+ if err := tw.Close(); err != nil {
|
|
| 272 |
+ utils.Debugf("Can't close tar writer: %s\n", err)
|
|
| 280 | 273 |
} |
| 281 |
- }) |
|
| 274 |
+ if err := compressWriter.Close(); err != nil {
|
|
| 275 |
+ utils.Debugf("Can't close compress writer: %s\n", err)
|
|
| 276 |
+ } |
|
| 277 |
+ }() |
|
| 278 |
+ |
|
| 279 |
+ return pipeReader, nil |
|
| 282 | 280 |
} |
| 283 | 281 |
|
| 284 | 282 |
// Untar reads a stream of bytes from `archive`, parses it as a tar archive, |
| ... | ... |
@@ -311,19 +363,6 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
| 311 | 311 |
return err |
| 312 | 312 |
} |
| 313 | 313 |
|
| 314 |
- if options != nil {
|
|
| 315 |
- excludeFile := false |
|
| 316 |
- for _, exclude := range options.Excludes {
|
|
| 317 |
- if strings.HasPrefix(hdr.Name, exclude) {
|
|
| 318 |
- excludeFile = true |
|
| 319 |
- break |
|
| 320 |
- } |
|
| 321 |
- } |
|
| 322 |
- if excludeFile {
|
|
| 323 |
- continue |
|
| 324 |
- } |
|
| 325 |
- } |
|
| 326 |
- |
|
| 327 | 314 |
// Normalize name, for safety and for a simple is-root check |
| 328 | 315 |
hdr.Name = filepath.Clean(hdr.Name) |
| 329 | 316 |
|
| ... | ... |
@@ -378,9 +417,9 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
| 378 | 378 |
// TarUntar is a convenience function which calls Tar and Untar, with |
| 379 | 379 |
// the output of one piped into the other. If either Tar or Untar fails, |
| 380 | 380 |
// TarUntar aborts and returns the error. |
| 381 |
-func TarUntar(src string, filter []string, dst string) error {
|
|
| 382 |
- utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
|
|
| 383 |
- archive, err := TarFilter(src, &TarOptions{Compression: Uncompressed, Includes: filter, Recursive: true})
|
|
| 381 |
+func TarUntar(src string, dst string) error {
|
|
| 382 |
+ utils.Debugf("TarUntar(%s %s)", src, dst)
|
|
| 383 |
+ archive, err := TarFilter(src, &TarOptions{Compression: Uncompressed})
|
|
| 384 | 384 |
if err != nil {
|
| 385 | 385 |
return err |
| 386 | 386 |
} |
| ... | ... |
@@ -417,7 +456,7 @@ func CopyWithTar(src, dst string) error {
|
| 417 | 417 |
return err |
| 418 | 418 |
} |
| 419 | 419 |
utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
| 420 |
- return TarUntar(src, nil, dst) |
|
| 420 |
+ return TarUntar(src, dst) |
|
| 421 | 421 |
} |
| 422 | 422 |
|
| 423 | 423 |
// CopyFileWithTar emulates the behavior of the 'cp' command-line |
| ... | ... |
@@ -480,13 +519,10 @@ func CopyFileWithTar(src, dst string) (err error) {
|
| 480 | 480 |
// CmdStream executes a command, and returns its stdout as a stream. |
| 481 | 481 |
// If the command fails to run or doesn't complete successfully, an error |
| 482 | 482 |
// will be returned, including anything written on stderr. |
| 483 |
-func CmdStream(cmd *exec.Cmd, input io.Reader, atEnd func()) (io.Reader, error) {
|
|
| 483 |
+func CmdStream(cmd *exec.Cmd, input io.Reader) (io.Reader, error) {
|
|
| 484 | 484 |
if input != nil {
|
| 485 | 485 |
stdin, err := cmd.StdinPipe() |
| 486 | 486 |
if err != nil {
|
| 487 |
- if atEnd != nil {
|
|
| 488 |
- atEnd() |
|
| 489 |
- } |
|
| 490 | 487 |
return nil, err |
| 491 | 488 |
} |
| 492 | 489 |
// Write stdin if any |
| ... | ... |
@@ -497,16 +533,10 @@ func CmdStream(cmd *exec.Cmd, input io.Reader, atEnd func()) (io.Reader, error) |
| 497 | 497 |
} |
| 498 | 498 |
stdout, err := cmd.StdoutPipe() |
| 499 | 499 |
if err != nil {
|
| 500 |
- if atEnd != nil {
|
|
| 501 |
- atEnd() |
|
| 502 |
- } |
|
| 503 | 500 |
return nil, err |
| 504 | 501 |
} |
| 505 | 502 |
stderr, err := cmd.StderrPipe() |
| 506 | 503 |
if err != nil {
|
| 507 |
- if atEnd != nil {
|
|
| 508 |
- atEnd() |
|
| 509 |
- } |
|
| 510 | 504 |
return nil, err |
| 511 | 505 |
} |
| 512 | 506 |
pipeR, pipeW := io.Pipe() |
| ... | ... |
@@ -531,9 +561,6 @@ func CmdStream(cmd *exec.Cmd, input io.Reader, atEnd func()) (io.Reader, error) |
| 531 | 531 |
} else {
|
| 532 | 532 |
pipeW.Close() |
| 533 | 533 |
} |
| 534 |
- if atEnd != nil {
|
|
| 535 |
- atEnd() |
|
| 536 |
- } |
|
| 537 | 534 |
}() |
| 538 | 535 |
// Run the command and return the pipe |
| 539 | 536 |
if err := cmd.Start(); err != nil {
|
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
|
| 15 | 15 |
func TestCmdStreamLargeStderr(t *testing.T) {
|
| 16 | 16 |
cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
|
| 17 |
- out, err := CmdStream(cmd, nil, nil) |
|
| 17 |
+ out, err := CmdStream(cmd, nil) |
|
| 18 | 18 |
if err != nil {
|
| 19 | 19 |
t.Fatalf("Failed to start command: %s", err)
|
| 20 | 20 |
} |
| ... | ... |
@@ -35,7 +35,7 @@ func TestCmdStreamLargeStderr(t *testing.T) {
|
| 35 | 35 |
|
| 36 | 36 |
func TestCmdStreamBad(t *testing.T) {
|
| 37 | 37 |
badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
|
| 38 |
- out, err := CmdStream(badCmd, nil, nil) |
|
| 38 |
+ out, err := CmdStream(badCmd, nil) |
|
| 39 | 39 |
if err != nil {
|
| 40 | 40 |
t.Fatalf("Failed to start command: %s", err)
|
| 41 | 41 |
} |
| ... | ... |
@@ -50,7 +50,7 @@ func TestCmdStreamBad(t *testing.T) {
|
| 50 | 50 |
|
| 51 | 51 |
func TestCmdStreamGood(t *testing.T) {
|
| 52 | 52 |
cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
|
| 53 |
- out, err := CmdStream(cmd, nil, nil) |
|
| 53 |
+ out, err := CmdStream(cmd, nil) |
|
| 54 | 54 |
if err != nil {
|
| 55 | 55 |
t.Fatal(err) |
| 56 | 56 |
} |
| ... | ... |
@@ -89,6 +89,16 @@ func tarUntar(t *testing.T, origin string, compression Compression) error {
|
| 89 | 89 |
if _, err := os.Stat(tmp); err != nil {
|
| 90 | 90 |
return err |
| 91 | 91 |
} |
| 92 |
+ |
|
| 93 |
+ changes, err := ChangesDirs(origin, tmp) |
|
| 94 |
+ if err != nil {
|
|
| 95 |
+ return err |
|
| 96 |
+ } |
|
| 97 |
+ |
|
| 98 |
+ if len(changes) != 0 {
|
|
| 99 |
+ t.Fatalf("Unexpected differences after tarUntar: %v", changes)
|
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 92 | 102 |
return nil |
| 93 | 103 |
} |
| 94 | 104 |
|
| ... | ... |
@@ -108,8 +118,6 @@ func TestTarUntar(t *testing.T) {
|
| 108 | 108 |
for _, c := range []Compression{
|
| 109 | 109 |
Uncompressed, |
| 110 | 110 |
Gzip, |
| 111 |
- Bzip2, |
|
| 112 |
- Xz, |
|
| 113 | 111 |
} {
|
| 114 | 112 |
if err := tarUntar(t, origin, c); err != nil {
|
| 115 | 113 |
t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
|
| ... | ... |
@@ -347,70 +347,12 @@ func ExportChanges(dir string, changes []Change) (Archive, error) {
|
| 347 | 347 |
} |
| 348 | 348 |
} else {
|
| 349 | 349 |
path := filepath.Join(dir, change.Path) |
| 350 |
- |
|
| 351 |
- var stat syscall.Stat_t |
|
| 352 |
- if err := syscall.Lstat(path, &stat); err != nil {
|
|
| 353 |
- utils.Debugf("Can't stat source file: %s\n", err)
|
|
| 354 |
- continue |
|
| 355 |
- } |
|
| 356 |
- |
|
| 357 |
- mtim := getLastModification(&stat) |
|
| 358 |
- atim := getLastAccess(&stat) |
|
| 359 |
- hdr := &tar.Header{
|
|
| 360 |
- Name: change.Path[1:], |
|
| 361 |
- Mode: int64(stat.Mode & 07777), |
|
| 362 |
- Uid: int(stat.Uid), |
|
| 363 |
- Gid: int(stat.Gid), |
|
| 364 |
- ModTime: time.Unix(int64(mtim.Sec), int64(mtim.Nsec)), |
|
| 365 |
- AccessTime: time.Unix(int64(atim.Sec), int64(atim.Nsec)), |
|
| 366 |
- } |
|
| 367 |
- |
|
| 368 |
- if stat.Mode&syscall.S_IFDIR == syscall.S_IFDIR {
|
|
| 369 |
- hdr.Typeflag = tar.TypeDir |
|
| 370 |
- } else if stat.Mode&syscall.S_IFLNK == syscall.S_IFLNK {
|
|
| 371 |
- hdr.Typeflag = tar.TypeSymlink |
|
| 372 |
- if link, err := os.Readlink(path); err != nil {
|
|
| 373 |
- utils.Debugf("Can't readlink source file: %s\n", err)
|
|
| 374 |
- continue |
|
| 375 |
- } else {
|
|
| 376 |
- hdr.Linkname = link |
|
| 377 |
- } |
|
| 378 |
- } else if stat.Mode&syscall.S_IFBLK == syscall.S_IFBLK || |
|
| 379 |
- stat.Mode&syscall.S_IFCHR == syscall.S_IFCHR {
|
|
| 380 |
- if stat.Mode&syscall.S_IFBLK == syscall.S_IFBLK {
|
|
| 381 |
- hdr.Typeflag = tar.TypeBlock |
|
| 382 |
- } else {
|
|
| 383 |
- hdr.Typeflag = tar.TypeChar |
|
| 384 |
- } |
|
| 385 |
- hdr.Devmajor = int64(major(uint64(stat.Rdev))) |
|
| 386 |
- hdr.Devminor = int64(minor(uint64(stat.Rdev))) |
|
| 387 |
- } else if stat.Mode&syscall.S_IFIFO == syscall.S_IFIFO || |
|
| 388 |
- stat.Mode&syscall.S_IFSOCK == syscall.S_IFSOCK {
|
|
| 389 |
- hdr.Typeflag = tar.TypeFifo |
|
| 390 |
- } else if stat.Mode&syscall.S_IFREG == syscall.S_IFREG {
|
|
| 391 |
- hdr.Typeflag = tar.TypeReg |
|
| 392 |
- hdr.Size = stat.Size |
|
| 393 |
- } else {
|
|
| 394 |
- utils.Debugf("Unknown file type: %s\n", path)
|
|
| 395 |
- continue |
|
| 396 |
- } |
|
| 397 |
- |
|
| 398 |
- if err := tw.WriteHeader(hdr); err != nil {
|
|
| 399 |
- utils.Debugf("Can't write tar header: %s\n", err)
|
|
| 400 |
- } |
|
| 401 |
- if hdr.Typeflag == tar.TypeReg {
|
|
| 402 |
- if file, err := os.Open(path); err != nil {
|
|
| 403 |
- utils.Debugf("Can't open file: %s\n", err)
|
|
| 404 |
- } else {
|
|
| 405 |
- _, err := io.Copy(tw, file) |
|
| 406 |
- if err != nil {
|
|
| 407 |
- utils.Debugf("Can't copy file: %s\n", err)
|
|
| 408 |
- } |
|
| 409 |
- file.Close() |
|
| 410 |
- } |
|
| 350 |
+ if err := addTarFile(path, change.Path[1:], tw); err != nil {
|
|
| 351 |
+ utils.Debugf("Can't add file %s to tar: %s\n", path, err)
|
|
| 411 | 352 |
} |
| 412 | 353 |
} |
| 413 | 354 |
} |
| 355 |
+ |
|
| 414 | 356 |
// Make sure to check the error on Close. |
| 415 | 357 |
if err := tw.Close(); err != nil {
|
| 416 | 358 |
utils.Debugf("Can't close layer: %s\n", err)
|
| ... | ... |
@@ -1473,7 +1473,6 @@ func (container *Container) Copy(resource string) (archive.Archive, error) {
|
| 1473 | 1473 |
return archive.TarFilter(basePath, &archive.TarOptions{
|
| 1474 | 1474 |
Compression: archive.Uncompressed, |
| 1475 | 1475 |
Includes: filter, |
| 1476 |
- Recursive: true, |
|
| 1477 | 1476 |
}) |
| 1478 | 1477 |
} |
| 1479 | 1478 |
|
| ... | ... |
@@ -225,7 +225,6 @@ func (a *Driver) Get(id string) (string, error) {
|
| 225 | 225 |
// Returns an archive of the contents for the id |
| 226 | 226 |
func (a *Driver) Diff(id string) (archive.Archive, error) {
|
| 227 | 227 |
return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
| 228 |
- Recursive: true, |
|
| 229 | 228 |
Compression: archive.Uncompressed, |
| 230 | 229 |
}) |
| 231 | 230 |
} |
| ... | ... |
@@ -120,7 +120,6 @@ The test suite will also download a small test container, so you will need inter |
| 120 | 120 |
|
| 121 | 121 |
To run properly, docker needs the following software to be installed at runtime: |
| 122 | 122 |
|
| 123 |
-* GNU Tar version 1.26 or later |
|
| 124 | 123 |
* iproute2 version 3.5 or later (build after 2012-05-21), and specifically the "ip" utility |
| 125 | 124 |
* iptables version 1.4 or later |
| 126 | 125 |
* The LXC utility scripts (http://lxc.sourceforge.net) version 0.8 or later |