[w32file] Push win32 specific error to win32 specific implementation (#5665)
[mono.git] / mono / metadata / w32file-win32.c
index df96ee154476d9e9bef3fcc681eee2c772b22a90..6ecb7ff9d5f40c58d05e8c72cff844bb9b1ee634 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * w32file-win32.c: Windows File IO internal calls.
+/**
+ * \file
+ * Windows File IO internal calls.
  *
  * Copyright 2016 Microsoft
  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
@@ -220,34 +221,91 @@ mono_w32file_remove_directory (const gunichar2 *name)
        return res;
 }
 
+/*
+ * GetFileAttributes|Ex () seems to try opening the file, which might lead to sharing violation errors, whereas
+ * FindFirstFile always succeeds.
+ */
 guint32
 mono_w32file_get_attributes (const gunichar2 *name)
 {
        guint32 res;
+       guint32 error;
+       HANDLE find_handle;
+       WIN32_FIND_DATA find_data;
+
        MONO_ENTER_GC_SAFE;
        res = GetFileAttributes (name);
        MONO_EXIT_GC_SAFE;
-       return res;
+
+       if (res != INVALID_FILE_ATTRIBUTES)
+               return res;
+
+       error = GetLastError ();
+       if (error != ERROR_SHARING_VIOLATION)
+               return INVALID_FILE_ATTRIBUTES;
+
+       MONO_ENTER_GC_SAFE;
+       find_handle = FindFirstFile (name, &find_data);
+       MONO_EXIT_GC_SAFE;
+
+       if (find_handle == INVALID_HANDLE_VALUE)
+               return INVALID_FILE_ATTRIBUTES;
+
+       MONO_ENTER_GC_SAFE;
+       FindClose (find_handle);
+       MONO_EXIT_GC_SAFE;
+
+       return find_data.dwFileAttributes;
+}
+
+static gint64
+convert_filetime (const FILETIME *filetime)
+{
+       return (gint64) ((((guint64) filetime->dwHighDateTime) << 32) + filetime->dwLowDateTime);
 }
 
 gboolean
 mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat)
 {
-       gboolean result;
-       WIN32_FILE_ATTRIBUTE_DATA data;
+       gboolean res;
+       guint32 error;
+       HANDLE find_handle;
+       WIN32_FIND_DATA find_data;
+       WIN32_FILE_ATTRIBUTE_DATA file_attribute_data;
 
        MONO_ENTER_GC_SAFE;
-       result = GetFileAttributesEx (name, GetFileExInfoStandard, &data);
+       res = GetFileAttributesEx (name, GetFileExInfoStandard, &file_attribute_data);
        MONO_EXIT_GC_SAFE;
-       if (result) {
-               stat->attributes = data.dwFileAttributes;
-               stat->creation_time = (gint64) ((((guint64) data.ftCreationTime.dwHighDateTime) << 32) + data.ftCreationTime.dwLowDateTime);
-               stat->last_access_time = (gint64) ((((guint64) data.ftLastAccessTime.dwHighDateTime) << 32) + data.ftLastAccessTime.dwLowDateTime);
-               stat->last_write_time = (gint64) ((((guint64) data.ftLastWriteTime.dwHighDateTime) << 32) + data.ftLastWriteTime.dwLowDateTime);
-               stat->length = ((gint64)data.nFileSizeHigh << 32) | data.nFileSizeLow;
+       if (res) {
+               stat->attributes = file_attribute_data.dwFileAttributes;
+               stat->creation_time = convert_filetime (&file_attribute_data.ftCreationTime);
+               stat->last_access_time = convert_filetime (&file_attribute_data.ftLastAccessTime);
+               stat->last_write_time = convert_filetime (&file_attribute_data.ftLastWriteTime);
+               stat->length = ((gint64)file_attribute_data.nFileSizeHigh << 32) | file_attribute_data.nFileSizeLow;
+               return TRUE;
        }
 
-       return result;
+       error = GetLastError ();
+       if (error != ERROR_SHARING_VIOLATION)
+               return FALSE;
+
+       MONO_ENTER_GC_SAFE;
+       find_handle = FindFirstFile (name, &find_data);
+       MONO_EXIT_GC_SAFE;
+
+       if (find_handle == INVALID_HANDLE_VALUE)
+               return FALSE;
+
+       MONO_ENTER_GC_SAFE;
+       FindClose (find_handle);
+       MONO_EXIT_GC_SAFE;
+
+       stat->attributes = find_data.dwFileAttributes;
+       stat->creation_time = convert_filetime (&find_data.ftCreationTime);
+       stat->last_access_time = convert_filetime (&find_data.ftLastAccessTime);
+       stat->last_write_time = convert_filetime (&find_data.ftLastWriteTime);
+       stat->length = ((gint64)find_data.nFileSizeHigh << 32) | find_data.nFileSizeLow;
+       return TRUE;
 }
 
 gboolean
@@ -298,20 +356,20 @@ gboolean
 mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_bytes_avail, guint64 *total_number_of_bytes, guint64 *total_number_of_free_bytes)
 {
        gboolean result;
-       ULARGE_INTEGER *wapi_free_bytes_avail;
-       ULARGE_INTEGER *wapi_total_number_of_bytes;
-       ULARGE_INTEGER *wapi_total_number_of_free_bytes;
+       ULARGE_INTEGER wapi_free_bytes_avail;
+       ULARGE_INTEGER wapi_total_number_of_bytes;
+       ULARGE_INTEGER wapi_total_number_of_free_bytes;
 
        MONO_ENTER_GC_SAFE;
-       result = GetDiskFreeSpaceEx (path_name, wapi_free_bytes_avail, wapi_total_number_of_bytes, wapi_total_number_of_free_bytes);
+       result = GetDiskFreeSpaceEx (path_name, &wapi_free_bytes_avail, &wapi_total_number_of_bytes, &wapi_total_number_of_free_bytes);
        MONO_EXIT_GC_SAFE;
        if (result) {
                if (free_bytes_avail)
-                       *free_bytes_avail = wapi_free_bytes_avail->QuadPart;
+                       *free_bytes_avail = wapi_free_bytes_avail.QuadPart;
                if (total_number_of_bytes)
-                       *total_number_of_bytes = wapi_total_number_of_bytes->QuadPart;
+                       *total_number_of_bytes = wapi_total_number_of_bytes.QuadPart;
                if (total_number_of_free_bytes)
-                       *total_number_of_free_bytes = wapi_total_number_of_free_bytes->QuadPart;
+                       *total_number_of_free_bytes = wapi_total_number_of_free_bytes.QuadPart;
        }
 
        return result;