* Dick Porter (dick@ximian.com)
* Gonzalo Paniagua Javier (gonzalo@ximian.com)
*
- * (C) 2001,2002,2003 Ximian, Inc.
- * Copyright (c) 2004,2005,2006 Novell, Inc. (http://www.novell.com)
+ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
+ * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
*/
#include <config.h>
+
#include <glib.h>
#include <string.h>
#include <errno.h>
-#include <signal.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <mono/metadata/appdomain.h>
#include <mono/metadata/marshal.h>
#include <mono/utils/strenc.h>
+#include <utils/mono-io-portability.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);
}
return (gint64)ticks;
}
-static void convert_win32_file_attribute_data (const WIN32_FILE_ATTRIBUTE_DATA *data, const gunichar2 *name, MonoIOStat *stat)
+static void convert_win32_file_attribute_data (const WIN32_FILE_ATTRIBUTE_DATA *data, MonoIOStat *stat)
{
- int len;
-
stat->attributes = data->dwFileAttributes;
stat->creation_time = convert_filetime (&data->ftCreationTime);
stat->last_access_time = convert_filetime (&data->ftLastAccessTime);
stat->last_write_time = convert_filetime (&data->ftLastWriteTime);
stat->length = ((gint64)data->nFileSizeHigh << 32) | data->nFileSizeLow;
-
- len = 0;
- while (name [len])
- ++ len;
-
- 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);
}
+static gchar *
+get_search_dir (MonoString *pattern)
+{
+ gchar *p;
+ gchar *result;
+
+ p = mono_string_to_utf8 (pattern);
+ result = g_path_get_dirname (p);
+ g_free (p);
+ return result;
+}
+
MonoArray *
ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path,
MonoString *path_with_pattern,
HANDLE find_handle;
GPtrArray *names;
gchar *utf8_path, *utf8_result, *full_name;
+ gint32 attributes;
MONO_ARCH_SAVE_REGS;
+ result = NULL;
*error = ERROR_SUCCESS;
domain = mono_domain_get ();
mask = convert_attrs (mask);
+ attributes = get_file_attributes (mono_string_chars (path));
+ if (attributes != -1) {
+ if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ *error = ERROR_INVALID_NAME;
+ goto leave;
+ }
+ } else {
+ *error = GetLastError ();
+ goto leave;
+ }
- find_handle = FindFirstFile (mono_string_chars (path_with_pattern),
- &data);
+ 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) {
+ if (find_error == ERROR_FILE_NOT_FOUND || find_error == ERROR_NO_MORE_FILES) {
/* No files, so just return an empty array */
- result = mono_array_new (domain,
- mono_defaults.string_class,
- 0);
-
- return(result);
+ goto leave;
}
*error = find_error;
- return(NULL);
+ goto leave;
}
+ utf8_path = get_search_dir (path_with_pattern);
names = g_ptr_array_new ();
- utf8_path = mono_string_to_utf8 (path);
do {
if ((data.cFileName[0] == '.' && data.cFileName[1] == 0) ||
if (FindClose (find_handle) == FALSE) {
*error = GetLastError ();
- g_ptr_array_free (names, TRUE);
- g_free (utf8_path);
- return(NULL);
+ 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)));
+ }
}
- 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)));
+ g_free (g_ptr_array_index (names, i));
}
-
g_ptr_array_free (names, TRUE);
g_free (utf8_path);
+
+leave:
+ // If there's no array and no error, then return an empty array.
+ if (result == NULL && *error == ERROR_SUCCESS)
+ result = mono_array_new (domain, mono_defaults.string_class, 0);
+
+ return result;
+}
+
+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)
+{
+ 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;
+
+ 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;
+}
+
+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;
+
+ *error = ERROR_SUCCESS;
+
+ 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;
+}
+
+MonoString *
+ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *error)
+{
+ IncrementalFind *ifh = handle;
+ WIN32_FIND_DATA data;
+ MonoString *result;
+
+ 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;
+
+ if (FindClose (ifh->find_handle) == FALSE){
+ error = GetLastError ();
+ } else
+ error = ERROR_SUCCESS;
+ g_free (ifh->utf8_path);
+ g_free (ifh);
+
+ return error;
+}
+
MonoString *
ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error)
{
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,
- mono_string_chars (path),
- stat);
+ convert_win32_file_attribute_data (&data, stat);
} else {
*error=GetLastError ();
+ memset (stat, 0, sizeof (MonoIOStat));
}
return result;
/* If we're opening a directory we need to set the extra flag
*/
- attrs = GetFileAttributes (chars);
+ attrs = get_file_attributes (chars);
if (attrs != INVALID_FILE_ATTRIBUTES) {
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
attributes |= FILE_FLAG_BACKUP_SEMANTICS;
MONO_ARCH_SAVE_REGS;
*error=ERROR_SUCCESS;
+
+ MONO_CHECK_ARG_NULL (dest);
if (dest_offset + count > mono_array_length (dest))
return 0;
MONO_ARCH_SAVE_REGS;
*error=ERROR_SUCCESS;
+
+ MONO_CHECK_ARG_NULL (src);
if (src_offset + count > mono_array_length (src))
return 0;
*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 */
+ if (IS_PORTABILITY_SET)
+ return (gunichar2) '\\'; /* backslash */
+ else
+ return (gunichar2) '/'; /* forward slash */
#endif
}
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);
}
}
+//Support for io-layer free mmap'd files.
+
+#if defined (TARGET_IOS) || defined (TARGET_ANDROID)
+
+gint64
+mono_filesize_from_path (MonoString *string)
+{
+ struct stat buf;
+ gint64 res;
+ char *path = mono_string_to_utf8 (string);
+
+ if (stat (path, &buf) == -1)
+ res = -1;
+ else
+ res = (gint64)buf.st_size;
+
+ g_free (path);
+ return res;
+}
+
+gint64
+mono_filesize_from_fd (int fd)
+{
+ struct stat buf;
+
+ if (fstat (fd, &buf) == -1)
+ return (gint64)-1;
+
+ return (gint64)buf.st_size;
+}
+
+#endif