Fixes a handle leak in DirectoryInfo.EnumerateFileSystemInfos()
authorNiklas Therning <niklas@therning.org>
Mon, 22 Aug 2016 14:54:03 +0000 (16:54 +0200)
committerNiklas Therning <niklas@therning.org>
Tue, 23 Aug 2016 10:05:53 +0000 (12:05 +0200)
When running DirectoryInfo.EnumerateFileSystemInfos() on an empty directory
MonoIO.FindFirst() will return null and the method returns without closing the
handle which is opened by FindFirst(). This patch makes sure the finally block
which closes the handle is run even when FindFirst() returns null.

Also added a call to FindClose() and some more cleanup in the native
implementation of MonoIO.FindFirst() when an allocation fails.

This bug caused an exception in DirectyoryInfoTest.TearDown() and several
other TearDown() methods in the MonoTests.System.IO tests on Windows due to
RemoveDirectory() failing on an empty directory which still had an open handle
associated with it.

mcs/class/corlib/System.IO/DirectoryInfo.cs
mono/metadata/file-io.c

index d25432dcacdf559da372f14a60dde2ff5ca36fbb..205fadc0026b852b25a17c83bdd4cf76aaff0239 100644 (file)
@@ -427,20 +427,20 @@ namespace System.IO {
                static internal IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string full, string searchPattern, SearchOption searchOption)
                {
                        string path_with_pattern = Path.Combine (full, searchPattern);
-                       IntPtr handle;
+                       IntPtr handle = IntPtr.Zero;
                        MonoIOError error;
                        FileAttributes rattr;
                        bool subdirs = searchOption == SearchOption.AllDirectories;
 
                        Path.Validate (full);
                        
-                       string s = MonoIO.FindFirst (full, path_with_pattern, out rattr, out error, out handle);
-                       if (s == null)
-                               yield break;
-                       if (error != 0)
-                               throw MonoIO.GetException (Path.GetDirectoryName (path_with_pattern), (MonoIOError) error);
-
                        try {
+                               string s = MonoIO.FindFirst (full, path_with_pattern, out rattr, out error, out handle);
+                               if (s == null)
+                                       yield break;
+                               if (error != 0)
+                                       throw MonoIO.GetException (Path.GetDirectoryName (path_with_pattern), (MonoIOError) error);
+
                                do {
                                        if (((rattr & FileAttributes.ReparsePoint) == 0)){
                                                if ((rattr & FileAttributes.Directory) != 0)
@@ -455,7 +455,8 @@ namespace System.IO {
 
                                } while ((s = MonoIO.FindNext (handle, out rattr, out error)) != null);
                        } finally {
-                               MonoIO.FindClose (handle);
+                               if (handle != IntPtr.Zero)
+                                       MonoIO.FindClose (handle);
                        }
                }
                
index 9315bdf828fed4df658bc31e68eab9fda9c95f93..be1ad8a1c79df27548f7f97eac879fd0969145e6 100644 (file)
@@ -489,8 +489,13 @@ ves_icall_System_IO_MonoIO_FindFirst (MonoString *path,
        ifh = g_new (IncrementalFind, 1);
        ifh->find_handle = find_handle;
        ifh->utf8_path = mono_string_to_utf8_checked (path, &error);
-       if (mono_error_set_pending_exception (&error))
+       if (mono_error_set_pending_exception (&error)) {
+               MONO_ENTER_GC_SAFE;
+               FindClose (find_handle);
+               MONO_EXIT_GC_SAFE;
+               g_free (ifh);
                return NULL;
+       }
        ifh->domain = mono_domain_get ();
        *handle = ifh;