*
* Author:
* Dick Porter (dick@ximian.com)
+ * Gonzalo Paniagua Javier (gonzalo@ximian.com)
*
- * (C) 2001 Ximian, Inc.
+ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
*/
#include <config.h>
+
#include <glib.h>
#include <string.h>
#include <errno.h>
-#include <signal.h>
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
#include <mono/metadata/object.h>
#include <mono/io-layer/io-layer.h>
#include <mono/metadata/exception.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/marshal.h>
+#include <mono/utils/strenc.h>
#undef DEBUG
static guint32 convert_share(MonoFileShare mono_share)
{
- guint32 share;
+ guint32 share = 0;
- switch(mono_share) {
- case FileShare_None:
- share=0;
- break;
- case FileShare_Read:
- share=FILE_SHARE_READ;
- break;
- case FileShare_Write:
- share=FILE_SHARE_WRITE;
- break;
- case FileShare_ReadWrite:
- share=FILE_SHARE_READ|FILE_SHARE_WRITE;
- break;
- default:
+ if (mono_share & FileShare_Read) {
+ share |= FILE_SHARE_READ;
+ }
+ if (mono_share & FileShare_Write) {
+ share |= FILE_SHARE_WRITE;
+ }
+ if (mono_share & FileShare_Delete) {
+ share |= FILE_SHARE_DELETE;
+ }
+
+ if (mono_share & ~(FileShare_Read|FileShare_Write|FileShare_Delete)) {
g_warning("System.IO.FileShare has unknown value 0x%x",
mono_share);
/* Safe fallback */
share=0;
}
-
+
return(share);
}
while (name [len])
++ len;
- stat->name = mono_string_new_utf16 (mono_domain_get (), name, len);
+ MONO_STRUCT_SETREF (stat, name, mono_string_new_utf16 (mono_domain_get (), name, len));
}
/* Managed file attributes have nearly but not quite the same values
return(attrs);
}
+/*
+ * On Win32, GetFileAttributes|Ex () seems to try opening the file,
+ * which might lead to sharing violation errors, whereas FindFirstFile
+ * always succeeds. These 2 wrappers resort to FindFirstFile if
+ * GetFileAttributes|Ex () has failed.
+ */
+static guint32
+get_file_attributes (const gunichar2 *path)
+{
+ guint32 res;
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle;
+ gint32 error;
+
+ res = GetFileAttributes (path);
+ if (res != -1)
+ return res;
+
+ error = GetLastError ();
+
+ if (error != ERROR_SHARING_VIOLATION)
+ return res;
+
+ find_handle = FindFirstFile (path, &find_data);
+
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return res;
+
+ FindClose (find_handle);
+
+ return find_data.dwFileAttributes;
+}
+
+static gboolean
+get_file_attributes_ex (const gunichar2 *path, WIN32_FILE_ATTRIBUTE_DATA *data)
+{
+ gboolean res;
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle;
+ gint32 error;
+
+ res = GetFileAttributesEx (path, GetFileExInfoStandard, data);
+ if (res)
+ return TRUE;
+
+ error = GetLastError ();
+
+ if (error != ERROR_SHARING_VIOLATION)
+ return FALSE;
+
+ find_handle = FindFirstFile (path, &find_data);
+
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ FindClose (find_handle);
+
+ data->dwFileAttributes = find_data.dwFileAttributes;
+ data->ftCreationTime = find_data.ftCreationTime;
+ data->ftLastAccessTime = find_data.ftLastAccessTime;
+ data->ftLastWriteTime = find_data.ftLastWriteTime;
+ data->nFileSizeHigh = find_data.nFileSizeHigh;
+ data->nFileSizeLow = find_data.nFileSizeLow;
+
+ return TRUE;
+}
+
/* System.IO.MonoIO internal calls */
MonoBoolean
return(ret);
}
-HANDLE
-ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path, MonoIOStat *stat,
- gint32 *error)
+MonoArray *
+ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path,
+ MonoString *path_with_pattern,
+ gint attrs, gint mask,
+ gint32 *error)
{
+ MonoDomain *domain;
+ MonoArray *result;
+ int i;
WIN32_FIND_DATA data;
- HANDLE result;
- gboolean r = TRUE;
-
+ HANDLE find_handle;
+ GPtrArray *names;
+ gchar *utf8_path, *utf8_result, *full_name;
+
MONO_ARCH_SAVE_REGS;
- *error=ERROR_SUCCESS;
+ *error = ERROR_SUCCESS;
- result = FindFirstFile (mono_string_chars (path), &data);
+ domain = mono_domain_get ();
+ mask = convert_attrs (mask);
+
+ find_handle = FindFirstFile (mono_string_chars (path_with_pattern),
+ &data);
+ if (find_handle == INVALID_HANDLE_VALUE) {
+ gint32 find_error = GetLastError ();
+
+ if (find_error == ERROR_FILE_NOT_FOUND) {
+ /* No files, so just return an empty array */
+ result = mono_array_new (domain,
+ mono_defaults.string_class,
+ 0);
+
+ return(result);
+ }
+
+ *error = find_error;
+ return(NULL);
+ }
- /* note: WIN32_FIND_DATA is an extension of WIN32_FILE_ATTRIBUTE_DATA */
- while (result != INVALID_HANDLE_VALUE && r) {
- if ((data.cFileName [0] == '.' && data.cFileName [1] == 0) ||
- (data.cFileName [0] == '.' && data.cFileName [1] == '.' && data.cFileName [2] == 0)) {
- r = FindNextFile (result, &data);
- } else {
- convert_win32_file_attribute_data ((const WIN32_FILE_ATTRIBUTE_DATA *)&data,
- &data.cFileName [0], stat);
- break;
- }
+ utf8_path = mono_string_to_utf8 (path); /*If this raises there is not memory to release*/
+ names = g_ptr_array_new ();
+
+ do {
+ if ((data.cFileName[0] == '.' && data.cFileName[1] == 0) ||
+ (data.cFileName[0] == '.' && data.cFileName[1] == '.' && data.cFileName[2] == 0)) {
+ continue;
+ }
+
+ if ((data.dwFileAttributes & mask) == attrs) {
+ utf8_result = g_utf16_to_utf8 (data.cFileName, -1, NULL, NULL, NULL);
+ if (utf8_result == NULL) {
+ continue;
+ }
+
+ full_name = g_build_filename (utf8_path, utf8_result, NULL);
+ g_ptr_array_add (names, full_name);
+
+ g_free (utf8_result);
+ }
+ } while(FindNextFile (find_handle, &data));
+
+ if (FindClose (find_handle) == FALSE) {
+ *error = GetLastError ();
+ result = NULL;
+ } else {
+ result = mono_array_new (domain, mono_defaults.string_class, names->len);
+ for (i = 0; i < names->len; i++) {
+ mono_array_setref (result, i, mono_string_new (domain, g_ptr_array_index (names, i)));
+ }
}
-
- if (result == INVALID_HANDLE_VALUE) {
- *error=GetLastError ();
+
+ for (i = 0; i < names->len; i++) {
+ g_free (g_ptr_array_index (names, i));
}
+ g_ptr_array_free (names, TRUE);
+ g_free (utf8_path);
- if (r == FALSE) {
- /* No more files were found, after we discarded . and .. */
- FindClose(result);
- result = INVALID_HANDLE_VALUE;
- *error = ERROR_NO_MORE_FILES;
- }
-
return result;
}
-MonoBoolean
-ves_icall_System_IO_MonoIO_FindNextFile (HANDLE find, MonoIOStat *stat,
- gint32 *error)
+typedef struct {
+ MonoDomain *domain;
+ gchar *utf8_path;
+ HANDLE find_handle;
+} IncrementalFind;
+
+static gboolean
+incremental_find_check_match (IncrementalFind *handle, WIN32_FIND_DATA *data, MonoString **result)
{
- WIN32_FIND_DATA data;
- gboolean result;
+ gchar *utf8_result;
+ gchar *full_name;
+
+ if ((data->cFileName[0] == '.' && data->cFileName[1] == 0) || (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0))
+ return FALSE;
- MONO_ARCH_SAVE_REGS;
+ utf8_result = g_utf16_to_utf8 (data->cFileName, -1, NULL, NULL, NULL);
+ if (utf8_result == NULL)
+ return FALSE;
+
+ full_name = g_build_filename (handle->utf8_path, utf8_result, NULL);
+ g_free (utf8_result);
+ *result = mono_string_new (mono_domain_get (), full_name);
+ g_free (full_name);
+
+ return TRUE;
+}
- *error=ERROR_SUCCESS;
+MonoString *
+ves_icall_System_IO_MonoIO_FindFirst (MonoString *path,
+ MonoString *path_with_pattern,
+ gint32 *result_attr, gint32 *error,
+ gpointer *handle)
+{
+ WIN32_FIND_DATA data;
+ HANDLE find_handle;
+ IncrementalFind *ifh;
+ MonoString *result;
- result = FindNextFile (find, &data);
- while (result != FALSE) {
- if ((data.cFileName [0] == '.' && data.cFileName [1] == 0) ||
- (data.cFileName [0] == '.' && data.cFileName [1] == '.' && data.cFileName [2] == 0)) {
- result = FindNextFile (find, &data);
- } else {
- convert_win32_file_attribute_data ((const WIN32_FILE_ATTRIBUTE_DATA *)&data,
- &data.cFileName [0], stat);
- break;
- }
- }
+ *error = ERROR_SUCCESS;
- if (result == FALSE) {
- *error=GetLastError ();
+ find_handle = FindFirstFile (mono_string_chars (path_with_pattern), &data);
+
+ if (find_handle == INVALID_HANDLE_VALUE) {
+ gint32 find_error = GetLastError ();
+ *handle = NULL;
+
+ if (find_error == ERROR_FILE_NOT_FOUND)
+ return NULL;
+
+ *error = find_error;
+ return NULL;
}
+ ifh = g_new (IncrementalFind, 1);
+ ifh->find_handle = find_handle;
+ ifh->utf8_path = mono_string_to_utf8 (path);
+ ifh->domain = mono_domain_get ();
+ *handle = ifh;
+
+ while (incremental_find_check_match (ifh, &data, &result) == 0){
+ if (FindNextFile (find_handle, &data) == FALSE){
+ int e = GetLastError ();
+ if (e != ERROR_NO_MORE_FILES)
+ *error = e;
+ return NULL;
+ }
+ }
+ *result_attr = data.dwFileAttributes;
+
return result;
}
-MonoBoolean
-ves_icall_System_IO_MonoIO_FindClose (HANDLE find, gint32 *error)
+MonoString *
+ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *error)
{
- gboolean ret;
-
- MONO_ARCH_SAVE_REGS;
+ IncrementalFind *ifh = handle;
+ WIN32_FIND_DATA data;
+ MonoString *result;
- *error=ERROR_SUCCESS;
-
- ret=FindClose (find);
- if(ret==FALSE) {
- *error=GetLastError ();
- }
+ error = ERROR_SUCCESS;
+ do {
+ if (FindNextFile (ifh->find_handle, &data) == FALSE){
+ int e = GetLastError ();
+ if (e != ERROR_NO_MORE_FILES)
+ *error = e;
+ return NULL;
+ }
+ } while (incremental_find_check_match (ifh, &data, &result) == 0);
+
+ *result_attr = data.dwFileAttributes;
+ return result;
+}
+
+int
+ves_icall_System_IO_MonoIO_FindClose (gpointer handle)
+{
+ IncrementalFind *ifh = handle;
+ gint32 error;
- return(ret);
+ if (FindClose (ifh->find_handle) == FALSE){
+ error = GetLastError ();
+ } else
+ error = ERROR_SUCCESS;
+ g_free (ifh->utf8_path);
+ g_free (ifh);
+
+ return error;
}
MonoString *
{
MonoString *result;
gunichar2 *buf;
- int len;
+ int len, res_len;
MONO_ARCH_SAVE_REGS;
- len = MAX_PATH + 1;
+ len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/
buf = g_new (gunichar2, len);
*error=ERROR_SUCCESS;
result = NULL;
- if (GetCurrentDirectory (len, buf) > 0) {
+ res_len = GetCurrentDirectory (len, buf);
+ if (res_len > len) { /*buf is too small.*/
+ int old_res_len = res_len;
+ g_free (buf);
+ buf = g_new (gunichar2, res_len);
+ res_len = GetCurrentDirectory (res_len, buf) == old_res_len;
+ }
+
+ if (res_len) {
len = 0;
while (buf [len])
++ len;
return(ret);
}
+MonoBoolean
+ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName,
+ MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors,
+ gint32 *error)
+{
+ gboolean ret;
+ gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL;
+ guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH;
+
+ MONO_ARCH_SAVE_REGS;
+
+ if (sourceFileName)
+ utf16_sourceFileName = mono_string_chars (sourceFileName);
+ if (destinationFileName)
+ utf16_destinationFileName = mono_string_chars (destinationFileName);
+ if (destinationBackupFileName)
+ utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName);
+
+ *error = ERROR_SUCCESS;
+ if (ignoreMetadataErrors)
+ replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS;
+
+ ret = ReplaceFile (utf16_destinationFileName, utf16_sourceFileName, utf16_destinationBackupFileName,
+ replaceFlags, NULL, NULL);
+ if (ret == FALSE)
+ *error = GetLastError ();
+
+ return ret;
+}
+
MonoBoolean
ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
MonoBoolean overwrite, gint32 *error)
*error=ERROR_SUCCESS;
- ret=GetFileAttributes (mono_string_chars (path));
+ ret=get_file_attributes (mono_string_chars (path));
/*
* The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
*error=ERROR_SUCCESS;
- result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data);
+ result = get_file_attributes_ex (mono_string_chars (path), &data);
if (result) {
convert_win32_file_attribute_data (&data,
stat);
} else {
*error=GetLastError ();
+ memset (stat, 0, sizeof (MonoIOStat));
}
return result;
HANDLE
ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
- gint32 access_mode, gint32 share,
- MonoBoolean async, gint32 *error)
+ gint32 access_mode, gint32 share, gint32 options,
+ gint32 *error)
{
HANDLE ret;
+ int attributes, attrs;
+ gunichar2 *chars = mono_string_chars (filename);
MONO_ARCH_SAVE_REGS;
*error=ERROR_SUCCESS;
+
+ if (options != 0){
+ if (options & FileOptions_Encrypted)
+ attributes = FILE_ATTRIBUTE_ENCRYPTED;
+ else
+ attributes = FILE_ATTRIBUTE_NORMAL;
+ if (options & FileOptions_DeleteOnClose)
+ attributes |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (options & FileOptions_SequentialScan)
+ attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+ if (options & FileOptions_RandomAccess)
+ attributes |= FILE_FLAG_RANDOM_ACCESS;
+
+ if (options & FileOptions_Temporary)
+ attributes |= FILE_ATTRIBUTE_TEMPORARY;
+
+ /* Not sure if we should set FILE_FLAG_OVERLAPPED, how does this mix with the "Async" bool here? */
+ if (options & FileOptions_Asynchronous)
+ attributes |= FILE_FLAG_OVERLAPPED;
+
+ if (options & FileOptions_WriteThrough)
+ attributes |= FILE_FLAG_WRITE_THROUGH;
+ } else
+ attributes = FILE_ATTRIBUTE_NORMAL;
+
+ /* If we're opening a directory we need to set the extra flag
+ */
+ attrs = get_file_attributes (chars);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
+ attributes |= FILE_FLAG_BACKUP_SEMANTICS;
+ }
+ }
- ret=CreateFile (mono_string_chars (filename),
- convert_access (access_mode), convert_share (share),
- NULL, convert_mode (mode),
- FILE_ATTRIBUTE_NORMAL | ((async) ? FILE_FLAG_OVERLAPPED : 0),
- NULL);
+ ret=CreateFile (chars, convert_access (access_mode),
+ convert_share (share), NULL, convert_mode (mode),
+ attributes, NULL);
if(ret==INVALID_HANDLE_VALUE) {
*error=GetLastError ();
- }
+ }
return(ret);
}
ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
gint32 *error)
{
- guint32 offset_hi;
+ gint32 offset_hi;
MONO_ARCH_SAVE_REGS;
*error=ERROR_SUCCESS;
offset_hi = offset >> 32;
- offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
+ offset = SetFilePointer (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi,
convert_seekorigin (origin));
if(offset==INVALID_SET_FILE_POINTER) {
return(TRUE);
}
+MonoBoolean ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle,
+ HANDLE source_handle, HANDLE target_process_handle, HANDLE *target_handle,
+ gint32 access, gint32 inherit, gint32 options)
+{
+ /* This is only used on Windows */
+ gboolean ret;
+
+ MONO_ARCH_SAVE_REGS;
+
+ ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options);
+ if(ret==FALSE) {
+ /* FIXME: throw an exception? */
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
gunichar2
ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
{
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
return (gunichar2) ':'; /* colon */
#else
return (gunichar2) '/'; /* forward slash */
gunichar2
ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
{
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
return (gunichar2) '\\'; /* backslash */
#else
return (gunichar2) '/'; /* forward slash */
gunichar2
ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
{
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
return (gunichar2) '/'; /* forward slash */
#else
return (gunichar2) '/'; /* slash, same as DirectorySeparatorChar */
gunichar2
ves_icall_System_IO_MonoIO_get_PathSeparator ()
{
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
return (gunichar2) ';'; /* semicolon */
#else
return (gunichar2) ':'; /* colon */
static const gunichar2
invalid_path_chars [] = {
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */
0x003c, /* less than */
0x003e, /* greater than */
if(ret>0) {
#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": Temp path is [%s] (len %d)", name, ret);
+ g_message ("%s: Temp path is [%s] (len %d)", __func__, name, ret);
#endif
- *mono_name=mono_string_new_utf16 (mono_domain_get (), name,
- ret);
+ mono_gc_wbarrier_generic_store ((gpointer) mono_name,
+ (MonoObject*) mono_string_new_utf16 (mono_domain_get (), name, ret));
}
g_free (name);