Repository: docker-java/docker-java
Issue: Build command fails when symlinks in docker folder are used

My Docker image includes some files, which are symlinks pointing outside of docker folder, e.g.

```
$ ls -l include/
total 0
lrwxrwxrwx 1 dmitry dmitry 29 Mar 30 17:25 python3.5m -> /usr/local/include/python3.5m
```

Native docker client handles this correctly. Java `File.getCanonicalFile()` follows symlinks, so TAR archive sent to Docker daemon contains `usr/local/include/python3.5m/**` instead of expected `include/python3.5m/**`, and as a result I get confusing errors about non-existent files.

I guess replacing `FilePathUtil.relativize(File, File)` with the following code would work, but please check if this behavior does not break other usages.

```
    public static String relativize(File baseDir, File file) {
        try {
            return baseDir.toPath()
                          .toRealPath(LinkOption.NOFOLLOW_LINKS)
                          .relativize(file.toPath().toRealPath(LinkOption.NOFOLLOW_LINKS))
                          .toString();
        } catch (IOException e) {
            throw new DockerClientException(e.getMessage(), e);
        }
    }
```


Changed files:
src/main/java/com/github/dockerjava/core/util/TarDirWalker.java, src/main/java/com/github/dockerjava/core/util/CompressArchiveUtil.java, src/test/java/com/github/dockerjava/core/util/CompressArchiveUtilTest.java

diff --git a/src/main/java/com/github/dockerjava/core/util/CompressArchiveUtil.java b/src/main/java/com/github/dockerjava/core/util/CompressArchiveUtil.java
index 97471d7f..4590abb1 100644
--- a/src/main/java/com/github/dockerjava/core/util/CompressArchiveUtil.java
+++ b/src/main/java/com/github/dockerjava/core/util/CompressArchiveUtil.java
@@ -1,7 +1,6 @@
 package com.github.dockerjava.core.util;
 
 import static com.github.dockerjava.core.util.FilePathUtil.relativize;
-import static java.nio.file.FileVisitOption.FOLLOW_LINKS;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -13,7 +12,6 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.EnumSet;
 import java.util.zip.GZIPOutputStream;
 
 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
@@ -28,14 +26,26 @@ public class CompressArchiveUtil {
         // utility class
     }
 
-    static void putTarEntry(TarArchiveOutputStream tarOutputStream, TarArchiveEntry tarEntry, Path file)
+    static void addFileToTar(TarArchiveOutputStream tarArchiveOutputStream, Path file, String entryName)
             throws IOException {
-        tarEntry.setSize(Files.size(file));
-        tarOutputStream.putArchiveEntry(tarEntry);
-        try (InputStream input = new BufferedInputStream(Files.newInputStream(file))) {
-            ByteStreams.copy(input, tarOutputStream);
-            tarOutputStream.closeArchiveEntry();
+        if (Files.isSymbolicLink(file)) {
+            TarArchiveEntry tarArchiveEntry = new TarArchiveEntry(entryName, TarArchiveEntry.LF_SYMLINK);
+            tarArchiveEntry.setLinkName(Files.readSymbolicLink(file).toString());
+            tarArchiveOutputStream.putArchiveEntry(tarArchiveEntry);
+        } else {
+            TarArchiveEntry tarArchiveEntry = (TarArchiveEntry) tarArchiveOutputStream.createArchiveEntry(file.toFile(),
+                    entryName);
+            if (file.toFile().canExecute()) {
+                tarArchiveEntry.setMode(tarArchiveEntry.getMode() | 0755);
+            }
+            tarArchiveOutputStream.putArchiveEntry(tarArchiveEntry);
+            if (file.toFile().isFile()) {
+                try (InputStream input = new BufferedInputStream(Files.newInputStream(file))) {
+                    ByteStreams.copy(input, tarArchiveOutputStream);
+                }
+            }
         }
+        tarArchiveOutputStream.closeArchiveEntry();
     }
 
     private static TarArchiveOutputStream buildTarStream(Path outputPath, boolean gZipped) throws IOException {
@@ -69,19 +79,14 @@ public class CompressArchiveUtil {
 
         try (TarArchiveOutputStream tarArchiveOutputStream = buildTarStream(outputPath, gZipped)) {
             if (!Files.isDirectory(inputPath)) {
-                TarArchiveEntry tarEntry = new TarArchiveEntry(inputPath.getFileName().toString());
-                if (inputPath.toFile().canExecute()) {
-                    tarEntry.setMode(tarEntry.getMode() | 0755);
-                }
-                putTarEntry(tarArchiveOutputStream, tarEntry, inputPath);
+                addFileToTar(tarArchiveOutputStream, inputPath, inputPath.getFileName().toString());
             } else {
                 Path sourcePath = inputPath;
                 if (!childrenOnly) {
                     // In order to have the dossier as the root entry
                     sourcePath = inputPath.getParent();
                 }
-                Files.walkFileTree(inputPath, EnumSet.of(FOLLOW_LINKS), Integer.MAX_VALUE,
-                        new TarDirWalker(sourcePath, tarArchiveOutputStream));
+                Files.walkFileTree(inputPath, new TarDirWalker(sourcePath, tarArchiveOutputStream));
             }
             tarArchiveOutputStream.flush();
         }
@@ -95,19 +100,10 @@ public class CompressArchiveUtil {
                 new FileOutputStream(tarFile))))) {
             tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
             for (File file : files) {
-                TarArchiveEntry tarEntry = new TarArchiveEntry(file);
-                tarEntry.setName(relativize(base, file));
-
-                if (!file.isDirectory() && file.canExecute()) {
-                    tarEntry.setMode(tarEntry.getMode() | 0755);
-                }
-
-                tos.putArchiveEntry(tarEntry);
-
-                if (!file.isDirectory()) {
-                    FileUtils.copyFile(file, tos);
-                }
-                tos.closeArchiveEntry();
+                // relativize with method using Path otherwise method with File resolves the symlinks
+                // and this is not want we want. If the file is a symlink, the relativized path should
+                // keep the symlink name and not the target it points to.
+                addFileToTar(tos, file.toPath(), relativize(base.toPath(), file.toPath()));
             }
         }
 
diff --git a/src/main/java/com/github/dockerjava/core/util/TarDirWalker.java b/src/main/java/com/github/dockerjava/core/util/TarDirWalker.java
index ec390611..a2b87d7b 100644
--- a/src/main/java/com/github/dockerjava/core/util/TarDirWalker.java
+++ b/src/main/java/com/github/dockerjava/core/util/TarDirWalker.java
@@ -33,14 +33,7 @@ public class TarDirWalker extends SimpleFileVisitor<Path> {
 
     @Override
     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
-        if (attrs.isSymbolicLink()) { // symbolic link to folder
-            return FileVisitResult.CONTINUE;
-        }
-        TarArchiveEntry tarEntry = new TarArchiveEntry(FilePathUtil.relativize(basePath, file));
-        if (file.toFile().canExecute()) {
-            tarEntry.setMode(tarEntry.getMode() | 0755);
-        }
-        CompressArchiveUtil.putTarEntry(tarArchiveOutputStream, tarEntry, file);
+        CompressArchiveUtil.addFileToTar(tarArchiveOutputStream, file, FilePathUtil.relativize(basePath, file));
         return FileVisitResult.CONTINUE;
     }
 
diff --git a/src/test/java/com/github/dockerjava/core/util/CompressArchiveUtilTest.java b/src/test/java/com/github/dockerjava/core/util/CompressArchiveUtilTest.java
index c2b4a6f4..e8d88d24 100644
--- a/src/test/java/com/github/dockerjava/core/util/CompressArchiveUtilTest.java
+++ b/src/test/java/com/github/dockerjava/core/util/CompressArchiveUtilTest.java
@@ -2,7 +2,6 @@ package com.github.dockerjava.core.util;
 
 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -69,7 +68,6 @@ public class CompressArchiveUtilTest {
     }
 
     @Test
-    @Ignore("Symlink creation is broken so test do not pass")
     public void tarWithSymbolicLinkFileAsInput() throws IOException {
         Path archiveSourceFile = tempFolder.getRoot().toPath().resolve("symlinkFile");
         Path linkTargetFile = tempFolder.newFile("link-target").toPath();
@@ -140,7 +138,6 @@ public class CompressArchiveUtilTest {
     }
 
     @Test
-    @Ignore("Symlink creation is broken so test do not pass")
     public void tarWithfolderAsInputAndNestedSymbolicLinkFile() throws Exception {
         Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
         Path linkTargetFile = tempFolder.newFile("link-target").toPath();
@@ -162,7 +159,6 @@ public class CompressArchiveUtilTest {
     }
 
     @Test
-    @Ignore("Symlink creation is broken so test do not pass")
     public void tarWithfolderAsInputAndNestedSymbolicLinkDir() throws Exception {
         Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
         Path linkTargetDir = tempFolder.newFolder("link-target").toPath();
@@ -207,7 +203,6 @@ public class CompressArchiveUtilTest {
     }
 
     @Test
-    @Ignore("Symlink creation is broken so test do not pass")
     public void archiveTARFilesWithSymbolicLinkFile() throws Exception {
         Path linkTargetFile = tempFolder.newFile("link-target").toPath();
         Path symlinkFile = tempFolder.getRoot().toPath().resolve("symlinkFile");
@@ -219,7 +214,6 @@ public class CompressArchiveUtilTest {
     }
 
     @Test
-    @Ignore("Symlink creation is broken so test do not pass")
     public void archiveTARFilesWithSymbolicLinkDir() throws Exception {
         Path linkTargetDir = tempFolder.newFolder("link-target").toPath();
         Path symlinkFile = tempFolder.getRoot().toPath().resolve("symlinkFile");