Repository: jmcgeheeiv/pyfakefs
Pull request #214 (2017-06-16 19:41:14)

Fixed handling of invalid parent paths in CreateDirectory()
- fixed handling of symlinks in MakeDirectories()
- fixed exception thrown on too many symlinks
- see #209


Patch:
diff --git a/CHANGES.md b/CHANGES.md
index df79a96..d12b8ea 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,6 +12,7 @@ The release versions are PyPi releases.
 #### Infrastructure
 
 #### Fixes
+  * Incorrect error handling during directory creation ([#209](../../issues/209)) 
   * Creating files in read-only directory was possible ([#203](../../issues/203))
 
 ## [Version 3.2](https://pypi.python.org/pypi/pyfakefs/3.2)
diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py
index 937b0df..5d3ff7e 100644
--- a/pyfakefs/fake_filesystem.py
+++ b/pyfakefs/fake_filesystem.py
@@ -1498,7 +1498,7 @@ class FakeFilesystem(object):
             return False
         try:
             file_path = self.ResolvePath(file_path)
-        except IOError:
+        except (IOError, OSError):
             return False
         if file_path == self.root.name:
             return True
@@ -1638,7 +1638,7 @@ class FakeFilesystem(object):
                 # cycles.
                 link_depth += 1
                 if link_depth > _MAX_LINK_DEPTH:
-                    raise IOError(errno.EMLINK,
+                    raise OSError(errno.ELOOP,
                                   'Too many levels of symbolic links: \'%s\'' %
                                   _ComponentsToPath(resolved_components))
                 link_path = _FollowLink(resolved_components, current_dir)
@@ -1906,6 +1906,10 @@ class FakeFilesystem(object):
                 current_dir = new_dir
             else:
                 current_dir = directory
+                if directory.st_mode & stat.S_IFLNK == stat.S_IFLNK:
+                    current_dir = self.ResolveObject(directory.contents)
+                if directory.st_mode & stat.S_IFDIR != stat.S_IFDIR:
+                    raise OSError(errno.ENOTDIR, 'Not a directory', current_dir.GetPath())
 
         # set the permission after creating the directories
         # to allow directory creation inside a read-only directory
@@ -2255,7 +2259,8 @@ class FakeFilesystem(object):
         # writeable.
         current_dir = self.root
         for component in path_components:
-            if component not in current_dir.contents:
+            if (component not in current_dir.contents
+                or not isinstance(current_dir.contents, list)):
                 break
             else:
                 current_dir = current_dir.contents[component]
diff --git a/tox.ini b/tox.ini
index b6009d9..2758e78 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist=py26,py27,py33,py34,py35,py36,py37,pypy
+envlist=py26,py27,py33,py34,py35,py36
 
 [testenv]
 deps = -rrequirements.txt


Test patch:
diff --git a/fake_filesystem_test.py b/fake_filesystem_test.py
index a5068cf..b1e8637 100755
--- a/fake_filesystem_test.py
+++ b/fake_filesystem_test.py
@@ -1471,7 +1471,24 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         directory = '%s/plugh' % file_path
         self.filesystem.CreateFile(file_path)
         self.assertTrue(self.filesystem.Exists(file_path))
-        self.assertRaises(Exception, self.os.makedirs, directory)
+        self.assertRaisesOSError(errno.ENOTDIR, self.os.makedirs, directory)
+
+    @unittest.skipIf(TestCase.is_windows and sys.version_info < (3, 3),
+                     'Links are not supported under Windows before Python 3.3')
+    def testMakedirsRaisesIfParentIsBrokenLink(self):
+        link_path = '/broken_link'
+        self.os.symlink('bogus', link_path)
+        self.assertRaisesIOError(errno.ENOENT, self.os.makedirs, link_path + '/newdir')
+
+    @unittest.skipIf(TestCase.is_windows and sys.version_info < (3, 3),
+                     'Links are not supported under Windows before Python 3.3')
+    def testMakedirsRaisesIfParentIsLoopingLink(self):
+        dir_path = '/foo/bar'
+        self.filesystem.CreateDirectory(dir_path)
+        link_target = dir_path + "/link"
+        link_path = link_target + "/link"
+        self.os.symlink(link_path, link_target)
+        self.assertRaisesOSError(errno.ELOOP, self.os.makedirs, link_path)
 
     def testMakedirsRaisesIfAccessDenied(self):
         """makedirs raises exception if access denied."""
@@ -4201,9 +4218,8 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertEqual(3, st.st_atime)
         self.assertEqual(4, st.st_mtime)
 
-    @unittest.skipIf(TestCase.is_windows and sys.version_info < (3, 3),
-                     'Links are not supported under Windows before Python 3.3')
     def testTooManyLinks(self):
+        self.filesystem.is_windows_fs = False
         self.filesystem.CreateLink('!a!loop', 'loop')
         self.assertFalse(self.filesystem.Exists('!a!loop'))