Repository: docker/docker-py

Issue:
Build context (.tar) is not prepared properly
Hey,

This morning I've updated to version `3.1.1` however, using this version i'm getting wired error from docker-engine build:
```
ERROR: Error processing tar file(exit status 1): mkdir /foodir/bardir: no such file or directory
```
and the actual building does not start.
took me some time to realise this is related to update i got this morning, 
Reverting back to version `3.0.1`, I could build again.

*NOTE*: `/foodir/bardir` is censored  due to security policy in my company,
so for the sake of this issue, lets assume this is the context:
- Dockerfile
- foodir
  - bardir
    - file

Also, path in error did start with `/` so i kept it there.



Discussion:
I'm not able to reproduce the issue with the example you describe:
```
$ tree 
.
├── Dockerfile
└── foo
    └── bar
        └── baz.txt

2 directories, 2 files
$ ipython
[...]
In [1]: import docker

In [2]: docker.version
Out[2]: '3.1.1'

In [3]: c = docker.APIClient()

In [4]: out = c.build('.')

In [5]: for x in out:
   ...:     print(x)
   ...:     
{"stream":"Step 1/2 : FROM busybox"}

{"stream":"\n"}

{"stream":" ---\u003e 5b0d59026729\n"}

{"stream":"Step 2/2 : ADD . /"}

{"stream":"\n"}

{"stream":" ---\u003e 1f811cf2c4b2\n"}

{"aux":{"ID":"sha256:1f811cf2c4b2a26603b27d4980ea50e94d0c2262dc43cbcfd8a7740b5ffdbdbc"}}

{"stream":"Successfully built 1f811cf2c4b2\n"}
```

Is there any more information you could provide that could help us diagnose this? Thanks!
Well, it had much much more files then just those few..
I just gave an example of the error i was getting since i couldnt really share what i am building :/
Ill try to see if i can reproduce that with other sources, however, it looks like it is related to context creation on client side (since server/engine did not update)

Is there any changes around this area on the new version?
One thing that could be relevant is that we updated the processing of the  `.dockerignore` file to be more in line with the `docker` CLI (#1914). You might want to check that you're not accidentally excluding a folder that's supposed to be there during your build?
Not intentionally :)
Ill try importing both packages and see if the functions around here returns different values.

Thanks that was very helpful, hope to provide with more info soon :)
Just finished quick review of the patch, i bet thats the root cause for the breaking change in having.
Ill write a small python script to invoke my exculde_patterns om my project with both versions and report back with the findings :)
Helped me a ton! Thanks!
If you give me the .dockerignore and the culprit file, I can assess whether it's included by the current code or not, and whether that's a bug or a feature.

Resolving patch:
diff --git a/docker/utils/build.py b/docker/utils/build.py
--- a/docker/utils/build.py
+++ b/docker/utils/build.py
@@ -93,7 +93,7 @@ def match(p):
         # Whether this file is implicitely included / excluded.
         matched = default if hit is None else hit
         sub = list(filter(lambda p: p[1], sub))
-        if os.path.isdir(cur):
+        if os.path.isdir(cur) and not os.path.islink(cur):
             # Entirely skip directories if there are no chance any subfile will
             # be included.
             if all(not p[0] for p in sub) and not matched:


Test patch:
diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py
--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -1058,6 +1058,21 @@ def tar_test_negative_mtime_bug(self):
             assert tar_data.getnames() == ['th.txt']
             assert tar_data.getmember('th.txt').mtime == -3600
 
+    @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows')
+    def test_tar_directory_link(self):
+        dirs = ['a', 'b', 'a/c']
+        files = ['a/hello.py', 'b/utils.py', 'a/c/descend.py']
+        base = make_tree(dirs, files)
+        self.addCleanup(shutil.rmtree, base)
+        os.symlink(os.path.join(base, 'b'), os.path.join(base, 'a/c/b'))
+        with tar(base) as archive:
+            tar_data = tarfile.open(fileobj=archive)
+            names = tar_data.getnames()
+            for member in dirs + files:
+                assert member in names
+            assert 'a/c/b' in names
+            assert 'a/c/b/utils.py' not in names
+
 
 class FormatEnvironmentTest(unittest.TestCase):
     def test_format_env_binary_unicode_value(self):