From: Ludovic Henry Date: Fri, 20 Jan 2017 16:32:48 +0000 (-0500) Subject: [io-layer] Extract file (#4255) X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=mono.git;a=commitdiff_plain;h=f23f72c9de0cb7d03e5db68623440af9dcb19d6c [io-layer] Extract file (#4255) * [io-layer] Merge io related headers * [io-layer] Remove global variable _wapi_had_shut_down * [io-layer] Inline io-portability.c file into io.c * [io-layer] Inline locking.c file into io.c * [io-layer] Inline posix.c file into io.c * [file] Move metadata/file-io* files to metadata/w32file* * [io-layer] Extract file functions * [file] Remove overlapped parameter which is always NULL * [file] Remove SECURITY_ATTRIBUTES parameter which is always NULL * [file] Remove GET_FILEEX_INFO_LEVELS parameter which is always GetFileExInfoStandard * [file] Replace ULARGE_INTEGER parameter type with guint64 * [file] Replace WIN32_FILE_ATTRIBUTE_DATA parameter type with MonoIOStat * [file] Remove mono_w32file_create unused parameter tmplate * [file] Fix win32 build --- diff --git a/mcs/class/corlib/System.IO/FileOptions.cs b/mcs/class/corlib/System.IO/FileOptions.cs index 499319e343a..502974e7b00 100644 --- a/mcs/class/corlib/System.IO/FileOptions.cs +++ b/mcs/class/corlib/System.IO/FileOptions.cs @@ -50,7 +50,7 @@ namespace System.IO // The above is an internal value used by Path.GetTempFile to // get a file with 600 permissions, regardless of the umask // settings. If a value "1" must be introduced here, update - // both metadata/file-io.c and Path.GetTempFile + // both metadata/w32file.c and Path.GetTempFile // } } diff --git a/mono/io-layer/Makefile.am b/mono/io-layer/Makefile.am index 2247a51a71c..48e48baada2 100644 --- a/mono/io-layer/Makefile.am +++ b/mono/io-layer/Makefile.am @@ -12,30 +12,15 @@ libwapiincludedir = $(includedir)/mono-$(API_VER)/mono/io-layer OTHER_H = \ error.h \ - io.h \ - io-trace.h \ io-layer.h \ - io-portability.h \ - uglify.h \ wapi.h \ wapi-remap.h OTHER_SRC = \ error.c \ error.h \ - io.c \ - io.h \ - io-portability.c \ - io-portability.h \ - io-private.h \ io-layer.h \ - locking.c \ - posix.c \ - uglify.h \ - wapi_glob.h \ - wapi_glob.c \ wapi.h \ - wapi-private.h \ wapi.c diff --git a/mono/io-layer/error.c b/mono/io-layer/error.c index 5b1b22d6dcd..38a9141d2e1 100644 --- a/mono/io-layer/error.c +++ b/mono/io-layer/error.c @@ -14,7 +14,6 @@ #include #include "mono/io-layer/wapi.h" -#include "mono/io-layer/wapi-private.h" #include "mono/utils/mono-lazy-init.h" static pthread_key_t error_key; @@ -28,19 +27,6 @@ static void error_init(void) g_assert (ret == 0); } -static void error_cleanup (void) -{ - int ret; - - ret = pthread_key_delete (error_key); - g_assert (ret == 0); -} - -void _wapi_error_cleanup (void) -{ - mono_lazy_cleanup (&error_key_once, error_cleanup); -} - /** * GetLastError: * @@ -54,8 +40,6 @@ guint32 GetLastError(void) guint32 err; void *errptr; - if (_wapi_has_shut_down) - return 0; mono_lazy_initialize(&error_key_once, error_init); errptr=pthread_getspecific(error_key); err=GPOINTER_TO_UINT(errptr); @@ -73,8 +57,6 @@ void SetLastError(guint32 code) { int ret; - if (_wapi_has_shut_down) - return; /* Set the thread-local error code */ mono_lazy_initialize(&error_key_once, error_init); ret = pthread_setspecific(error_key, GUINT_TO_POINTER(code)); diff --git a/mono/io-layer/error.h b/mono/io-layer/error.h index d4ffe1e9235..9f6c640cc5e 100644 --- a/mono/io-layer/error.h +++ b/mono/io-layer/error.h @@ -82,7 +82,6 @@ G_BEGIN_DECLS guint32 GetLastError (void); void SetLastError (guint32 code); gint _wapi_get_win32_file_error (gint err); -void _wapi_error_cleanup (void); G_END_DECLS diff --git a/mono/io-layer/io-layer.h b/mono/io-layer/io-layer.h index e9096e25580..f1d2e05344e 100755 --- a/mono/io-layer/io-layer.h +++ b/mono/io-layer/io-layer.h @@ -47,7 +47,6 @@ typedef struct pollfd { #else /* EVERYONE ELSE */ #include "mono/io-layer/wapi.h" -#include "mono/io-layer/uglify.h" #endif /* HOST_WIN32 */ #ifdef __native_client__ diff --git a/mono/io-layer/io-portability.c b/mono/io-layer/io-portability.c deleted file mode 100644 index 282cbe93a29..00000000000 --- a/mono/io-layer/io-portability.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * io-portability.c: Optional filename mangling to try to cope with - * badly-written non-portable windows apps - * - * Author: - * Dick Porter (dick@ximian.com) - * - * Copyright (c) 2006 Novell, Inc. - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_DIRENT_H -# include -#endif -#include -#include - -#include -#include -#include -#include - -#undef DEBUG - -int _wapi_open (const char *pathname, int flags, mode_t mode) -{ - int fd; - gchar *located_filename; - - if (flags & O_CREAT) { - located_filename = mono_portability_find_file (pathname, FALSE); - if (located_filename == NULL) { - fd = open (pathname, flags, mode); - } else { - fd = open (located_filename, flags, mode); - g_free (located_filename); - } - } else { - fd = open (pathname, flags, mode); - if (fd == -1 && - (errno == ENOENT || errno == ENOTDIR) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - located_filename = mono_portability_find_file (pathname, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return (-1); - } - - fd = open (located_filename, flags, mode); - g_free (located_filename); - } - } - - - return(fd); -} - -int _wapi_access (const char *pathname, int mode) -{ - int ret; - - ret = access (pathname, mode); - if (ret == -1 && - (errno == ENOENT || errno == ENOTDIR) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_filename = mono_portability_find_file (pathname, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return(-1); - } - - ret = access (located_filename, mode); - g_free (located_filename); - } - - return(ret); -} - -int _wapi_chmod (const char *pathname, mode_t mode) -{ - int ret; - - ret = chmod (pathname, mode); - if (ret == -1 && - (errno == ENOENT || errno == ENOTDIR) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_filename = mono_portability_find_file (pathname, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return(-1); - } - - ret = chmod (located_filename, mode); - g_free (located_filename); - } - - return(ret); -} - -int _wapi_utime (const char *filename, const struct utimbuf *buf) -{ - int ret; - - ret = utime (filename, buf); - if (ret == -1 && - errno == ENOENT && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_filename = mono_portability_find_file (filename, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return(-1); - } - - ret = utime (located_filename, buf); - g_free (located_filename); - } - - return(ret); -} - -int _wapi_unlink (const char *pathname) -{ - int ret; - - ret = unlink (pathname); - if (ret == -1 && - (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_filename = mono_portability_find_file (pathname, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return(-1); - } - - ret = unlink (located_filename); - g_free (located_filename); - } - - return(ret); -} - -int _wapi_rename (const char *oldpath, const char *newpath) -{ - int ret; - gchar *located_newpath = mono_portability_find_file (newpath, FALSE); - - if (located_newpath == NULL) { - ret = rename (oldpath, newpath); - } else { - ret = rename (oldpath, located_newpath); - - if (ret == -1 && - (errno == EISDIR || errno == ENAMETOOLONG || - errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE); - - if (located_oldpath == NULL) { - g_free (located_oldpath); - g_free (located_newpath); - - errno = saved_errno; - return(-1); - } - - ret = rename (located_oldpath, located_newpath); - g_free (located_oldpath); - } - g_free (located_newpath); - } - - return(ret); -} - -int _wapi_stat (const char *path, struct stat *buf) -{ - int ret; - - ret = stat (path, buf); - if (ret == -1 && - (errno == ENOENT || errno == ENOTDIR) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_filename = mono_portability_find_file (path, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return(-1); - } - - ret = stat (located_filename, buf); - g_free (located_filename); - } - - return(ret); -} - -int _wapi_lstat (const char *path, struct stat *buf) -{ - int ret; - - ret = lstat (path, buf); - if (ret == -1 && - (errno == ENOENT || errno == ENOTDIR) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_filename = mono_portability_find_file (path, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return(-1); - } - - ret = lstat (located_filename, buf); - g_free (located_filename); - } - - return(ret); -} - -int _wapi_mkdir (const char *pathname, mode_t mode) -{ - int ret; - gchar *located_filename = mono_portability_find_file (pathname, FALSE); - - if (located_filename == NULL) { - ret = mkdir (pathname, mode); - } else { - ret = mkdir (located_filename, mode); - g_free (located_filename); - } - - return(ret); -} - -int _wapi_rmdir (const char *pathname) -{ - int ret; - - ret = rmdir (pathname); - if (ret == -1 && - (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_filename = mono_portability_find_file (pathname, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return(-1); - } - - ret = rmdir (located_filename); - g_free (located_filename); - } - - return(ret); -} - -int _wapi_chdir (const char *path) -{ - int ret; - - ret = chdir (path); - if (ret == -1 && - (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && - IS_PORTABILITY_SET) { - int saved_errno = errno; - gchar *located_filename = mono_portability_find_file (path, TRUE); - - if (located_filename == NULL) { - errno = saved_errno; - return(-1); - } - - ret = chdir (located_filename); - g_free (located_filename); - } - - return(ret); -} - -gchar *_wapi_basename (const gchar *filename) -{ - gchar *new_filename = g_strdup (filename), *ret; - - if (IS_PORTABILITY_SET) { - g_strdelimit (new_filename, "\\", '/'); - } - - if (IS_PORTABILITY_DRIVE && - g_ascii_isalpha (new_filename[0]) && - (new_filename[1] == ':')) { - int len = strlen (new_filename); - - g_memmove (new_filename, new_filename + 2, len - 2); - new_filename[len - 2] = '\0'; - } - - ret = g_path_get_basename (new_filename); - g_free (new_filename); - - return(ret); -} - -gchar *_wapi_dirname (const gchar *filename) -{ - gchar *new_filename = g_strdup (filename), *ret; - - if (IS_PORTABILITY_SET) { - g_strdelimit (new_filename, "\\", '/'); - } - - if (IS_PORTABILITY_DRIVE && - g_ascii_isalpha (new_filename[0]) && - (new_filename[1] == ':')) { - int len = strlen (new_filename); - - g_memmove (new_filename, new_filename + 2, len - 2); - new_filename[len - 2] = '\0'; - } - - ret = g_path_get_dirname (new_filename); - g_free (new_filename); - - return(ret); -} - -GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error) -{ - GDir *ret; - - ret = g_dir_open (path, flags, error); - if (ret == NULL && - ((*error)->code == G_FILE_ERROR_NOENT || - (*error)->code == G_FILE_ERROR_NOTDIR || - (*error)->code == G_FILE_ERROR_NAMETOOLONG) && - IS_PORTABILITY_SET) { - gchar *located_filename = mono_portability_find_file (path, TRUE); - GError *tmp_error = NULL; - - if (located_filename == NULL) { - return(NULL); - } - - ret = g_dir_open (located_filename, flags, &tmp_error); - g_free (located_filename); - if (tmp_error == NULL) { - g_clear_error (error); - } - } - - return(ret); -} - - -static gint -file_compare (gconstpointer a, gconstpointer b) -{ - gchar *astr = *(gchar **) a; - gchar *bstr = *(gchar **) b; - - return strcmp (astr, bstr); -} - -static gint -get_errno_from_g_file_error (gint error) -{ - switch (error) { -#ifdef EACCESS - case G_FILE_ERROR_ACCES: - error = EACCES; - break; -#endif -#ifdef ENAMETOOLONG - case G_FILE_ERROR_NAMETOOLONG: - error = ENAMETOOLONG; - break; -#endif -#ifdef ENOENT - case G_FILE_ERROR_NOENT: - error = ENOENT; - break; -#endif -#ifdef ENOTDIR - case G_FILE_ERROR_NOTDIR: - error = ENOTDIR; - break; -#endif -#ifdef ENXIO - case G_FILE_ERROR_NXIO: - error = ENXIO; - break; -#endif -#ifdef ENODEV - case G_FILE_ERROR_NODEV: - error = ENODEV; - break; -#endif -#ifdef EROFS - case G_FILE_ERROR_ROFS: - error = EROFS; - break; -#endif -#ifdef ETXTBSY - case G_FILE_ERROR_TXTBSY: - error = ETXTBSY; - break; -#endif -#ifdef EFAULT - case G_FILE_ERROR_FAULT: - error = EFAULT; - break; -#endif -#ifdef ELOOP - case G_FILE_ERROR_LOOP: - error = ELOOP; - break; -#endif -#ifdef ENOSPC - case G_FILE_ERROR_NOSPC: - error = ENOSPC; - break; -#endif -#ifdef ENOMEM - case G_FILE_ERROR_NOMEM: - error = ENOMEM; - break; -#endif -#ifdef EMFILE - case G_FILE_ERROR_MFILE: - error = EMFILE; - break; -#endif -#ifdef ENFILE - case G_FILE_ERROR_NFILE: - error = ENFILE; - break; -#endif -#ifdef EBADF - case G_FILE_ERROR_BADF: - error = EBADF; - break; -#endif -#ifdef EINVAL - case G_FILE_ERROR_INVAL: - error = EINVAL; - break; -#endif -#ifdef EPIPE - case G_FILE_ERROR_PIPE: - error = EPIPE; - break; -#endif -#ifdef EAGAIN - case G_FILE_ERROR_AGAIN: - error = EAGAIN; - break; -#endif -#ifdef EINTR - case G_FILE_ERROR_INTR: - error = EINTR; - break; -#endif -#ifdef EWIO - case G_FILE_ERROR_IO: - error = EIO; - break; -#endif -#ifdef EPERM - case G_FILE_ERROR_PERM: - error = EPERM; - break; -#endif - case G_FILE_ERROR_FAILED: - error = ERROR_INVALID_PARAMETER; - break; - } - - return error; -} - -/* scandir using glib */ -gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern, - gchar ***namelist) -{ - GError *error = NULL; - GDir *dir; - GPtrArray *names; - gint result; - wapi_glob_t glob_buf; - int flags = 0, i; - - dir = _wapi_g_dir_open (dirname, 0, &error); - if (dir == NULL) { - /* g_dir_open returns ENOENT on directories on which we don't - * have read/x permission */ - gint errnum = get_errno_from_g_file_error (error->code); - g_error_free (error); - if (errnum == ENOENT && - !_wapi_access (dirname, F_OK) && - _wapi_access (dirname, R_OK|X_OK)) { - errnum = EACCES; - } - - errno = errnum; - return -1; - } - - if (IS_PORTABILITY_CASE) { - flags = WAPI_GLOB_IGNORECASE; - } - - result = _wapi_glob (dir, pattern, flags, &glob_buf); - if (g_str_has_suffix (pattern, ".*")) { - /* Special-case the patterns ending in '.*', as - * windows also matches entries with no extension with - * this pattern. - * - * TODO: should this be a MONO_IOMAP option? - */ - gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2); - gint result2; - - g_dir_rewind (dir); - result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf); - - g_free (pattern2); - - if (result != 0) { - result = result2; - } - } - - g_dir_close (dir); - if (glob_buf.gl_pathc == 0) { - return(0); - } else if (result != 0) { - return(-1); - } - - names = g_ptr_array_new (); - for (i = 0; i < glob_buf.gl_pathc; i++) { - g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i])); - } - - _wapi_globfree (&glob_buf); - - result = names->len; - if (result > 0) { - g_ptr_array_sort (names, file_compare); - g_ptr_array_set_size (names, result + 1); - - *namelist = (gchar **) g_ptr_array_free (names, FALSE); - } else { - g_ptr_array_free (names, TRUE); - } - - return result; -} diff --git a/mono/io-layer/io-portability.h b/mono/io-layer/io-portability.h deleted file mode 100644 index d7e9dba666d..00000000000 --- a/mono/io-layer/io-portability.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * io-portability.h: Optional filename mangling to try to cope with - * badly-written non-portable windows apps - * - * Author: - * Dick Porter (dick@ximian.com) - * - * Copyright (C) 2006 Novell, Inc. - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#ifndef _WAPI_IO_PORTABILITY_H_ -#define _WAPI_IO_PORTABILITY_H_ - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -extern int _wapi_open (const char *pathname, int flags, mode_t mode); -extern int _wapi_access (const char *pathname, int mode); -extern int _wapi_chmod (const char *pathname, mode_t mode); -extern int _wapi_utime (const char *filename, const struct utimbuf *buf); -extern int _wapi_unlink (const char *pathname); -extern int _wapi_rename (const char *oldpath, const char *newpath); -extern int _wapi_stat (const char *path, struct stat *buf); -extern int _wapi_lstat (const char *path, struct stat *buf); -extern int _wapi_mkdir (const char *pathname, mode_t mode); -extern int _wapi_rmdir (const char *pathname); -extern int _wapi_chdir (const char *path); -extern gchar *_wapi_basename (const gchar *filename); -extern gchar *_wapi_dirname (const gchar *filename); -extern GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error); -extern gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern, - gchar ***namelist); - -G_END_DECLS - -#endif /* _WAPI_IO_PORTABILITY_H_ */ diff --git a/mono/io-layer/io-private.h b/mono/io-layer/io-private.h deleted file mode 100644 index 392b081ca79..00000000000 --- a/mono/io-layer/io-private.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * io-private.h: Private definitions for file, console and find handles - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - * Copyright 2011 Xamarin Inc - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#ifndef _WAPI_IO_PRIVATE_H_ -#define _WAPI_IO_PRIVATE_H_ - -#include -#include -#ifdef HAVE_DIRENT_H -#include -#endif - -#include -#include - -extern gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length); -extern gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length); -extern gpointer _wapi_stdhandle_create (int fd, const gchar *name); - -/* Currently used for both FILE, CONSOLE and PIPE handle types. This may - * have to change in future. - */ -struct _WapiHandle_file -{ - gchar *filename; - struct _WapiFileShare *share_info; /* Pointer into shared mem */ - int fd; - guint32 security_attributes; - guint32 fileaccess; - guint32 sharemode; - guint32 attrs; -}; - -struct _WapiHandle_find -{ - gchar **namelist; - gchar *dir_part; - int num; - size_t count; -}; - -#endif /* _WAPI_IO_PRIVATE_H_ */ diff --git a/mono/io-layer/io-trace.h b/mono/io-layer/io-trace.h deleted file mode 100644 index e65fdc345a3..00000000000 --- a/mono/io-layer/io-trace.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * io-trace.h: tracing macros - * - * Authors: - * Marek Habersack - * - * Copyright 2016 Xamarin, Inc (http://xamarin.com/) - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#ifndef __IO_TRACE_H - -#ifdef DISABLE_IO_LAYER_TRACE -#define MONO_TRACE(...) -#else -#include "mono/utils/mono-logger-internals.h" -#define MONO_TRACE(...) mono_trace (__VA_ARGS__) -#endif - -#endif diff --git a/mono/io-layer/io.c b/mono/io-layer/io.c deleted file mode 100644 index 05d554a12c0..00000000000 --- a/mono/io-layer/io.c +++ /dev/null @@ -1,4453 +0,0 @@ -/* - * io.c: File, console and find handles - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - * Copyright (c) 2002-2006 Novell, Inc. - * Copyright 2011 Xamarin Inc (http://www.xamarin.com). - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_STATVFS_H -#include -#endif -#if defined(HAVE_SYS_STATFS_H) -#include -#endif -#if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H) -#include -#include -#endif -#include -#include -#include -#ifdef __linux__ -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * If SHM is disabled, this will point to a hash of _WapiFileShare structures, otherwise - * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a - * 4MB array. - */ -static GHashTable *file_share_hash; -static mono_mutex_t file_share_mutex; - -static void -time_t_to_filetime (time_t timeval, WapiFileTime *filetime) -{ - guint64 ticks; - - ticks = ((guint64)timeval * 10000000) + 116444736000000000ULL; - filetime->dwLowDateTime = ticks & 0xFFFFFFFF; - filetime->dwHighDateTime = ticks >> 32; -} - -static void -_wapi_handle_share_release (_WapiFileShare *share_info) -{ - /* Prevent new entries racing with us */ - mono_os_mutex_lock (&file_share_mutex); - - g_assert (share_info->handle_refs > 0); - share_info->handle_refs -= 1; - - if (share_info->handle_refs == 0) - g_hash_table_remove (file_share_hash, share_info); - - mono_os_mutex_unlock (&file_share_mutex); -} - -static gint -wapi_share_info_equal (gconstpointer ka, gconstpointer kb) -{ - const _WapiFileShare *s1 = (const _WapiFileShare *)ka; - const _WapiFileShare *s2 = (const _WapiFileShare *)kb; - - return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0; -} - -static guint -wapi_share_info_hash (gconstpointer data) -{ - const _WapiFileShare *s = (const _WapiFileShare *)data; - - return s->inode; -} - -static gboolean -_wapi_handle_get_or_set_share (guint64 device, guint64 inode, guint32 new_sharemode, guint32 new_access, - guint32 *old_sharemode, guint32 *old_access, struct _WapiFileShare **share_info) -{ - struct _WapiFileShare *file_share; - gboolean exists = FALSE; - - /* Prevent new entries racing with us */ - mono_os_mutex_lock (&file_share_mutex); - - _WapiFileShare tmp; - - /* - * Instead of allocating a 4MB array, we use a hash table to keep track of this - * info. This is needed even if SHM is disabled, to track sharing inside - * the current process. - */ - if (!file_share_hash) - file_share_hash = g_hash_table_new_full (wapi_share_info_hash, wapi_share_info_equal, NULL, g_free); - - tmp.device = device; - tmp.inode = inode; - - file_share = (_WapiFileShare *)g_hash_table_lookup (file_share_hash, &tmp); - if (file_share) { - *old_sharemode = file_share->sharemode; - *old_access = file_share->access; - *share_info = file_share; - - g_assert (file_share->handle_refs > 0); - file_share->handle_refs += 1; - - exists = TRUE; - } else { - file_share = g_new0 (_WapiFileShare, 1); - - file_share->device = device; - file_share->inode = inode; - file_share->opened_by_pid = wapi_getpid (); - file_share->sharemode = new_sharemode; - file_share->access = new_access; - file_share->handle_refs = 1; - *share_info = file_share; - - g_hash_table_insert (file_share_hash, file_share, file_share); - } - - mono_os_mutex_unlock (&file_share_mutex); - - return(exists); -} - -static void file_close (gpointer handle, gpointer data); -static void file_details (gpointer data); -static const gchar* file_typename (void); -static gsize file_typesize (void); -static WapiFileType file_getfiletype(void); -static gboolean file_read(gpointer handle, gpointer buffer, - guint32 numbytes, guint32 *bytesread, - WapiOverlapped *overlapped); -static gboolean file_write(gpointer handle, gconstpointer buffer, - guint32 numbytes, guint32 *byteswritten, - WapiOverlapped *overlapped); -static gboolean file_flush(gpointer handle); -static guint32 file_seek(gpointer handle, gint32 movedistance, - gint32 *highmovedistance, WapiSeekMethod method); -static gboolean file_setendoffile(gpointer handle); -static guint32 file_getfilesize(gpointer handle, guint32 *highsize); -static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time, - WapiFileTime *last_access, - WapiFileTime *last_write); -static gboolean file_setfiletime(gpointer handle, - const WapiFileTime *create_time, - const WapiFileTime *last_access, - const WapiFileTime *last_write); -static guint32 GetDriveTypeFromPath (const gchar *utf8_root_path_name); - -/* File handle is only signalled for overlapped IO */ -static MonoW32HandleOps _wapi_file_ops = { - file_close, /* close */ - NULL, /* signal */ - NULL, /* own */ - NULL, /* is_owned */ - NULL, /* special_wait */ - NULL, /* prewait */ - file_details, /* details */ - file_typename, /* typename */ - file_typesize, /* typesize */ -}; - -static void console_close (gpointer handle, gpointer data); -static void console_details (gpointer data); -static const gchar* console_typename (void); -static gsize console_typesize (void); -static WapiFileType console_getfiletype(void); -static gboolean console_read(gpointer handle, gpointer buffer, - guint32 numbytes, guint32 *bytesread, - WapiOverlapped *overlapped); -static gboolean console_write(gpointer handle, gconstpointer buffer, - guint32 numbytes, guint32 *byteswritten, - WapiOverlapped *overlapped); - -/* Console is mostly the same as file, except it can block waiting for - * input or output - */ -static MonoW32HandleOps _wapi_console_ops = { - console_close, /* close */ - NULL, /* signal */ - NULL, /* own */ - NULL, /* is_owned */ - NULL, /* special_wait */ - NULL, /* prewait */ - console_details, /* details */ - console_typename, /* typename */ - console_typesize, /* typesize */ -}; - -static const gchar* find_typename (void); -static gsize find_typesize (void); - -static MonoW32HandleOps _wapi_find_ops = { - NULL, /* close */ - NULL, /* signal */ - NULL, /* own */ - NULL, /* is_owned */ - NULL, /* special_wait */ - NULL, /* prewait */ - NULL, /* details */ - find_typename, /* typename */ - find_typesize, /* typesize */ -}; - -static void pipe_close (gpointer handle, gpointer data); -static void pipe_details (gpointer data); -static const gchar* pipe_typename (void); -static gsize pipe_typesize (void); -static WapiFileType pipe_getfiletype (void); -static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, - guint32 *bytesread, WapiOverlapped *overlapped); -static gboolean pipe_write (gpointer handle, gconstpointer buffer, - guint32 numbytes, guint32 *byteswritten, - WapiOverlapped *overlapped); - -/* Pipe handles - */ -static MonoW32HandleOps _wapi_pipe_ops = { - pipe_close, /* close */ - NULL, /* signal */ - NULL, /* own */ - NULL, /* is_owned */ - NULL, /* special_wait */ - NULL, /* prewait */ - pipe_details, /* details */ - pipe_typename, /* typename */ - pipe_typesize, /* typesize */ -}; - -static const struct { - /* File, console and pipe handles */ - WapiFileType (*getfiletype)(void); - - /* File, console and pipe handles */ - gboolean (*readfile)(gpointer handle, gpointer buffer, - guint32 numbytes, guint32 *bytesread, - WapiOverlapped *overlapped); - gboolean (*writefile)(gpointer handle, gconstpointer buffer, - guint32 numbytes, guint32 *byteswritten, - WapiOverlapped *overlapped); - gboolean (*flushfile)(gpointer handle); - - /* File handles */ - guint32 (*seek)(gpointer handle, gint32 movedistance, - gint32 *highmovedistance, WapiSeekMethod method); - gboolean (*setendoffile)(gpointer handle); - guint32 (*getfilesize)(gpointer handle, guint32 *highsize); - gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time, - WapiFileTime *last_access, - WapiFileTime *last_write); - gboolean (*setfiletime)(gpointer handle, - const WapiFileTime *create_time, - const WapiFileTime *last_access, - const WapiFileTime *last_write); -} io_ops[MONO_W32HANDLE_COUNT]={ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - /* file */ - {file_getfiletype, - file_read, file_write, - file_flush, file_seek, - file_setendoffile, - file_getfilesize, - file_getfiletime, - file_setfiletime}, - /* console */ - {console_getfiletype, - console_read, - console_write, - NULL, NULL, NULL, NULL, NULL, NULL}, - /* thread */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - /* sem */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - /* mutex */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - /* event */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - /* socket (will need at least read and write) */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - /* find */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - /* process */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - /* pipe */ - {pipe_getfiletype, - pipe_read, - pipe_write, - NULL, NULL, NULL, NULL, NULL, NULL}, -}; - -static gboolean lock_while_writing = FALSE; - -/* Some utility functions. - */ - -/* - * Check if a file is writable by the current user. - * - * This is is a best effort kind of thing. It assumes a reasonable sane set - * of permissions by the underlying OS. - * - * We generally assume that basic unix permission bits are authoritative. Which might not - * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc) - * - * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file. - * - * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent - * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous - * and should not be used with a dynamic runtime. - */ -static gboolean -is_file_writable (struct stat *st, const char *path) -{ -#if __APPLE__ - // OS X Finder "locked" or `ls -lO` "uchg". - // This only covers one of several cases where an OS X file could be unwritable through special flags. - if (st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE)) - return 0; -#endif - - /* Is it globally writable? */ - if (st->st_mode & S_IWOTH) - return 1; - - /* Am I the owner? */ - if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR)) - return 1; - - /* Am I in the same group? */ - if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP)) - return 1; - - /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid - * but it's the only sane option we have on unix. - */ - return access (path, W_OK) == 0; -} - - -static guint32 _wapi_stat_to_file_attributes (const gchar *pathname, - struct stat *buf, - struct stat *lbuf) -{ - guint32 attrs = 0; - gchar *filename; - - /* FIXME: this could definitely be better, but there seems to - * be no pattern to the attributes that are set - */ - - /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */ - if (S_ISSOCK (buf->st_mode)) - buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */ - - filename = _wapi_basename (pathname); - - if (S_ISDIR (buf->st_mode)) { - attrs = FILE_ATTRIBUTE_DIRECTORY; - if (!is_file_writable (buf, pathname)) { - attrs |= FILE_ATTRIBUTE_READONLY; - } - if (filename[0] == '.') { - attrs |= FILE_ATTRIBUTE_HIDDEN; - } - } else { - if (!is_file_writable (buf, pathname)) { - attrs = FILE_ATTRIBUTE_READONLY; - - if (filename[0] == '.') { - attrs |= FILE_ATTRIBUTE_HIDDEN; - } - } else if (filename[0] == '.') { - attrs = FILE_ATTRIBUTE_HIDDEN; - } else { - attrs = FILE_ATTRIBUTE_NORMAL; - } - } - - if (lbuf != NULL) { - if (S_ISLNK (lbuf->st_mode)) { - attrs |= FILE_ATTRIBUTE_REPARSE_POINT; - } - } - - g_free (filename); - - return attrs; -} - -static void -_wapi_set_last_error_from_errno (void) -{ - SetLastError (_wapi_get_win32_file_error (errno)); -} - -static void _wapi_set_last_path_error_from_errno (const gchar *dir, - const gchar *path) -{ - if (errno == ENOENT) { - /* Check the path - if it's a missing directory then - * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND - */ - gchar *dirname; - - - if (dir == NULL) { - dirname = _wapi_dirname (path); - } else { - dirname = g_strdup (dir); - } - - if (_wapi_access (dirname, F_OK) == 0) { - SetLastError (ERROR_FILE_NOT_FOUND); - } else { - SetLastError (ERROR_PATH_NOT_FOUND); - } - - g_free (dirname); - } else { - _wapi_set_last_error_from_errno (); - } -} - -/* Handle ops. - */ -static void file_close (gpointer handle, gpointer data) -{ - struct _WapiHandle_file *file_handle = (struct _WapiHandle_file *)data; - int fd = file_handle->fd; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing file handle %p [%s]", __func__, handle, - file_handle->filename); - - if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE) - _wapi_unlink (file_handle->filename); - - g_free (file_handle->filename); - - if (file_handle->share_info) - _wapi_handle_share_release (file_handle->share_info); - - close (fd); -} - -static void file_details (gpointer data) -{ - struct _WapiHandle_file *file = (struct _WapiHandle_file *)data; - - g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u", - file->filename, - file->fileaccess&GENERIC_READ?'R':'.', - file->fileaccess&GENERIC_WRITE?'W':'.', - file->fileaccess&GENERIC_EXECUTE?'X':'.', - file->sharemode&FILE_SHARE_READ?'R':'.', - file->sharemode&FILE_SHARE_WRITE?'W':'.', - file->sharemode&FILE_SHARE_DELETE?'D':'.', - file->attrs); -} - -static const gchar* file_typename (void) -{ - return "File"; -} - -static gsize file_typesize (void) -{ - return sizeof (struct _WapiHandle_file); -} - -static WapiFileType file_getfiletype(void) -{ - return(FILE_TYPE_DISK); -} - -static gboolean file_read(gpointer handle, gpointer buffer, - guint32 numbytes, guint32 *bytesread, - WapiOverlapped *overlapped) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - int fd, ret; - MonoThreadInfo *info = mono_thread_info_current (); - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - fd = file_handle->fd; - if(bytesread!=NULL) { - *bytesread=0; - } - - if(!(file_handle->fileaccess & GENERIC_READ) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u", - __func__, handle, file_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - do { - ret = read (fd, buffer, numbytes); - } while (ret == -1 && errno == EINTR && - !mono_thread_info_is_interrupt_state (info)); - - if(ret==-1) { - gint err = errno; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, - handle, strerror(err)); - SetLastError (_wapi_get_win32_file_error (err)); - return(FALSE); - } - - if (bytesread != NULL) { - *bytesread = ret; - } - - return(TRUE); -} - -static gboolean file_write(gpointer handle, gconstpointer buffer, - guint32 numbytes, guint32 *byteswritten, - WapiOverlapped *overlapped G_GNUC_UNUSED) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - int ret, fd; - off_t current_pos = 0; - MonoThreadInfo *info = mono_thread_info_current (); - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - fd = file_handle->fd; - - if(byteswritten!=NULL) { - *byteswritten=0; - } - - if(!(file_handle->fileaccess & GENERIC_WRITE) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - if (lock_while_writing) { - /* Need to lock the region we're about to write to, - * because we only do advisory locking on POSIX - * systems - */ - current_pos = lseek (fd, (off_t)0, SEEK_CUR); - if (current_pos == -1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__, - handle, strerror (errno)); - _wapi_set_last_error_from_errno (); - return(FALSE); - } - - if (_wapi_lock_file_region (fd, current_pos, - numbytes) == FALSE) { - /* The error has already been set */ - return(FALSE); - } - } - - do { - ret = write (fd, buffer, numbytes); - } while (ret == -1 && errno == EINTR && - !mono_thread_info_is_interrupt_state (info)); - - if (lock_while_writing) { - _wapi_unlock_file_region (fd, current_pos, numbytes); - } - - if (ret == -1) { - if (errno == EINTR) { - ret = 0; - } else { - _wapi_set_last_error_from_errno (); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", - __func__, handle, strerror(errno)); - - return(FALSE); - } - } - if (byteswritten != NULL) { - *byteswritten = ret; - } - return(TRUE); -} - -static gboolean file_flush(gpointer handle) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - int ret, fd; - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - fd = file_handle->fd; - - if(!(file_handle->fileaccess & GENERIC_WRITE) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - ret=fsync(fd); - if (ret==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fsync of handle %p error: %s", __func__, handle, - strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } - - return(TRUE); -} - -static guint32 file_seek(gpointer handle, gint32 movedistance, - gint32 *highmovedistance, WapiSeekMethod method) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - gint64 offset, newpos; - int whence, fd; - guint32 ret; - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(INVALID_SET_FILE_POINTER); - } - - fd = file_handle->fd; - - if(!(file_handle->fileaccess & GENERIC_READ) && - !(file_handle->fileaccess & GENERIC_WRITE) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(INVALID_SET_FILE_POINTER); - } - - switch(method) { - case FILE_BEGIN: - whence=SEEK_SET; - break; - case FILE_CURRENT: - whence=SEEK_CUR; - break; - case FILE_END: - whence=SEEK_END; - break; - default: - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: invalid seek type %d", __func__, method); - - SetLastError (ERROR_INVALID_PARAMETER); - return(INVALID_SET_FILE_POINTER); - } - -#ifdef HAVE_LARGE_FILE_SUPPORT - if(highmovedistance==NULL) { - offset=movedistance; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld (low %d)", __func__, - offset, movedistance); - } else { - offset=((gint64) *highmovedistance << 32) | (guint32)movedistance; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance); - } -#else - offset=movedistance; -#endif - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: moving handle %p by %lld bytes from %d", __func__, - handle, (long long)offset, whence); - -#ifdef PLATFORM_ANDROID - /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */ - newpos=lseek64(fd, offset, whence); -#else - newpos=lseek(fd, offset, whence); -#endif - if(newpos==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek on handle %p returned error %s", - __func__, handle, strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(INVALID_SET_FILE_POINTER); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek returns %lld", __func__, newpos); - -#ifdef HAVE_LARGE_FILE_SUPPORT - ret=newpos & 0xFFFFFFFF; - if(highmovedistance!=NULL) { - *highmovedistance=newpos>>32; - } -#else - ret=newpos; - if(highmovedistance!=NULL) { - /* Accurate, but potentially dodgy :-) */ - *highmovedistance=0; - } -#endif - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: move of handle %p returning %d/%d", __func__, - handle, ret, highmovedistance==NULL?0:*highmovedistance); - - return(ret); -} - -static gboolean file_setendoffile(gpointer handle) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - struct stat statbuf; - off_t pos; - int ret, fd; - MonoThreadInfo *info = mono_thread_info_current (); - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - fd = file_handle->fd; - - if(!(file_handle->fileaccess & GENERIC_WRITE) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - /* Find the current file position, and the file length. If - * the file position is greater than the length, write to - * extend the file with a hole. If the file position is less - * than the length, truncate the file. - */ - - ret=fstat(fd, &statbuf); - if(ret==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, - handle, strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } - - pos=lseek(fd, (off_t)0, SEEK_CUR); - if(pos==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__, - handle, strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } - -#ifdef FTRUNCATE_DOESNT_EXTEND - off_t size = statbuf.st_size; - /* I haven't bothered to write the configure.ac stuff for this - * because I don't know if any platform needs it. I'm leaving - * this code just in case though - */ - if(pos>size) { - /* Extend the file. Use write() here, because some - * manuals say that ftruncate() behaviour is undefined - * when the file needs extending. The POSIX spec says - * that on XSI-conformant systems it extends, so if - * every system we care about conforms, then we can - * drop this write. - */ - do { - ret = write (fd, "", 1); - } while (ret == -1 && errno == EINTR && - !mono_thread_info_is_interrupt_state (info)); - - if(ret==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p extend write failed: %s", __func__, handle, strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } - - /* And put the file position back after the write */ - ret = lseek (fd, pos, SEEK_SET); - if (ret == -1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p second lseek failed: %s", - __func__, handle, strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } - } -#endif - -/* Native Client has no ftruncate function, even in standalone sel_ldr. */ -#ifndef __native_client__ - /* always truncate, because the extend write() adds an extra - * byte to the end of the file - */ - do { - ret=ftruncate(fd, pos); - } - while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); - if(ret==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ftruncate failed: %s", __func__, - handle, strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } -#endif - - return(TRUE); -} - -static guint32 file_getfilesize(gpointer handle, guint32 *highsize) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - struct stat statbuf; - guint32 size; - int ret; - int fd; - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(INVALID_FILE_SIZE); - } - fd = file_handle->fd; - - if(!(file_handle->fileaccess & GENERIC_READ) && - !(file_handle->fileaccess & GENERIC_WRITE) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(INVALID_FILE_SIZE); - } - - /* If the file has a size with the low bits 0xFFFFFFFF the - * caller can't tell if this is an error, so clear the error - * value - */ - SetLastError (ERROR_SUCCESS); - - ret = fstat(fd, &statbuf); - if (ret == -1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, - handle, strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(INVALID_FILE_SIZE); - } - - /* fstat indicates block devices as zero-length, so go a different path */ -#ifdef BLKGETSIZE64 - if (S_ISBLK(statbuf.st_mode)) { - guint64 bigsize; - if (ioctl(fd, BLKGETSIZE64, &bigsize) < 0) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ioctl BLKGETSIZE64 failed: %s", - __func__, handle, strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(INVALID_FILE_SIZE); - } - - size = bigsize & 0xFFFFFFFF; - if (highsize != NULL) { - *highsize = bigsize>>32; - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning block device size %d/%d", - __func__, size, *highsize); - - return(size); - } -#endif - -#ifdef HAVE_LARGE_FILE_SUPPORT - size = statbuf.st_size & 0xFFFFFFFF; - if (highsize != NULL) { - *highsize = statbuf.st_size>>32; - } -#else - if (highsize != NULL) { - /* Accurate, but potentially dodgy :-) */ - *highsize = 0; - } - size = statbuf.st_size; -#endif - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning size %d/%d", __func__, size, *highsize); - - return(size); -} - -static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time, - WapiFileTime *last_access, - WapiFileTime *last_write) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - struct stat statbuf; - guint64 create_ticks, access_ticks, write_ticks; - int ret, fd; - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - fd = file_handle->fd; - - if(!(file_handle->fileaccess & GENERIC_READ) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u", - __func__, handle, file_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - ret=fstat(fd, &statbuf); - if(ret==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle, - strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: atime: %ld ctime: %ld mtime: %ld", __func__, - statbuf.st_atime, statbuf.st_ctime, - statbuf.st_mtime); - - /* Try and guess a meaningful create time by using the older - * of atime or ctime - */ - /* The magic constant comes from msdn documentation - * "Converting a time_t Value to a File Time" - */ - if(statbuf.st_atime < statbuf.st_ctime) { - create_ticks=((guint64)statbuf.st_atime*10000000) - + 116444736000000000ULL; - } else { - create_ticks=((guint64)statbuf.st_ctime*10000000) - + 116444736000000000ULL; - } - - access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL; - write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: aticks: %llu cticks: %llu wticks: %llu", __func__, - access_ticks, create_ticks, write_ticks); - - if(create_time!=NULL) { - create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF; - create_time->dwHighDateTime = create_ticks >> 32; - } - - if(last_access!=NULL) { - last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF; - last_access->dwHighDateTime = access_ticks >> 32; - } - - if(last_write!=NULL) { - last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF; - last_write->dwHighDateTime = write_ticks >> 32; - } - - return(TRUE); -} - -static gboolean file_setfiletime(gpointer handle, - const WapiFileTime *create_time G_GNUC_UNUSED, - const WapiFileTime *last_access, - const WapiFileTime *last_write) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - struct utimbuf utbuf; - struct stat statbuf; - guint64 access_ticks, write_ticks; - int ret, fd; - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - fd = file_handle->fd; - - if(!(file_handle->fileaccess & GENERIC_WRITE) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - if(file_handle->filename == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p unknown filename", __func__, handle); - - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - /* Get the current times, so we can put the same times back in - * the event that one of the FileTime structs is NULL - */ - ret=fstat (fd, &statbuf); - if(ret==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle, - strerror(errno)); - - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - if(last_access!=NULL) { - access_ticks=((guint64)last_access->dwHighDateTime << 32) + - last_access->dwLowDateTime; - /* This is (time_t)0. We can actually go to INT_MIN, - * but this will do for now. - */ - if (access_ticks < 116444736000000000ULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set access time too early", - __func__); - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - if (sizeof (utbuf.actime) == 4 && ((access_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time that is too big for a 32bits time_t", - __func__); - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000; - } else { - utbuf.actime=statbuf.st_atime; - } - - if(last_write!=NULL) { - write_ticks=((guint64)last_write->dwHighDateTime << 32) + - last_write->dwLowDateTime; - /* This is (time_t)0. We can actually go to INT_MIN, - * but this will do for now. - */ - if (write_ticks < 116444736000000000ULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time too early", - __func__); - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - if (sizeof (utbuf.modtime) == 4 && ((write_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time that is too big for a 32bits time_t", - __func__); - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000; - } else { - utbuf.modtime=statbuf.st_mtime; - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting handle %p access %ld write %ld", __func__, - handle, utbuf.actime, utbuf.modtime); - - ret = _wapi_utime (file_handle->filename, &utbuf); - if (ret == -1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p [%s] utime failed: %s", __func__, - handle, file_handle->filename, strerror(errno)); - - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - return(TRUE); -} - -static void console_close (gpointer handle, gpointer data) -{ - struct _WapiHandle_file *console_handle = (struct _WapiHandle_file *)data; - int fd = console_handle->fd; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing console handle %p", __func__, handle); - - g_free (console_handle->filename); - - if (fd > 2) { - if (console_handle->share_info) - _wapi_handle_share_release (console_handle->share_info); - close (fd); - } -} - -static void console_details (gpointer data) -{ - file_details (data); -} - -static const gchar* console_typename (void) -{ - return "Console"; -} - -static gsize console_typesize (void) -{ - return sizeof (struct _WapiHandle_file); -} - -static WapiFileType console_getfiletype(void) -{ - return(FILE_TYPE_CHAR); -} - -static gboolean console_read(gpointer handle, gpointer buffer, - guint32 numbytes, guint32 *bytesread, - WapiOverlapped *overlapped G_GNUC_UNUSED) -{ - struct _WapiHandle_file *console_handle; - gboolean ok; - int ret, fd; - MonoThreadInfo *info = mono_thread_info_current (); - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE, - (gpointer *)&console_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up console handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - fd = console_handle->fd; - - if(bytesread!=NULL) { - *bytesread=0; - } - - if(!(console_handle->fileaccess & GENERIC_READ) && - !(console_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u", - __func__, handle, console_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - do { - ret=read(fd, buffer, numbytes); - } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); - - if(ret==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, handle, - strerror(errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } - - if(bytesread!=NULL) { - *bytesread=ret; - } - - return(TRUE); -} - -static gboolean console_write(gpointer handle, gconstpointer buffer, - guint32 numbytes, guint32 *byteswritten, - WapiOverlapped *overlapped G_GNUC_UNUSED) -{ - struct _WapiHandle_file *console_handle; - gboolean ok; - int ret, fd; - MonoThreadInfo *info = mono_thread_info_current (); - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE, - (gpointer *)&console_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up console handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - fd = console_handle->fd; - - if(byteswritten!=NULL) { - *byteswritten=0; - } - - if(!(console_handle->fileaccess & GENERIC_WRITE) && - !(console_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, console_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - do { - ret = write(fd, buffer, numbytes); - } while (ret == -1 && errno == EINTR && - !mono_thread_info_is_interrupt_state (info)); - - if (ret == -1) { - if (errno == EINTR) { - ret = 0; - } else { - _wapi_set_last_error_from_errno (); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", - __func__, handle, strerror(errno)); - - return(FALSE); - } - } - if(byteswritten!=NULL) { - *byteswritten=ret; - } - - return(TRUE); -} - -static const gchar* find_typename (void) -{ - return "Find"; -} - -static gsize find_typesize (void) -{ - return sizeof (struct _WapiHandle_find); -} - -static void pipe_close (gpointer handle, gpointer data) -{ - struct _WapiHandle_file *pipe_handle = (struct _WapiHandle_file*)data; - int fd = pipe_handle->fd; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing pipe handle %p fd %d", __func__, handle, fd); - - /* No filename with pipe handles */ - - if (pipe_handle->share_info) - _wapi_handle_share_release (pipe_handle->share_info); - - close (fd); -} - -static void pipe_details (gpointer data) -{ - file_details (data); -} - -static const gchar* pipe_typename (void) -{ - return "Pipe"; -} - -static gsize pipe_typesize (void) -{ - return sizeof (struct _WapiHandle_file); -} - -static WapiFileType pipe_getfiletype(void) -{ - return(FILE_TYPE_PIPE); -} - -static gboolean pipe_read (gpointer handle, gpointer buffer, - guint32 numbytes, guint32 *bytesread, - WapiOverlapped *overlapped G_GNUC_UNUSED) -{ - struct _WapiHandle_file *pipe_handle; - gboolean ok; - int ret, fd; - MonoThreadInfo *info = mono_thread_info_current (); - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE, - (gpointer *)&pipe_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up pipe handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - fd = pipe_handle->fd; - - if(bytesread!=NULL) { - *bytesread=0; - } - - if(!(pipe_handle->fileaccess & GENERIC_READ) && - !(pipe_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u", - __func__, handle, pipe_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: reading up to %d bytes from pipe %p", __func__, - numbytes, handle); - - do { - ret=read(fd, buffer, numbytes); - } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); - - if (ret == -1) { - if (errno == EINTR) { - ret = 0; - } else { - _wapi_set_last_error_from_errno (); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, - handle, strerror(errno)); - - return(FALSE); - } - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read %d bytes from pipe %p", __func__, ret, handle); - - if(bytesread!=NULL) { - *bytesread=ret; - } - - return(TRUE); -} - -static gboolean pipe_write(gpointer handle, gconstpointer buffer, - guint32 numbytes, guint32 *byteswritten, - WapiOverlapped *overlapped G_GNUC_UNUSED) -{ - struct _WapiHandle_file *pipe_handle; - gboolean ok; - int ret, fd; - MonoThreadInfo *info = mono_thread_info_current (); - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE, - (gpointer *)&pipe_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up pipe handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - fd = pipe_handle->fd; - - if(byteswritten!=NULL) { - *byteswritten=0; - } - - if(!(pipe_handle->fileaccess & GENERIC_WRITE) && - !(pipe_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, pipe_handle->fileaccess); - - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: writing up to %d bytes to pipe %p", __func__, numbytes, - handle); - - do { - ret = write (fd, buffer, numbytes); - } while (ret == -1 && errno == EINTR && - !mono_thread_info_is_interrupt_state (info)); - - if (ret == -1) { - if (errno == EINTR) { - ret = 0; - } else { - _wapi_set_last_error_from_errno (); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", __func__, - handle, strerror(errno)); - - return(FALSE); - } - } - if(byteswritten!=NULL) { - *byteswritten=ret; - } - - return(TRUE); -} - -static int convert_flags(guint32 fileaccess, guint32 createmode) -{ - int flags=0; - - switch(fileaccess) { - case GENERIC_READ: - flags=O_RDONLY; - break; - case GENERIC_WRITE: - flags=O_WRONLY; - break; - case GENERIC_READ|GENERIC_WRITE: - flags=O_RDWR; - break; - default: - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown access type 0x%x", __func__, - fileaccess); - break; - } - - switch(createmode) { - case CREATE_NEW: - flags|=O_CREAT|O_EXCL; - break; - case CREATE_ALWAYS: - flags|=O_CREAT|O_TRUNC; - break; - case OPEN_EXISTING: - break; - case OPEN_ALWAYS: - flags|=O_CREAT; - break; - case TRUNCATE_EXISTING: - flags|=O_TRUNC; - break; - default: - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown create mode 0x%x", __func__, - createmode); - break; - } - - return(flags); -} - -#if 0 /* unused */ -static mode_t convert_perms(guint32 sharemode) -{ - mode_t perms=0600; - - if(sharemode&FILE_SHARE_READ) { - perms|=044; - } - if(sharemode&FILE_SHARE_WRITE) { - perms|=022; - } - - return(perms); -} -#endif - -static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode, - guint32 fileaccess, - struct _WapiFileShare **share_info) -{ - gboolean file_already_shared; - guint32 file_existing_share, file_existing_access; - - file_already_shared = _wapi_handle_get_or_set_share (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info); - - if (file_already_shared) { - /* The reference to this share info was incremented - * when we looked it up, so be careful to put it back - * if we conclude we can't use this file. - */ - if (file_existing_share == 0) { - /* Quick and easy, no possibility to share */ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess); - - _wapi_handle_share_release (*share_info); - - return(FALSE); - } - - if (((file_existing_share == FILE_SHARE_READ) && - (fileaccess != GENERIC_READ)) || - ((file_existing_share == FILE_SHARE_WRITE) && - (fileaccess != GENERIC_WRITE))) { - /* New access mode doesn't match up */ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, fileaccess, file_existing_share); - - _wapi_handle_share_release (*share_info); - - return(FALSE); - } - - if (((file_existing_access & GENERIC_READ) && - !(sharemode & FILE_SHARE_READ)) || - ((file_existing_access & GENERIC_WRITE) && - !(sharemode & FILE_SHARE_WRITE))) { - /* New share mode doesn't match up */ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__, sharemode, file_existing_access); - - _wapi_handle_share_release (*share_info); - - return(FALSE); - } - } else { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__); - } - - return(TRUE); -} - - -static gboolean -share_allows_delete (struct stat *statbuf, struct _WapiFileShare **share_info) -{ - gboolean file_already_shared; - guint32 file_existing_share, file_existing_access; - - file_already_shared = _wapi_handle_get_or_set_share (statbuf->st_dev, statbuf->st_ino, FILE_SHARE_DELETE, GENERIC_READ, &file_existing_share, &file_existing_access, share_info); - - if (file_already_shared) { - /* The reference to this share info was incremented - * when we looked it up, so be careful to put it back - * if we conclude we can't use this file. - */ - if (file_existing_share == 0) { - /* Quick and easy, no possibility to share */ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, (*share_info)->access); - - _wapi_handle_share_release (*share_info); - - return(FALSE); - } - - if (!(file_existing_share & FILE_SHARE_DELETE)) { - /* New access mode doesn't match up */ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, (*share_info)->access, file_existing_share); - - _wapi_handle_share_release (*share_info); - - return(FALSE); - } - } else { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__); - } - - return(TRUE); -} - -/** - * CreateFile: - * @name: a pointer to a NULL-terminated unicode string, that names - * the file or other object to create. - * @fileaccess: specifies the file access mode - * @sharemode: whether the file should be shared. This parameter is - * currently ignored. - * @security: Ignored for now. - * @createmode: specifies whether to create a new file, whether to - * overwrite an existing file, whether to truncate the file, etc. - * @attrs: specifies file attributes and flags. On win32 attributes - * are characteristics of the file, not the handle, and are ignored - * when an existing file is opened. Flags give the library hints on - * how to process a file to optimise performance. - * @template: the handle of an open %GENERIC_READ file that specifies - * attributes to apply to a newly created file, ignoring @attrs. - * Normally this parameter is NULL. This parameter is ignored when an - * existing file is opened. - * - * Creates a new file handle. This only applies to normal files: - * pipes are handled by CreatePipe(), and console handles are created - * with GetStdHandle(). - * - * Return value: the new handle, or %INVALID_HANDLE_VALUE on error. - */ -gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, - guint32 sharemode, WapiSecurityAttributes *security, - guint32 createmode, guint32 attrs, - gpointer template_ G_GNUC_UNUSED) -{ - struct _WapiHandle_file file_handle = {0}; - gpointer handle; - int flags=convert_flags(fileaccess, createmode); - /*mode_t perms=convert_perms(sharemode);*/ - /* we don't use sharemode, because that relates to sharing of - * the file when the file is open and is already handled by - * other code, perms instead are the on-disk permissions and - * this is a sane default. - */ - mode_t perms=0666; - gchar *filename; - int fd, ret; - MonoW32HandleType handle_type; - struct stat statbuf; - - if (attrs & FILE_ATTRIBUTE_TEMPORARY) - perms = 0600; - - if (attrs & FILE_ATTRIBUTE_ENCRYPTED){ - SetLastError (ERROR_ENCRYPTION_FAILED); - return INVALID_HANDLE_VALUE; - } - - if (name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(INVALID_HANDLE_VALUE); - } - - filename = mono_unicode_to_external (name); - if (filename == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(INVALID_HANDLE_VALUE); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening %s with share 0x%x and access 0x%x", __func__, - filename, sharemode, fileaccess); - - fd = _wapi_open (filename, flags, perms); - - /* If we were trying to open a directory with write permissions - * (e.g. O_WRONLY or O_RDWR), this call will fail with - * EISDIR. However, this is a bit bogus because calls to - * manipulate the directory (e.g. SetFileTime) will still work on - * the directory because they use other API calls - * (e.g. utime()). Hence, if we failed with the EISDIR error, try - * to open the directory again without write permission. - */ - if (fd == -1 && errno == EISDIR) - { - /* Try again but don't try to make it writable */ - fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms); - } - - if (fd == -1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s: %s", __func__, filename, - strerror(errno)); - _wapi_set_last_path_error_from_errno (NULL, filename); - g_free (filename); - - return(INVALID_HANDLE_VALUE); - } - - if (fd >= mono_w32handle_fd_reserve) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__); - - SetLastError (ERROR_TOO_MANY_OPEN_FILES); - - close (fd); - g_free (filename); - - return(INVALID_HANDLE_VALUE); - } - - ret = fstat (fd, &statbuf); - if (ret == -1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fstat error of file %s: %s", __func__, - filename, strerror (errno)); - _wapi_set_last_error_from_errno (); - g_free (filename); - close (fd); - - return(INVALID_HANDLE_VALUE); - } -#ifdef __native_client__ - /* Workaround: Native Client currently returns the same fake inode - * for all files, so do a simple hash on the filename so we don't - * use the same share info for each file. - */ - statbuf.st_ino = g_str_hash(filename); -#endif - - if (share_allows_open (&statbuf, sharemode, fileaccess, - &file_handle.share_info) == FALSE) { - SetLastError (ERROR_SHARING_VIOLATION); - g_free (filename); - close (fd); - - return (INVALID_HANDLE_VALUE); - } - if (file_handle.share_info == NULL) { - /* No space, so no more files can be opened */ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No space in the share table", __func__); - - SetLastError (ERROR_TOO_MANY_OPEN_FILES); - close (fd); - g_free (filename); - - return(INVALID_HANDLE_VALUE); - } - - file_handle.filename = filename; - - if(security!=NULL) { - //file_handle->security_attributes=_wapi_handle_scratch_store ( - //security, sizeof(WapiSecurityAttributes)); - } - - file_handle.fd = fd; - file_handle.fileaccess=fileaccess; - file_handle.sharemode=sharemode; - file_handle.attrs=attrs; - -#ifdef HAVE_POSIX_FADVISE - if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) - posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL); - if (attrs & FILE_FLAG_RANDOM_ACCESS) - posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM); -#endif - -#ifdef F_RDAHEAD - if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) - fcntl(fd, F_RDAHEAD, 1); -#endif - -#ifndef S_ISFIFO -#define S_ISFIFO(m) ((m & S_IFIFO) != 0) -#endif - if (S_ISFIFO (statbuf.st_mode)) { - handle_type = MONO_W32HANDLE_PIPE; - /* maintain invariant that pipes have no filename */ - file_handle.filename = NULL; - g_free (filename); - filename = NULL; - } else if (S_ISCHR (statbuf.st_mode)) { - handle_type = MONO_W32HANDLE_CONSOLE; - } else { - handle_type = MONO_W32HANDLE_FILE; - } - - handle = mono_w32handle_new_fd (handle_type, fd, &file_handle); - if (handle == INVALID_HANDLE_VALUE) { - g_warning ("%s: error creating file handle", __func__); - g_free (filename); - close (fd); - - SetLastError (ERROR_GEN_FAILURE); - return(INVALID_HANDLE_VALUE); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle); - - return(handle); -} - -/** - * DeleteFile: - * @name: a pointer to a NULL-terminated unicode string, that names - * the file to be deleted. - * - * Deletes file @name. - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean DeleteFile(const gunichar2 *name) -{ - gchar *filename; - int retval; - gboolean ret = FALSE; - guint32 attrs; -#if 0 - struct stat statbuf; - struct _WapiFileShare *shareinfo; -#endif - - if(name==NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - filename=mono_unicode_to_external(name); - if(filename==NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - attrs = GetFileAttributes (name); - if (attrs == INVALID_FILE_ATTRIBUTES) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file attributes error", __func__); - /* Error set by GetFileAttributes() */ - g_free (filename); - return(FALSE); - } - -#if 0 - /* Check to make sure sharing allows us to open the file for - * writing. See bug 323389. - * - * Do the checks that don't need an open file descriptor, for - * simplicity's sake. If we really have to do the full checks - * then we can implement that later. - */ - if (_wapi_stat (filename, &statbuf) < 0) { - _wapi_set_last_path_error_from_errno (NULL, filename); - g_free (filename); - return(FALSE); - } - - if (share_allows_open (&statbuf, 0, GENERIC_WRITE, - &shareinfo) == FALSE) { - SetLastError (ERROR_SHARING_VIOLATION); - g_free (filename); - return FALSE; - } - if (shareinfo) - _wapi_handle_share_release (shareinfo); -#endif - - retval = _wapi_unlink (filename); - - if (retval == -1) { - _wapi_set_last_path_error_from_errno (NULL, filename); - } else { - ret = TRUE; - } - - g_free(filename); - - return(ret); -} - -/** - * MoveFile: - * @name: a pointer to a NULL-terminated unicode string, that names - * the file to be moved. - * @dest_name: a pointer to a NULL-terminated unicode string, that is the - * new name for the file. - * - * Renames file @name to @dest_name. - * MoveFile sets ERROR_ALREADY_EXISTS if the destination exists, except - * when it is the same file as the source. In that case it silently succeeds. - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name) -{ - gchar *utf8_name, *utf8_dest_name; - int result, errno_copy; - struct stat stat_src, stat_dest; - gboolean ret = FALSE; - struct _WapiFileShare *shareinfo; - - if(name==NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_name = mono_unicode_to_external (name); - if (utf8_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return FALSE; - } - - if(dest_name==NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - g_free (utf8_name); - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_dest_name = mono_unicode_to_external (dest_name); - if (utf8_dest_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - g_free (utf8_name); - SetLastError (ERROR_INVALID_NAME); - return FALSE; - } - - /* - * In C# land we check for the existence of src, but not for dest. - * We check it here and return the failure if dest exists and is not - * the same file as src. - */ - if (_wapi_stat (utf8_name, &stat_src) < 0) { - if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) { - _wapi_set_last_path_error_from_errno (NULL, utf8_name); - g_free (utf8_name); - g_free (utf8_dest_name); - return FALSE; - } - } - - if (!_wapi_stat (utf8_dest_name, &stat_dest)) { - if (stat_dest.st_dev != stat_src.st_dev || - stat_dest.st_ino != stat_src.st_ino) { - g_free (utf8_name); - g_free (utf8_dest_name); - SetLastError (ERROR_ALREADY_EXISTS); - return FALSE; - } - } - - /* Check to make that we have delete sharing permission. - * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009 - * - * Do the checks that don't need an open file descriptor, for - * simplicity's sake. If we really have to do the full checks - * then we can implement that later. - */ - if (share_allows_delete (&stat_src, &shareinfo) == FALSE) { - SetLastError (ERROR_SHARING_VIOLATION); - return FALSE; - } - if (shareinfo) - _wapi_handle_share_release (shareinfo); - - result = _wapi_rename (utf8_name, utf8_dest_name); - errno_copy = errno; - - if (result == -1) { - switch(errno_copy) { - case EEXIST: - SetLastError (ERROR_ALREADY_EXISTS); - break; - - case EXDEV: - /* Ignore here, it is dealt with below */ - break; - - case ENOENT: - /* We already know src exists. Must be dest that doesn't exist. */ - _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name); - break; - - default: - _wapi_set_last_error_from_errno (); - } - } - - g_free (utf8_name); - g_free (utf8_dest_name); - - if (result != 0 && errno_copy == EXDEV) { - if (S_ISDIR (stat_src.st_mode)) { - SetLastError (ERROR_NOT_SAME_DEVICE); - return FALSE; - } - /* Try a copy to the new location, and delete the source */ - if (CopyFile (name, dest_name, TRUE)==FALSE) { - /* CopyFile will set the error */ - return(FALSE); - } - - return(DeleteFile (name)); - } - - if (result == 0) { - ret = TRUE; - } - - return(ret); -} - -static gboolean -write_file (int src_fd, int dest_fd, struct stat *st_src, gboolean report_errors) -{ - int remain, n; - char *buf, *wbuf; - int buf_size = st_src->st_blksize; - MonoThreadInfo *info = mono_thread_info_current (); - - buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size); - buf = (char *) g_malloc (buf_size); - - for (;;) { - remain = read (src_fd, buf, buf_size); - if (remain < 0) { - if (errno == EINTR && !mono_thread_info_is_interrupt_state (info)) - continue; - - if (report_errors) - _wapi_set_last_error_from_errno (); - - g_free (buf); - return FALSE; - } - if (remain == 0) { - break; - } - - wbuf = buf; - while (remain > 0) { - if ((n = write (dest_fd, wbuf, remain)) < 0) { - if (errno == EINTR && !mono_thread_info_is_interrupt_state (info)) - continue; - - if (report_errors) - _wapi_set_last_error_from_errno (); - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write failed.", __func__); - g_free (buf); - return FALSE; - } - - remain -= n; - wbuf += n; - } - } - - g_free (buf); - return TRUE ; -} - -/** - * CopyFile: - * @name: a pointer to a NULL-terminated unicode string, that names - * the file to be copied. - * @dest_name: a pointer to a NULL-terminated unicode string, that is the - * new name for the file. - * @fail_if_exists: if TRUE and dest_name exists, the copy will fail. - * - * Copies file @name to @dest_name - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, - gboolean fail_if_exists) -{ - gchar *utf8_src, *utf8_dest; - int src_fd, dest_fd; - struct stat st, dest_st; - struct utimbuf dest_time; - gboolean ret = TRUE; - int ret_utime; - - if(name==NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_src = mono_unicode_to_external (name); - if (utf8_src == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of source returned NULL", - __func__); - - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - if(dest_name==NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: dest is NULL", __func__); - - g_free (utf8_src); - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_dest = mono_unicode_to_external (dest_name); - if (utf8_dest == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of dest returned NULL", - __func__); - - SetLastError (ERROR_INVALID_PARAMETER); - - g_free (utf8_src); - - return(FALSE); - } - - src_fd = _wapi_open (utf8_src, O_RDONLY, 0); - if (src_fd < 0) { - _wapi_set_last_path_error_from_errno (NULL, utf8_src); - - g_free (utf8_src); - g_free (utf8_dest); - - return(FALSE); - } - - if (fstat (src_fd, &st) < 0) { - _wapi_set_last_error_from_errno (); - - g_free (utf8_src); - g_free (utf8_dest); - close (src_fd); - - return(FALSE); - } - - /* Before trying to open/create the dest, we need to report a 'file busy' - * error if src and dest are actually the same file. We do the check here to take - * advantage of the IOMAP capability */ - if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev && - st.st_ino == dest_st.st_ino) { - - g_free (utf8_src); - g_free (utf8_dest); - close (src_fd); - - SetLastError (ERROR_SHARING_VIOLATION); - return (FALSE); - } - - if (fail_if_exists) { - dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode); - } else { - /* FIXME: it kinda sucks that this code path potentially scans - * the directory twice due to the weird SetLastError() - * behavior. */ - dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode); - if (dest_fd < 0) { - /* The file does not exist, try creating it */ - dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode); - } else { - /* Apparently this error is set if we - * overwrite the dest file - */ - SetLastError (ERROR_ALREADY_EXISTS); - } - } - if (dest_fd < 0) { - _wapi_set_last_error_from_errno (); - - g_free (utf8_src); - g_free (utf8_dest); - close (src_fd); - - return(FALSE); - } - - if (!write_file (src_fd, dest_fd, &st, TRUE)) - ret = FALSE; - - close (src_fd); - close (dest_fd); - - dest_time.modtime = st.st_mtime; - dest_time.actime = st.st_atime; - ret_utime = utime (utf8_dest, &dest_time); - if (ret_utime == -1) - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file [%s] utime failed: %s", __func__, utf8_dest, strerror(errno)); - - g_free (utf8_src); - g_free (utf8_dest); - - return ret; -} - -static gchar* -convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name) -{ - gchar *utf8_ret; - - if (arg == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s is NULL", __func__, arg_name); - SetLastError (ERROR_INVALID_NAME); - return NULL; - } - - utf8_ret = mono_unicode_to_external (arg); - if (utf8_ret == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of %s returned NULL", - __func__, arg_name); - SetLastError (ERROR_INVALID_PARAMETER); - return NULL; - } - - return utf8_ret; -} - -gboolean -ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, - const gunichar2 *backupFileName, guint32 replaceFlags, - gpointer exclude, gpointer reserved) -{ - int result, backup_fd = -1,replaced_fd = -1; - gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL; - struct stat stBackup; - gboolean ret = FALSE; - - if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName"))) - return FALSE; - if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName"))) - goto replace_cleanup; - if (backupFileName != NULL) { - if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName"))) - goto replace_cleanup; - } - - if (utf8_backupFileName) { - // Open the backup file for read so we can restore the file if an error occurs. - backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0); - result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName); - if (result == -1) - goto replace_cleanup; - } - - result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName); - if (result == -1) { - _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName); - _wapi_rename (utf8_backupFileName, utf8_replacedFileName); - if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) { - replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC, - stBackup.st_mode); - - if (replaced_fd == -1) - goto replace_cleanup; - - write_file (backup_fd, replaced_fd, &stBackup, FALSE); - } - - goto replace_cleanup; - } - - ret = TRUE; - -replace_cleanup: - g_free (utf8_replacedFileName); - g_free (utf8_replacementFileName); - g_free (utf8_backupFileName); - if (backup_fd != -1) - close (backup_fd); - if (replaced_fd != -1) - close (replaced_fd); - return ret; -} - -/** - * GetStdHandle: - * @stdhandle: specifies the file descriptor - * - * Returns a handle for stdin, stdout, or stderr. Always returns the - * same handle for the same @stdhandle. - * - * Return value: the handle, or %INVALID_HANDLE_VALUE on error - */ - -static mono_mutex_t stdhandle_mutex; - -gpointer GetStdHandle(WapiStdHandle stdhandle) -{ - struct _WapiHandle_file *file_handle; - gpointer handle; - int fd; - const gchar *name; - gboolean ok; - - switch(stdhandle) { - case STD_INPUT_HANDLE: - fd = 0; - name = ""; - break; - - case STD_OUTPUT_HANDLE: - fd = 1; - name = ""; - break; - - case STD_ERROR_HANDLE: - fd = 2; - name = ""; - break; - - default: - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unknown standard handle type", __func__); - - SetLastError (ERROR_INVALID_PARAMETER); - return(INVALID_HANDLE_VALUE); - } - - handle = GINT_TO_POINTER (fd); - - mono_os_mutex_lock (&stdhandle_mutex); - - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE, - (gpointer *)&file_handle); - if (ok == FALSE) { - /* Need to create this console handle */ - handle = _wapi_stdhandle_create (fd, name); - - if (handle == INVALID_HANDLE_VALUE) { - SetLastError (ERROR_NO_MORE_FILES); - goto done; - } - } else { - /* Add a reference to this handle */ - mono_w32handle_ref (handle); - } - - done: - mono_os_mutex_unlock (&stdhandle_mutex); - - return(handle); -} - -/** - * ReadFile: - * @handle: The file handle to read from. The handle must have - * %GENERIC_READ access. - * @buffer: The buffer to store read data in - * @numbytes: The maximum number of bytes to read - * @bytesread: The actual number of bytes read is stored here. This - * value can be zero if the handle is positioned at the end of the - * file. - * @overlapped: points to a required %WapiOverlapped structure if - * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL - * otherwise. - * - * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this - * function reads up to @numbytes bytes from the file from the current - * file position, and stores them in @buffer. If there are not enough - * bytes left in the file, just the amount available will be read. - * The actual number of bytes read is stored in @bytesread. - - * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current - * file position is ignored and the read position is taken from data - * in the @overlapped structure. - * - * Return value: %TRUE if the read succeeds (even if no bytes were - * read due to an attempt to read past the end of the file), %FALSE on - * error. - */ -gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes, - guint32 *bytesread, WapiOverlapped *overlapped) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if(io_ops[type].readfile==NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - return(io_ops[type].readfile (handle, buffer, numbytes, bytesread, - overlapped)); -} - -/** - * WriteFile: - * @handle: The file handle to write to. The handle must have - * %GENERIC_WRITE access. - * @buffer: The buffer to read data from. - * @numbytes: The maximum number of bytes to write. - * @byteswritten: The actual number of bytes written is stored here. - * If the handle is positioned at the file end, the length of the file - * is extended. This parameter may be %NULL. - * @overlapped: points to a required %WapiOverlapped structure if - * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL - * otherwise. - * - * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this - * function writes up to @numbytes bytes from @buffer to the file at - * the current file position. If @handle is positioned at the end of - * the file, the file is extended. The actual number of bytes written - * is stored in @byteswritten. - * - * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current - * file position is ignored and the write position is taken from data - * in the @overlapped structure. - * - * Return value: %TRUE if the write succeeds, %FALSE on error. - */ -gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes, - guint32 *byteswritten, WapiOverlapped *overlapped) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if(io_ops[type].writefile==NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten, - overlapped)); -} - -/** - * FlushFileBuffers: - * @handle: Handle to open file. The handle must have - * %GENERIC_WRITE access. - * - * Flushes buffers of the file and causes all unwritten data to - * be written. - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean FlushFileBuffers(gpointer handle) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if(io_ops[type].flushfile==NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - return(io_ops[type].flushfile (handle)); -} - -/** - * SetEndOfFile: - * @handle: The file handle to set. The handle must have - * %GENERIC_WRITE access. - * - * Moves the end-of-file position to the current position of the file - * pointer. This function is used to truncate or extend a file. - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean SetEndOfFile(gpointer handle) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if (io_ops[type].setendoffile == NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - return(io_ops[type].setendoffile (handle)); -} - -/** - * SetFilePointer: - * @handle: The file handle to set. The handle must have - * %GENERIC_READ or %GENERIC_WRITE access. - * @movedistance: Low 32 bits of a signed value that specifies the - * number of bytes to move the file pointer. - * @highmovedistance: Pointer to the high 32 bits of a signed value - * that specifies the number of bytes to move the file pointer, or - * %NULL. - * @method: The starting point for the file pointer move. - * - * Sets the file pointer of an open file. - * - * The distance to move the file pointer is calculated from - * @movedistance and @highmovedistance: If @highmovedistance is %NULL, - * @movedistance is the 32-bit signed value; otherwise, @movedistance - * is the low 32 bits and @highmovedistance a pointer to the high 32 - * bits of a 64 bit signed value. A positive distance moves the file - * pointer forward from the position specified by @method; a negative - * distance moves the file pointer backward. - * - * If the library is compiled without large file support, - * @highmovedistance is ignored and its value is set to zero on a - * successful return. - * - * Return value: On success, the low 32 bits of the new file pointer. - * If @highmovedistance is not %NULL, the high 32 bits of the new file - * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER. - */ -guint32 SetFilePointer(gpointer handle, gint32 movedistance, - gint32 *highmovedistance, WapiSeekMethod method) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if (io_ops[type].seek == NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(INVALID_SET_FILE_POINTER); - } - - return(io_ops[type].seek (handle, movedistance, highmovedistance, - method)); -} - -/** - * GetFileType: - * @handle: The file handle to test. - * - * Finds the type of file @handle. - * - * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is - * unknown. %FILE_TYPE_DISK - @handle is a disk file. - * %FILE_TYPE_CHAR - @handle is a character device, such as a console. - * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe. - */ -WapiFileType GetFileType(gpointer handle) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if (io_ops[type].getfiletype == NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(FILE_TYPE_UNKNOWN); - } - - return(io_ops[type].getfiletype ()); -} - -/** - * GetFileSize: - * @handle: The file handle to query. The handle must have - * %GENERIC_READ or %GENERIC_WRITE access. - * @highsize: If non-%NULL, the high 32 bits of the file size are - * stored here. - * - * Retrieves the size of the file @handle. - * - * If the library is compiled without large file support, @highsize - * has its value set to zero on a successful return. - * - * Return value: On success, the low 32 bits of the file size. If - * @highsize is non-%NULL then the high 32 bits of the file size are - * stored here. On failure %INVALID_FILE_SIZE is returned. - */ -guint32 GetFileSize(gpointer handle, guint32 *highsize) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if (io_ops[type].getfilesize == NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(INVALID_FILE_SIZE); - } - - return(io_ops[type].getfilesize (handle, highsize)); -} - -/** - * GetFileTime: - * @handle: The file handle to query. The handle must have - * %GENERIC_READ access. - * @create_time: Points to a %WapiFileTime structure to receive the - * number of ticks since the epoch that file was created. May be - * %NULL. - * @last_access: Points to a %WapiFileTime structure to receive the - * number of ticks since the epoch when file was last accessed. May be - * %NULL. - * @last_write: Points to a %WapiFileTime structure to receive the - * number of ticks since the epoch when file was last written to. May - * be %NULL. - * - * Finds the number of ticks since the epoch that the file referenced - * by @handle was created, last accessed and last modified. A tick is - * a 100 nanosecond interval. The epoch is Midnight, January 1 1601 - * GMT. - * - * Create time isn't recorded on POSIX file systems or reported by - * stat(2), so that time is guessed by returning the oldest of the - * other times. - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean GetFileTime(gpointer handle, WapiFileTime *create_time, - WapiFileTime *last_access, WapiFileTime *last_write) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if (io_ops[type].getfiletime == NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - return(io_ops[type].getfiletime (handle, create_time, last_access, - last_write)); -} - -/** - * SetFileTime: - * @handle: The file handle to set. The handle must have - * %GENERIC_WRITE access. - * @create_time: Points to a %WapiFileTime structure that contains the - * number of ticks since the epoch that the file was created. May be - * %NULL. - * @last_access: Points to a %WapiFileTime structure that contains the - * number of ticks since the epoch when the file was last accessed. - * May be %NULL. - * @last_write: Points to a %WapiFileTime structure that contains the - * number of ticks since the epoch when the file was last written to. - * May be %NULL. - * - * Sets the number of ticks since the epoch that the file referenced - * by @handle was created, last accessed or last modified. A tick is - * a 100 nanosecond interval. The epoch is Midnight, January 1 1601 - * GMT. - * - * Create time isn't recorded on POSIX file systems, and is ignored. - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time, - const WapiFileTime *last_access, - const WapiFileTime *last_write) -{ - MonoW32HandleType type; - - type = mono_w32handle_get_type (handle); - - if (io_ops[type].setfiletime == NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - return(io_ops[type].setfiletime (handle, create_time, last_access, - last_write)); -} - -/* A tick is a 100-nanosecond interval. File time epoch is Midnight, - * January 1 1601 GMT - */ - -#define TICKS_PER_MILLISECOND 10000L -#define TICKS_PER_SECOND 10000000L -#define TICKS_PER_MINUTE 600000000L -#define TICKS_PER_HOUR 36000000000LL -#define TICKS_PER_DAY 864000000000LL - -#define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) - -static const guint16 mon_yday[2][13]={ - {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, - {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, -}; - -/** - * FileTimeToSystemTime: - * @file_time: Points to a %WapiFileTime structure that contains the - * number of ticks to convert. - * @system_time: Points to a %WapiSystemTime structure to receive the - * broken-out time. - * - * Converts a tick count into broken-out time values. - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean FileTimeToSystemTime(const WapiFileTime *file_time, - WapiSystemTime *system_time) -{ - gint64 file_ticks, totaldays, rem, y; - const guint16 *ip; - - if(system_time==NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: system_time NULL", __func__); - - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - file_ticks=((gint64)file_time->dwHighDateTime << 32) + - file_time->dwLowDateTime; - - /* Really compares if file_ticks>=0x8000000000000000 - * (LLONG_MAX+1) but we're working with a signed value for the - * year and day calculation to work later - */ - if(file_ticks<0) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file_time too big", __func__); - - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - totaldays=(file_ticks / TICKS_PER_DAY); - rem = file_ticks % TICKS_PER_DAY; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld rem: %lld", __func__, totaldays, rem); - - system_time->wHour=rem/TICKS_PER_HOUR; - rem %= TICKS_PER_HOUR; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem); - - system_time->wMinute = rem / TICKS_PER_MINUTE; - rem %= TICKS_PER_MINUTE; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Minute: %d rem: %lld", __func__, system_time->wMinute, - rem); - - system_time->wSecond = rem / TICKS_PER_SECOND; - rem %= TICKS_PER_SECOND; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Second: %d rem: %lld", __func__, system_time->wSecond, - rem); - - system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Milliseconds: %d", __func__, - system_time->wMilliseconds); - - /* January 1, 1601 was a Monday, according to Emacs calendar */ - system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day of week: %d", __func__, system_time->wDayOfWeek); - - /* This algorithm to find year and month given days from epoch - * from glibc - */ - y=1601; - -#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) -#define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400)) - - while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) { - /* Guess a corrected year, assuming 365 days per year */ - gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0); - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld yg: %lld y: %lld", __func__, - totaldays, yg, - y); - g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__, - LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1)); - - /* Adjust days and y to match the guessed year. */ - totaldays -= ((yg - y) * 365 - + LEAPS_THRU_END_OF (yg - 1) - - LEAPS_THRU_END_OF (y - 1)); - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays); - y = yg; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: y: %lld", __func__, y); - } - - system_time->wYear = y; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Year: %d", __func__, system_time->wYear); - - ip = mon_yday[isleap(y)]; - - for(y=11; totaldays < ip[y]; --y) { - continue; - } - totaldays-=ip[y]; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays); - - system_time->wMonth = y + 1; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Month: %d", __func__, system_time->wMonth); - - system_time->wDay = totaldays + 1; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day: %d", __func__, system_time->wDay); - - return(TRUE); -} - -gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) -{ - struct _WapiHandle_find find_handle = {0}; - gpointer handle; - gchar *utf8_pattern = NULL, *dir_part, *entry_part; - int result; - - if (pattern == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pattern is NULL", __func__); - - SetLastError (ERROR_PATH_NOT_FOUND); - return(INVALID_HANDLE_VALUE); - } - - utf8_pattern = mono_unicode_to_external (pattern); - if (utf8_pattern == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(INVALID_HANDLE_VALUE); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for [%s]", __func__, utf8_pattern); - - /* Figure out which bit of the pattern is the directory */ - dir_part = _wapi_dirname (utf8_pattern); - entry_part = _wapi_basename (utf8_pattern); - -#if 0 - /* Don't do this check for now, it breaks if directories - * really do have metachars in their names (see bug 58116). - * FIXME: Figure out a better solution to keep some checks... - */ - if (strchr (dir_part, '*') || strchr (dir_part, '?')) { - SetLastError (ERROR_INVALID_NAME); - g_free (dir_part); - g_free (entry_part); - g_free (utf8_pattern); - return(INVALID_HANDLE_VALUE); - } -#endif - - /* The pattern can specify a directory or a set of files. - * - * The pattern can have wildcard characters ? and *, but only - * in the section after the last directory delimiter. (Return - * ERROR_INVALID_NAME if there are wildcards in earlier path - * sections.) "*" has the usual 0-or-more chars meaning. "?" - * means "match one character", "??" seems to mean "match one - * or two characters", "???" seems to mean "match one, two or - * three characters", etc. Windows will also try and match - * the mangled "short name" of files, so 8 character patterns - * with wildcards will show some surprising results. - * - * All the written documentation I can find says that '?' - * should only match one character, and doesn't mention '??', - * '???' etc. I'm going to assume that the strict behaviour - * (ie '???' means three and only three characters) is the - * correct one, because that lets me use fnmatch(3) rather - * than mess around with regexes. - */ - - find_handle.namelist = NULL; - result = _wapi_io_scandir (dir_part, entry_part, - &find_handle.namelist); - - if (result == 0) { - /* No files, which windows seems to call - * FILE_NOT_FOUND - */ - SetLastError (ERROR_FILE_NOT_FOUND); - g_free (utf8_pattern); - g_free (entry_part); - g_free (dir_part); - return (INVALID_HANDLE_VALUE); - } - - if (result < 0) { - _wapi_set_last_path_error_from_errno (dir_part, NULL); - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: scandir error: %s", __func__, g_strerror (errno)); - g_free (utf8_pattern); - g_free (entry_part); - g_free (dir_part); - return (INVALID_HANDLE_VALUE); - } - - g_free (utf8_pattern); - g_free (entry_part); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Got %d matches", __func__, result); - - find_handle.dir_part = dir_part; - find_handle.num = result; - find_handle.count = 0; - - handle = mono_w32handle_new (MONO_W32HANDLE_FIND, &find_handle); - if (handle == INVALID_HANDLE_VALUE) { - g_warning ("%s: error creating find handle", __func__); - g_free (dir_part); - g_free (entry_part); - g_free (utf8_pattern); - SetLastError (ERROR_GEN_FAILURE); - - return(INVALID_HANDLE_VALUE); - } - - if (handle != INVALID_HANDLE_VALUE && - !FindNextFile (handle, find_data)) { - FindClose (handle); - SetLastError (ERROR_NO_MORE_FILES); - handle = INVALID_HANDLE_VALUE; - } - - return (handle); -} - -gboolean FindNextFile (gpointer handle, WapiFindData *find_data) -{ - struct _WapiHandle_find *find_handle; - gboolean ok; - struct stat buf, linkbuf; - int result; - gchar *filename; - gchar *utf8_filename, *utf8_basename; - gunichar2 *utf16_basename; - time_t create_time; - glong bytes; - gboolean ret = FALSE; - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND, - (gpointer *)&find_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up find handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - mono_w32handle_lock_handle (handle); - -retry: - if (find_handle->count >= find_handle->num) { - SetLastError (ERROR_NO_MORE_FILES); - goto cleanup; - } - - /* stat next match */ - - filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL); - - result = _wapi_stat (filename, &buf); - if (result == -1 && errno == ENOENT) { - /* Might be a dangling symlink */ - result = _wapi_lstat (filename, &buf); - } - - if (result != 0) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: stat failed: %s", __func__, filename); - - g_free (filename); - goto retry; - } - -#ifndef __native_client__ - result = _wapi_lstat (filename, &linkbuf); - if (result != 0) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lstat failed: %s", __func__, filename); - - g_free (filename); - goto retry; - } -#endif - - utf8_filename = mono_utf8_from_external (filename); - if (utf8_filename == NULL) { - /* We couldn't turn this filename into utf8 (eg the - * encoding of the name wasn't convertible), so just - * ignore it. - */ - g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename); - - g_free (filename); - goto retry; - } - g_free (filename); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Found [%s]", __func__, utf8_filename); - - /* fill data block */ - - if (buf.st_mtime < buf.st_ctime) - create_time = buf.st_mtime; - else - create_time = buf.st_ctime; - -#ifdef __native_client__ - find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, NULL); -#else - find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf); -#endif - - time_t_to_filetime (create_time, &find_data->ftCreationTime); - time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime); - time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime); - - if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - find_data->nFileSizeHigh = 0; - find_data->nFileSizeLow = 0; - } else { - find_data->nFileSizeHigh = buf.st_size >> 32; - find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF; - } - - find_data->dwReserved0 = 0; - find_data->dwReserved1 = 0; - - utf8_basename = _wapi_basename (utf8_filename); - utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes, - NULL); - if(utf16_basename==NULL) { - g_free (utf8_basename); - g_free (utf8_filename); - goto retry; - } - ret = TRUE; - - /* utf16 is 2 * utf8 */ - bytes *= 2; - - memset (find_data->cFileName, '\0', (MAX_PATH*2)); - - /* Truncating a utf16 string like this might leave the last - * char incomplete - */ - memcpy (find_data->cFileName, utf16_basename, - bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2); - - find_data->cAlternateFileName [0] = 0; /* not used */ - - g_free (utf8_basename); - g_free (utf8_filename); - g_free (utf16_basename); - -cleanup: - mono_w32handle_unlock_handle (handle); - - return(ret); -} - -/** - * FindClose: - * @wapi_handle: the find handle to close. - * - * Closes find handle @wapi_handle - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean FindClose (gpointer handle) -{ - struct _WapiHandle_find *find_handle; - gboolean ok; - - if (handle == NULL) { - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND, - (gpointer *)&find_handle); - if(ok==FALSE) { - g_warning ("%s: error looking up find handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - mono_w32handle_lock_handle (handle); - - g_strfreev (find_handle->namelist); - g_free (find_handle->dir_part); - - mono_w32handle_unlock_handle (handle); - - mono_w32handle_unref (handle); - - return(TRUE); -} - -/** - * CreateDirectory: - * @name: a pointer to a NULL-terminated unicode string, that names - * the directory to be created. - * @security: ignored for now - * - * Creates directory @name - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean CreateDirectory (const gunichar2 *name, - WapiSecurityAttributes *security) -{ - gchar *utf8_name; - int result; - - if (name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_name = mono_unicode_to_external (name); - if (utf8_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return FALSE; - } - - result = _wapi_mkdir (utf8_name, 0777); - - if (result == 0) { - g_free (utf8_name); - return TRUE; - } - - _wapi_set_last_path_error_from_errno (NULL, utf8_name); - g_free (utf8_name); - return FALSE; -} - -/** - * RemoveDirectory: - * @name: a pointer to a NULL-terminated unicode string, that names - * the directory to be removed. - * - * Removes directory @name - * - * Return value: %TRUE on success, %FALSE otherwise. - */ -gboolean RemoveDirectory (const gunichar2 *name) -{ - gchar *utf8_name; - int result; - - if (name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_name = mono_unicode_to_external (name); - if (utf8_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return FALSE; - } - - result = _wapi_rmdir (utf8_name); - if (result == -1) { - _wapi_set_last_path_error_from_errno (NULL, utf8_name); - g_free (utf8_name); - - return(FALSE); - } - g_free (utf8_name); - - return(TRUE); -} - -/** - * GetFileAttributes: - * @name: a pointer to a NULL-terminated unicode filename. - * - * Gets the attributes for @name; - * - * Return value: %INVALID_FILE_ATTRIBUTES on failure - */ -guint32 GetFileAttributes (const gunichar2 *name) -{ - gchar *utf8_name; - struct stat buf, linkbuf; - int result; - guint32 ret; - - if (name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_name = mono_unicode_to_external (name); - if (utf8_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_PARAMETER); - return (INVALID_FILE_ATTRIBUTES); - } - - result = _wapi_stat (utf8_name, &buf); - if (result == -1 && errno == ENOENT) { - /* Might be a dangling symlink... */ - result = _wapi_lstat (utf8_name, &buf); - } - - if (result != 0) { - _wapi_set_last_path_error_from_errno (NULL, utf8_name); - g_free (utf8_name); - return (INVALID_FILE_ATTRIBUTES); - } - -#ifndef __native_client__ - result = _wapi_lstat (utf8_name, &linkbuf); - if (result != 0) { - _wapi_set_last_path_error_from_errno (NULL, utf8_name); - g_free (utf8_name); - return (INVALID_FILE_ATTRIBUTES); - } -#endif - -#ifdef __native_client__ - ret = _wapi_stat_to_file_attributes (utf8_name, &buf, NULL); -#else - ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf); -#endif - - g_free (utf8_name); - - return(ret); -} - -/** - * GetFileAttributesEx: - * @name: a pointer to a NULL-terminated unicode filename. - * @level: must be GetFileExInfoStandard - * @info: pointer to a WapiFileAttributesData structure - * - * Gets attributes, size and filetimes for @name; - * - * Return value: %TRUE on success, %FALSE on failure - */ -gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info) -{ - gchar *utf8_name; - WapiFileAttributesData *data; - - struct stat buf, linkbuf; - time_t create_time; - int result; - - if (level != GetFileExInfoStandard) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: info level %d not supported.", __func__, - level); - - SetLastError (ERROR_INVALID_PARAMETER); - return FALSE; - } - - if (name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_name = mono_unicode_to_external (name); - if (utf8_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_PARAMETER); - return FALSE; - } - - result = _wapi_stat (utf8_name, &buf); - if (result == -1 && errno == ENOENT) { - /* Might be a dangling symlink... */ - result = _wapi_lstat (utf8_name, &buf); - } - - if (result != 0) { - _wapi_set_last_path_error_from_errno (NULL, utf8_name); - g_free (utf8_name); - return FALSE; - } - - result = _wapi_lstat (utf8_name, &linkbuf); - if (result != 0) { - _wapi_set_last_path_error_from_errno (NULL, utf8_name); - g_free (utf8_name); - return(FALSE); - } - - /* fill data block */ - - data = (WapiFileAttributesData *)info; - - if (buf.st_mtime < buf.st_ctime) - create_time = buf.st_mtime; - else - create_time = buf.st_ctime; - - data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_name, - &buf, - &linkbuf); - - g_free (utf8_name); - - time_t_to_filetime (create_time, &data->ftCreationTime); - time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime); - time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime); - - if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - data->nFileSizeHigh = 0; - data->nFileSizeLow = 0; - } - else { - data->nFileSizeHigh = buf.st_size >> 32; - data->nFileSizeLow = buf.st_size & 0xFFFFFFFF; - } - - return TRUE; -} - -/** - * SetFileAttributes - * @name: name of file - * @attrs: attributes to set - * - * Changes the attributes on a named file. - * - * Return value: %TRUE on success, %FALSE on failure. - */ -extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs) -{ - /* FIXME: think of something clever to do on unix */ - gchar *utf8_name; - struct stat buf; - int result; - - /* - * Currently we only handle one *internal* case, with a value that is - * not standard: 0x80000000, which means `set executable bit' - */ - - if (name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - - utf8_name = mono_unicode_to_external (name); - if (utf8_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return FALSE; - } - - result = _wapi_stat (utf8_name, &buf); - if (result == -1 && errno == ENOENT) { - /* Might be a dangling symlink... */ - result = _wapi_lstat (utf8_name, &buf); - } - - if (result != 0) { - _wapi_set_last_path_error_from_errno (NULL, utf8_name); - g_free (utf8_name); - return FALSE; - } - - /* Contrary to the documentation, ms allows NORMAL to be - * specified along with other attributes, so dont bother to - * catch that case here. - */ - if (attrs & FILE_ATTRIBUTE_READONLY) { - result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP)); - } else { - result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR); - } - - /* Ignore the other attributes for now */ - - if (attrs & 0x80000000){ - mode_t exec_mask = 0; - - if ((buf.st_mode & S_IRUSR) != 0) - exec_mask |= S_IXUSR; - - if ((buf.st_mode & S_IRGRP) != 0) - exec_mask |= S_IXGRP; - - if ((buf.st_mode & S_IROTH) != 0) - exec_mask |= S_IXOTH; - - result = chmod (utf8_name, buf.st_mode | exec_mask); - } - /* Don't bother to reset executable (might need to change this - * policy) - */ - - g_free (utf8_name); - - return(TRUE); -} - -/** - * GetCurrentDirectory - * @length: size of the buffer - * @buffer: pointer to buffer that recieves path - * - * Retrieves the current directory for the current process. - * - * Return value: number of characters in buffer on success, zero on failure - */ -extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer) -{ - gunichar2 *utf16_path; - glong count; - gsize bytes; - -#ifdef __native_client__ - gchar *path = g_get_current_dir (); - if (length < strlen(path) + 1 || path == NULL) - return 0; - memcpy (buffer, path, strlen(path) + 1); -#else - if (getcwd ((char*)buffer, length) == NULL) { - if (errno == ERANGE) { /*buffer length is not big enough */ - gchar *path = g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/ - if (path == NULL) - return 0; - utf16_path = mono_unicode_from_external (path, &bytes); - g_free (utf16_path); - g_free (path); - return (bytes/2)+1; - } - _wapi_set_last_error_from_errno (); - return 0; - } -#endif - - utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes); - count = (bytes/2)+1; - g_assert (count <= length); /*getcwd must have failed before with ERANGE*/ - - /* Add the terminator */ - memset (buffer, '\0', bytes+2); - memcpy (buffer, utf16_path, bytes); - - g_free (utf16_path); - - return count; -} - -/** - * SetCurrentDirectory - * @path: path to new directory - * - * Changes the directory path for the current process. - * - * Return value: %TRUE on success, %FALSE on failure. - */ -extern gboolean SetCurrentDirectory (const gunichar2 *path) -{ - gchar *utf8_path; - gboolean result; - - if (path == NULL) { - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - utf8_path = mono_unicode_to_external (path); - if (_wapi_chdir (utf8_path) != 0) { - _wapi_set_last_error_from_errno (); - result = FALSE; - } - else - result = TRUE; - - g_free (utf8_path); - return result; -} - -gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe, - WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size) -{ - struct _WapiHandle_file pipe_read_handle = {0}; - struct _WapiHandle_file pipe_write_handle = {0}; - gpointer read_handle; - gpointer write_handle; - int filedes[2]; - int ret; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating pipe", __func__); - - ret=pipe (filedes); - if(ret==-1) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error creating pipe: %s", __func__, - strerror (errno)); - - _wapi_set_last_error_from_errno (); - return(FALSE); - } - - if (filedes[0] >= mono_w32handle_fd_reserve || - filedes[1] >= mono_w32handle_fd_reserve) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__); - - SetLastError (ERROR_TOO_MANY_OPEN_FILES); - - close (filedes[0]); - close (filedes[1]); - - return(FALSE); - } - - /* filedes[0] is open for reading, filedes[1] for writing */ - - pipe_read_handle.fd = filedes [0]; - pipe_read_handle.fileaccess = GENERIC_READ; - read_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[0], - &pipe_read_handle); - if (read_handle == INVALID_HANDLE_VALUE) { - g_warning ("%s: error creating pipe read handle", __func__); - close (filedes[0]); - close (filedes[1]); - SetLastError (ERROR_GEN_FAILURE); - - return(FALSE); - } - - pipe_write_handle.fd = filedes [1]; - pipe_write_handle.fileaccess = GENERIC_WRITE; - write_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[1], - &pipe_write_handle); - if (write_handle == INVALID_HANDLE_VALUE) { - g_warning ("%s: error creating pipe write handle", __func__); - mono_w32handle_unref (read_handle); - - close (filedes[0]); - close (filedes[1]); - SetLastError (ERROR_GEN_FAILURE); - - return(FALSE); - } - - *readpipe = read_handle; - *writepipe = write_handle; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning pipe: read handle %p, write handle %p", - __func__, read_handle, write_handle); - - return(TRUE); -} - -#ifdef HAVE_GETFSSTAT -/* Darwin has getfsstat */ -gint32 GetLogicalDriveStrings (guint32 len, gunichar2 *buf) -{ - struct statfs *stats; - int size, n, i; - gunichar2 *dir; - glong length, total = 0; - - n = getfsstat (NULL, 0, MNT_NOWAIT); - if (n == -1) - return 0; - size = n * sizeof (struct statfs); - stats = (struct statfs *) g_malloc (size); - if (stats == NULL) - return 0; - if (getfsstat (stats, size, MNT_NOWAIT) == -1){ - g_free (stats); - return 0; - } - for (i = 0; i < n; i++){ - dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL); - if (total + length < len){ - memcpy (buf + total, dir, sizeof (gunichar2) * length); - buf [total+length] = 0; - } - g_free (dir); - total += length + 1; - } - if (total < len) - buf [total] = 0; - total++; - g_free (stats); - return total; -} -#else -/* In-place octal sequence replacement */ -static void -unescape_octal (gchar *str) -{ - gchar *rptr; - gchar *wptr; - - if (str == NULL) - return; - - rptr = wptr = str; - while (*rptr != '\0') { - if (*rptr == '\\') { - char c; - rptr++; - c = (*(rptr++) - '0') << 6; - c += (*(rptr++) - '0') << 3; - c += *(rptr++) - '0'; - *wptr++ = c; - } else if (wptr != rptr) { - *wptr++ = *rptr++; - } else { - rptr++; wptr++; - } - } - *wptr = '\0'; -} -static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf); - -#if __linux__ -#define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512 -#define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512 -#define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64 - -typedef struct -{ - glong total; - guint32 buffer_index; - guint32 mountpoint_index; - guint32 field_number; - guint32 allocated_size; - guint32 fsname_index; - guint32 fstype_index; - gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1]; - gchar *mountpoint_allocated; - gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER]; - gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1]; - gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1]; - ssize_t nbytes; - gchar delimiter; - gboolean check_mount_source; -} LinuxMountInfoParseState; - -static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); -static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); -static void append_to_mountpoint (LinuxMountInfoParseState *state); -static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); - -gint32 GetLogicalDriveStrings (guint32 len, gunichar2 *buf) -{ - int fd; - gint32 ret = 0; - LinuxMountInfoParseState state; - gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL; - - memset (buf, 0, len * sizeof (gunichar2)); - fd = open ("/proc/self/mountinfo", O_RDONLY); - if (fd != -1) - parser = GetLogicalDriveStrings_MountInfo; - else { - fd = open ("/proc/mounts", O_RDONLY); - if (fd != -1) - parser = GetLogicalDriveStrings_Mounts; - } - - if (!parser) { - ret = GetLogicalDriveStrings_Mtab (len, buf); - goto done_and_out; - } - - memset (&state, 0, sizeof (LinuxMountInfoParseState)); - state.field_number = 1; - state.delimiter = ' '; - - while ((state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER)) > 0) { - state.buffer_index = 0; - - while ((*parser)(len, buf, &state)) { - if (state.buffer [state.buffer_index] == '\n') { - gboolean quit = add_drive_string (len, buf, &state); - state.field_number = 1; - state.buffer_index++; - if (state.mountpoint_allocated) { - g_free (state.mountpoint_allocated); - state.mountpoint_allocated = NULL; - } - if (quit) { - ret = state.total; - goto done_and_out; - } - } - } - }; - ret = state.total; - - done_and_out: - if (fd != -1) - close (fd); - return ret; -} - -static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) -{ - gchar *ptr; - - if (state->field_number == 1) - state->check_mount_source = TRUE; - - while (state->buffer_index < (guint32)state->nbytes) { - if (state->buffer [state->buffer_index] == state->delimiter) { - state->field_number++; - switch (state->field_number) { - case 2: - state->mountpoint_index = 0; - break; - - case 3: - if (state->mountpoint_allocated) - state->mountpoint_allocated [state->mountpoint_index] = 0; - else - state->mountpoint [state->mountpoint_index] = 0; - break; - - default: - ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index); - if (ptr) - state->buffer_index = (ptr - (gchar*)state->buffer) - 1; - else - state->buffer_index = state->nbytes; - return TRUE; - } - state->buffer_index++; - continue; - } else if (state->buffer [state->buffer_index] == '\n') - return TRUE; - - switch (state->field_number) { - case 1: - if (state->check_mount_source) { - if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') { - /* We can ignore the rest, it's a device - * path */ - state->check_mount_source = FALSE; - state->fsname [state->fsname_index++] = '/'; - break; - } - if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) - state->fsname [state->fsname_index++] = state->buffer [state->buffer_index]; - } - break; - - case 2: - append_to_mountpoint (state); - break; - - case 3: - if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) - state->fstype [state->fstype_index++] = state->buffer [state->buffer_index]; - break; - } - - state->buffer_index++; - } - - return FALSE; -} - -static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) -{ - while (state->buffer_index < (guint32)state->nbytes) { - if (state->buffer [state->buffer_index] == state->delimiter) { - state->field_number++; - switch (state->field_number) { - case 5: - state->mountpoint_index = 0; - break; - - case 6: - if (state->mountpoint_allocated) - state->mountpoint_allocated [state->mountpoint_index] = 0; - else - state->mountpoint [state->mountpoint_index] = 0; - break; - - case 7: - state->delimiter = '-'; - break; - - case 8: - state->delimiter = ' '; - break; - - case 10: - state->check_mount_source = TRUE; - break; - } - state->buffer_index++; - continue; - } else if (state->buffer [state->buffer_index] == '\n') - return TRUE; - - switch (state->field_number) { - case 5: - append_to_mountpoint (state); - break; - - case 9: - if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) - state->fstype [state->fstype_index++] = state->buffer [state->buffer_index]; - break; - - case 10: - if (state->check_mount_source) { - if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') { - /* We can ignore the rest, it's a device - * path */ - state->check_mount_source = FALSE; - state->fsname [state->fsname_index++] = '/'; - break; - } - if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) - state->fsname [state->fsname_index++] = state->buffer [state->buffer_index]; - } - break; - } - - state->buffer_index++; - } - - return FALSE; -} - -static void -append_to_mountpoint (LinuxMountInfoParseState *state) -{ - gchar ch = state->buffer [state->buffer_index]; - if (state->mountpoint_allocated) { - if (state->mountpoint_index >= state->allocated_size) { - guint32 newsize = (state->allocated_size << 1) + 1; - gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar)); - - memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index); - g_free (state->mountpoint_allocated); - state->mountpoint_allocated = newbuf; - state->allocated_size = newsize; - } - state->mountpoint_allocated [state->mountpoint_index++] = ch; - } else { - if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) { - state->allocated_size = (state->mountpoint_index << 1) + 1; - state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar)); - memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index); - state->mountpoint_allocated [state->mountpoint_index++] = ch; - } else - state->mountpoint [state->mountpoint_index++] = ch; - } -} - -static gboolean -add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) -{ - gboolean quit = FALSE; - gboolean ignore_entry; - - if (state->fsname_index == 1 && state->fsname [0] == '/') - ignore_entry = FALSE; - else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 || - memcmp ("aufs", state->fstype, state->fstype_index) == 0) { - /* Don't ignore overlayfs and aufs - these might be used on Docker - * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */ - ignore_entry = FALSE; - } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) { - ignore_entry = TRUE; - } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) { - /* Ignore GNOME's gvfs */ - if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0) - ignore_entry = TRUE; - else - ignore_entry = FALSE; - } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0) - ignore_entry = FALSE; - else - ignore_entry = TRUE; - - if (!ignore_entry) { - gunichar2 *dir; - glong length; - gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint; - - unescape_octal (mountpoint); - dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL); - if (state->total + length + 1 > len) { - quit = TRUE; - state->total = len * 2; - } else { - length++; - memcpy (buf + state->total, dir, sizeof (gunichar2) * length); - state->total += length; - } - g_free (dir); - } - state->fsname_index = 0; - state->fstype_index = 0; - - return quit; -} -#else -gint32 -GetLogicalDriveStrings (guint32 len, gunichar2 *buf) -{ - return GetLogicalDriveStrings_Mtab (len, buf); -} -#endif -static gint32 -GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf) -{ - FILE *fp; - gunichar2 *ptr, *dir; - glong length, total = 0; - gchar buffer [512]; - gchar **splitted; - - memset (buf, 0, sizeof (gunichar2) * (len + 1)); - buf [0] = '/'; - buf [1] = 0; - buf [2] = 0; - - /* Sigh, mntent and friends don't work well. - * It stops on the first line that doesn't begin with a '/'. - * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */ - fp = fopen ("/etc/mtab", "rt"); - if (fp == NULL) { - fp = fopen ("/etc/mnttab", "rt"); - if (fp == NULL) - return 1; - } - - ptr = buf; - while (fgets (buffer, 512, fp) != NULL) { - if (*buffer != '/') - continue; - - splitted = g_strsplit (buffer, " ", 0); - if (!*splitted || !*(splitted + 1)) { - g_strfreev (splitted); - continue; - } - - unescape_octal (*(splitted + 1)); - dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL); - g_strfreev (splitted); - if (total + length + 1 > len) { - fclose (fp); - g_free (dir); - return len * 2; /* guess */ - } - - memcpy (ptr + total, dir, sizeof (gunichar2) * length); - g_free (dir); - total += length + 1; - } - - fclose (fp); - return total; -/* Commented out, does not work with my mtab!!! - Gonz */ -#ifdef NOTENABLED /* HAVE_MNTENT_H */ -{ - FILE *fp; - struct mntent *mnt; - gunichar2 *ptr, *dir; - glong len, total = 0; - - - fp = setmntent ("/etc/mtab", "rt"); - if (fp == NULL) { - fp = setmntent ("/etc/mnttab", "rt"); - if (fp == NULL) - return; - } - - ptr = buf; - while ((mnt = getmntent (fp)) != NULL) { - g_print ("GOT %s\n", mnt->mnt_dir); - dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL); - if (total + len + 1 > len) { - return len * 2; /* guess */ - } - - memcpy (ptr + total, dir, sizeof (gunichar2) * len); - g_free (dir); - total += len + 1; - } - - endmntent (fp); - return total; -} -#endif -} -#endif - -#if defined(HAVE_STATVFS) || defined(HAVE_STATFS) -gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, ULARGE_INTEGER *free_bytes_avail, - ULARGE_INTEGER *total_number_of_bytes, - ULARGE_INTEGER *total_number_of_free_bytes) -{ -#ifdef HAVE_STATVFS - struct statvfs fsstat; -#elif defined(HAVE_STATFS) - struct statfs fsstat; -#endif - gboolean isreadonly; - gchar *utf8_path_name; - int ret; - unsigned long block_size; - - if (path_name == NULL) { - utf8_path_name = g_strdup (g_get_current_dir()); - if (utf8_path_name == NULL) { - SetLastError (ERROR_DIRECTORY); - return(FALSE); - } - } - else { - utf8_path_name = mono_unicode_to_external (path_name); - if (utf8_path_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - - SetLastError (ERROR_INVALID_NAME); - return(FALSE); - } - } - - do { -#ifdef HAVE_STATVFS - ret = statvfs (utf8_path_name, &fsstat); - isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY); - block_size = fsstat.f_frsize; -#elif defined(HAVE_STATFS) - ret = statfs (utf8_path_name, &fsstat); -#if defined (MNT_RDONLY) - isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY); -#elif defined (MS_RDONLY) - isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY); -#endif - block_size = fsstat.f_bsize; -#endif - } while(ret == -1 && errno == EINTR); - - g_free(utf8_path_name); - - if (ret == -1) { - _wapi_set_last_error_from_errno (); - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: statvfs failed: %s", __func__, strerror (errno)); - return(FALSE); - } - - /* total number of free bytes for non-root */ - if (free_bytes_avail != NULL) { - if (isreadonly) { - free_bytes_avail->QuadPart = 0; - } - else { - free_bytes_avail->QuadPart = block_size * (guint64)fsstat.f_bavail; - } - } - - /* total number of bytes available for non-root */ - if (total_number_of_bytes != NULL) { - total_number_of_bytes->QuadPart = block_size * (guint64)fsstat.f_blocks; - } - - /* total number of bytes available for root */ - if (total_number_of_free_bytes != NULL) { - if (isreadonly) { - total_number_of_free_bytes->QuadPart = 0; - } - else { - total_number_of_free_bytes->QuadPart = block_size * (guint64)fsstat.f_bfree; - } - } - - return(TRUE); -} -#else -gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, ULARGE_INTEGER *free_bytes_avail, - ULARGE_INTEGER *total_number_of_bytes, - ULARGE_INTEGER *total_number_of_free_bytes) -{ - if (free_bytes_avail != NULL) { - free_bytes_avail->QuadPart = (guint64) -1; - } - - if (total_number_of_bytes != NULL) { - total_number_of_bytes->QuadPart = (guint64) -1; - } - - if (total_number_of_free_bytes != NULL) { - total_number_of_free_bytes->QuadPart = (guint64) -1; - } - - return(TRUE); -} -#endif - -/* - * General Unix support - */ -typedef struct { - guint32 drive_type; -#if __linux__ - const long fstypeid; -#endif - const gchar* fstype; -} _wapi_drive_type; - -static _wapi_drive_type _wapi_drive_types[] = { -#if PLATFORM_MACOSX - { DRIVE_REMOTE, "afp" }, - { DRIVE_REMOTE, "autofs" }, - { DRIVE_CDROM, "cddafs" }, - { DRIVE_CDROM, "cd9660" }, - { DRIVE_RAMDISK, "devfs" }, - { DRIVE_FIXED, "exfat" }, - { DRIVE_RAMDISK, "fdesc" }, - { DRIVE_REMOTE, "ftp" }, - { DRIVE_FIXED, "hfs" }, - { DRIVE_FIXED, "msdos" }, - { DRIVE_REMOTE, "nfs" }, - { DRIVE_FIXED, "ntfs" }, - { DRIVE_REMOTE, "smbfs" }, - { DRIVE_FIXED, "udf" }, - { DRIVE_REMOTE, "webdav" }, - { DRIVE_UNKNOWN, NULL } -#elif __linux__ - { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"}, - { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"}, - { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"}, - { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"}, - { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"}, - { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" }, - { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"}, - { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"}, - { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"}, - { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"}, - { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"}, - { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"}, - { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"}, - { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"}, - { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"}, - { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"}, - { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"}, - { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"}, - { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"}, - { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"}, - { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"}, - { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"}, - { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"}, - { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"}, - { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"}, - { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"}, - { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"}, - { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"}, - { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"}, - { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"}, - { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"}, - { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"}, - { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"}, - { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"}, - { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"}, - { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"}, - { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"}, - { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"}, - { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"}, - { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"}, - { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"}, - { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"}, - { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"}, - { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"}, - { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"}, - { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"}, - { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"}, - { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"}, - { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"}, - { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"}, - { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"}, - { DRIVE_FIXED, UFS_MAGIC, "ufs"}, - { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"}, - { DRIVE_FIXED, UFS2_MAGIC, "ufs2"}, - { DRIVE_FIXED, UFS_CIGAM, "ufs"}, - { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"}, - { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"}, - { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"}, - { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"}, - { DRIVE_FIXED, V9FS_MAGIC, "9p"}, - { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"}, - { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"}, - { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"}, - { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"}, - { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"}, - { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"}, - { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"}, - { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"}, - { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"}, - { DRIVE_FIXED, OMFS_MAGIC, "omfs"}, - { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"}, - { DRIVE_UNKNOWN, 0, NULL} -#else - { DRIVE_RAMDISK, "ramfs" }, - { DRIVE_RAMDISK, "tmpfs" }, - { DRIVE_RAMDISK, "proc" }, - { DRIVE_RAMDISK, "sysfs" }, - { DRIVE_RAMDISK, "debugfs" }, - { DRIVE_RAMDISK, "devpts" }, - { DRIVE_RAMDISK, "securityfs" }, - { DRIVE_CDROM, "iso9660" }, - { DRIVE_FIXED, "ext2" }, - { DRIVE_FIXED, "ext3" }, - { DRIVE_FIXED, "ext4" }, - { DRIVE_FIXED, "sysv" }, - { DRIVE_FIXED, "reiserfs" }, - { DRIVE_FIXED, "ufs" }, - { DRIVE_FIXED, "vfat" }, - { DRIVE_FIXED, "msdos" }, - { DRIVE_FIXED, "udf" }, - { DRIVE_FIXED, "hfs" }, - { DRIVE_FIXED, "hpfs" }, - { DRIVE_FIXED, "qnx4" }, - { DRIVE_FIXED, "ntfs" }, - { DRIVE_FIXED, "ntfs-3g" }, - { DRIVE_REMOTE, "smbfs" }, - { DRIVE_REMOTE, "fuse" }, - { DRIVE_REMOTE, "nfs" }, - { DRIVE_REMOTE, "nfs4" }, - { DRIVE_REMOTE, "cifs" }, - { DRIVE_REMOTE, "ncpfs" }, - { DRIVE_REMOTE, "coda" }, - { DRIVE_REMOTE, "afs" }, - { DRIVE_UNKNOWN, NULL } -#endif -}; - -#if __linux__ -static guint32 _wapi_get_drive_type(long f_type) -{ - _wapi_drive_type *current; - - current = &_wapi_drive_types[0]; - while (current->drive_type != DRIVE_UNKNOWN) { - if (current->fstypeid == f_type) - return current->drive_type; - current++; - } - - return DRIVE_UNKNOWN; -} -#else -static guint32 _wapi_get_drive_type(const gchar* fstype) -{ - _wapi_drive_type *current; - - current = &_wapi_drive_types[0]; - while (current->drive_type != DRIVE_UNKNOWN) { - if (strcmp (current->fstype, fstype) == 0) - break; - - current++; - } - - return current->drive_type; -} -#endif - -#if defined (PLATFORM_MACOSX) || defined (__linux__) -static guint32 -GetDriveTypeFromPath (const char *utf8_root_path_name) -{ - struct statfs buf; - - if (statfs (utf8_root_path_name, &buf) == -1) - return DRIVE_UNKNOWN; -#if PLATFORM_MACOSX - return _wapi_get_drive_type (buf.f_fstypename); -#else - return _wapi_get_drive_type (buf.f_type); -#endif -} -#else -static guint32 -GetDriveTypeFromPath (const gchar *utf8_root_path_name) -{ - guint32 drive_type; - FILE *fp; - gchar buffer [512]; - gchar **splitted; - - fp = fopen ("/etc/mtab", "rt"); - if (fp == NULL) { - fp = fopen ("/etc/mnttab", "rt"); - if (fp == NULL) - return(DRIVE_UNKNOWN); - } - - drive_type = DRIVE_NO_ROOT_DIR; - while (fgets (buffer, 512, fp) != NULL) { - splitted = g_strsplit (buffer, " ", 0); - if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) { - g_strfreev (splitted); - continue; - } - - /* compare given root_path_name with the one from mtab, - if length of utf8_root_path_name is zero it must be the root dir */ - if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 || - (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) { - drive_type = _wapi_get_drive_type (*(splitted + 2)); - /* it is possible this path might be mounted again with - a known type...keep looking */ - if (drive_type != DRIVE_UNKNOWN) { - g_strfreev (splitted); - break; - } - } - - g_strfreev (splitted); - } - - fclose (fp); - return drive_type; -} -#endif - -guint32 GetDriveType(const gunichar2 *root_path_name) -{ - gchar *utf8_root_path_name; - guint32 drive_type; - - if (root_path_name == NULL) { - utf8_root_path_name = g_strdup (g_get_current_dir()); - if (utf8_root_path_name == NULL) { - return(DRIVE_NO_ROOT_DIR); - } - } - else { - utf8_root_path_name = mono_unicode_to_external (root_path_name); - if (utf8_root_path_name == NULL) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); - return(DRIVE_NO_ROOT_DIR); - } - - /* strip trailing slash for compare below */ - if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) { - utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0; - } - } - drive_type = GetDriveTypeFromPath (utf8_root_path_name); - g_free (utf8_root_path_name); - - return (drive_type); -} - -#if defined (PLATFORM_MACOSX) || defined (__linux__) || defined(PLATFORM_BSD) || defined(__native_client__) || defined(__FreeBSD_kernel__) -static gchar* -get_fstypename (gchar *utfpath) -{ -#if defined (PLATFORM_MACOSX) || defined (__linux__) - struct statfs stat; -#if __linux__ - _wapi_drive_type *current; -#endif - if (statfs (utfpath, &stat) == -1) - return NULL; -#if PLATFORM_MACOSX - return g_strdup (stat.f_fstypename); -#else - current = &_wapi_drive_types[0]; - while (current->drive_type != DRIVE_UNKNOWN) { - if (stat.f_type == current->fstypeid) - return g_strdup (current->fstype); - current++; - } - return NULL; -#endif -#else - return NULL; -#endif -} - -/* Linux has struct statfs which has a different layout */ -gboolean -GetVolumeInformation (const gunichar2 *path, gunichar2 *volumename, int volumesize, int *outserial, int *maxcomp, int *fsflags, gunichar2 *fsbuffer, int fsbuffersize) -{ - gchar *utfpath; - gchar *fstypename; - gboolean status = FALSE; - glong len; - - // We only support getting the file system type - if (fsbuffer == NULL) - return 0; - - utfpath = mono_unicode_to_external (path); - if ((fstypename = get_fstypename (utfpath)) != NULL){ - gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL); - if (ret != NULL && len < fsbuffersize){ - memcpy (fsbuffer, ret, len * sizeof (gunichar2)); - fsbuffer [len] = 0; - status = TRUE; - } - if (ret != NULL) - g_free (ret); - g_free (fstypename); - } - g_free (utfpath); - return status; -} -#endif - -void -_wapi_io_init (void) -{ - mono_os_mutex_init (&stdhandle_mutex); - mono_os_mutex_init (&file_share_mutex); - - mono_w32handle_register_ops (MONO_W32HANDLE_FILE, &_wapi_file_ops); - mono_w32handle_register_ops (MONO_W32HANDLE_CONSOLE, &_wapi_console_ops); - mono_w32handle_register_ops (MONO_W32HANDLE_FIND, &_wapi_find_ops); - mono_w32handle_register_ops (MONO_W32HANDLE_PIPE, &_wapi_pipe_ops); - -/* mono_w32handle_register_capabilities (MONO_W32HANDLE_FILE, */ -/* MONO_W32HANDLE_CAP_WAIT); */ -/* mono_w32handle_register_capabilities (MONO_W32HANDLE_CONSOLE, */ -/* MONO_W32HANDLE_CAP_WAIT); */ - - if (g_getenv ("MONO_STRICT_IO_EMULATION")) - lock_while_writing = TRUE; -} - -void -_wapi_io_cleanup (void) -{ - mono_os_mutex_destroy (&file_share_mutex); - - if (file_share_hash) - g_hash_table_destroy (file_share_hash); -} diff --git a/mono/io-layer/io.h b/mono/io-layer/io.h deleted file mode 100644 index 6e518b60e45..00000000000 --- a/mono/io-layer/io.h +++ /dev/null @@ -1,249 +0,0 @@ -/* - * io.h: File, console and find handles - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - */ - -#ifndef _WAPI_IO_H_ -#define _WAPI_IO_H_ - -#include - -#include "mono/io-layer/wapi.h" - -G_BEGIN_DECLS - -typedef struct _WapiSecurityAttributes WapiSecurityAttributes; - -struct _WapiSecurityAttributes -{ - guint32 nLength; - gpointer lpSecurityDescriptor; - gboolean bInheritHandle; -}; - -typedef struct _WapiOverlapped WapiOverlapped; - -struct _WapiOverlapped -{ - guint32 Internal; - guint32 InternalHigh; - guint32 Offset; - guint32 OffsetHigh; - gpointer hEvent; - gpointer handle1; - gpointer handle2; -}; - -typedef void (*WapiOverlappedCB) (guint32 error, guint32 numbytes, - WapiOverlapped *overlapped); - -#define GENERIC_READ 0x80000000 -#define GENERIC_WRITE 0x40000000 -#define GENERIC_EXECUTE 0x20000000 -#define GENERIC_ALL 0x10000000 - -#define FILE_SHARE_READ 0x00000001 -#define FILE_SHARE_WRITE 0x00000002 -#define FILE_SHARE_DELETE 0x00000004 - -#define CREATE_NEW 1 -#define CREATE_ALWAYS 2 -#define OPEN_EXISTING 3 -#define OPEN_ALWAYS 4 -#define TRUNCATE_EXISTING 5 - - -#define FILE_ATTRIBUTE_READONLY 0x00000001 -#define FILE_ATTRIBUTE_HIDDEN 0x00000002 -#define FILE_ATTRIBUTE_SYSTEM 0x00000004 -#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 -#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 -#define FILE_ATTRIBUTE_ENCRYPTED 0x00000040 -#define FILE_ATTRIBUTE_NORMAL 0x00000080 -#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 -#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 -#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 -#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 -#define FILE_ATTRIBUTE_OFFLINE 0x00001000 -#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 -#define FILE_FLAG_OPEN_NO_RECALL 0x00100000 -#define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 -#define FILE_FLAG_POSIX_SEMANTICS 0x01000000 -#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 -#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000 -#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 -#define FILE_FLAG_RANDOM_ACCESS 0x10000000 -#define FILE_FLAG_NO_BUFFERING 0x20000000 -#define FILE_FLAG_OVERLAPPED 0x40000000 -#define FILE_FLAG_WRITE_THROUGH 0x80000000 - -#define REPLACEFILE_WRITE_THROUGH 0x00000001 -#define REPLACEFILE_IGNORE_MERGE_ERRORS 0x00000002 - -#define MAX_PATH 260 - -typedef enum { - STD_INPUT_HANDLE=-10, - STD_OUTPUT_HANDLE=-11, - STD_ERROR_HANDLE=-12 -} WapiStdHandle; - -typedef enum { - FILE_BEGIN=0, - FILE_CURRENT=1, - FILE_END=2 -} WapiSeekMethod; - -typedef enum { - FILE_TYPE_UNKNOWN=0x0000, - FILE_TYPE_DISK=0x0001, - FILE_TYPE_CHAR=0x0002, - FILE_TYPE_PIPE=0x0003, - FILE_TYPE_REMOTE=0x8000 -} WapiFileType; - -typedef enum { - DRIVE_UNKNOWN=0, - DRIVE_NO_ROOT_DIR=1, - DRIVE_REMOVABLE=2, - DRIVE_FIXED=3, - DRIVE_REMOTE=4, - DRIVE_CDROM=5, - DRIVE_RAMDISK=6 -} WapiDriveType; - -typedef enum { - GetFileExInfoStandard=0x0000, - GetFileExMaxInfoLevel=0x0001 -} WapiGetFileExInfoLevels; - -typedef struct -{ - guint16 wYear; - guint16 wMonth; - guint16 wDayOfWeek; - guint16 wDay; - guint16 wHour; - guint16 wMinute; - guint16 wSecond; - guint16 wMilliseconds; -} WapiSystemTime; - -typedef struct { -#if G_BYTE_ORDER == G_BIG_ENDIAN - guint32 dwHighDateTime; - guint32 dwLowDateTime; -#else - guint32 dwLowDateTime; - guint32 dwHighDateTime; -#endif -} WapiFileTime; - -typedef struct -{ - guint32 dwFileAttributes; - WapiFileTime ftCreationTime; - WapiFileTime ftLastAccessTime; - WapiFileTime ftLastWriteTime; - guint32 nFileSizeHigh; - guint32 nFileSizeLow; - guint32 dwReserved0; - guint32 dwReserved1; - gunichar2 cFileName [MAX_PATH]; - gunichar2 cAlternateFileName [14]; -} WapiFindData; - -typedef struct -{ - guint32 dwFileAttributes; - WapiFileTime ftCreationTime; - WapiFileTime ftLastAccessTime; - WapiFileTime ftLastWriteTime; - guint32 nFileSizeHigh; - guint32 nFileSizeLow; -} WapiFileAttributesData; - -typedef union { - struct { - guint32 LowPart; - guint32 HighPart; - } u; - guint64 QuadPart; -} ULARGE_INTEGER; - -#define INVALID_SET_FILE_POINTER ((guint32)-1) -#define INVALID_FILE_SIZE ((guint32)0xFFFFFFFF) -#define INVALID_FILE_ATTRIBUTES ((guint32)-1) - -extern gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, - guint32 sharemode, - WapiSecurityAttributes *security, - guint32 createmode, - guint32 attrs, gpointer tmplate); -extern gboolean DeleteFile(const gunichar2 *name); -extern gpointer GetStdHandle(WapiStdHandle stdhandle); -extern gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes, - guint32 *bytesread, WapiOverlapped *overlapped); -extern gboolean WriteFile(gpointer handle, gconstpointer buffer, - guint32 numbytes, guint32 *byteswritten, - WapiOverlapped *overlapped); -extern gboolean FlushFileBuffers(gpointer handle); -extern gboolean SetEndOfFile(gpointer handle); -extern guint32 SetFilePointer(gpointer handle, gint32 movedistance, - gint32 *highmovedistance, guint32 method); -extern WapiFileType GetFileType(gpointer handle); -extern guint32 GetFileSize(gpointer handle, guint32 *highsize); -extern gboolean GetFileTime(gpointer handle, WapiFileTime *create_time, - WapiFileTime *last_access, - WapiFileTime *last_write); -extern gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time, - const WapiFileTime *last_access, - const WapiFileTime *last_write); -extern gboolean FileTimeToSystemTime(const WapiFileTime *file_time, - WapiSystemTime *system_time); -extern gpointer FindFirstFile (const gunichar2 *pattern, - WapiFindData *find_data); -extern gboolean FindNextFile (gpointer handle, WapiFindData *find_data); -extern gboolean FindClose (gpointer handle); -extern gboolean CreateDirectory (const gunichar2 *name, - WapiSecurityAttributes *security); -extern gboolean RemoveDirectory (const gunichar2 *name); -extern gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name); -extern gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, - gboolean fail_if_exists); -extern gboolean ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, - const gunichar2 *backupFileName, guint32 replaceFlags, - gpointer exclude, gpointer reserved); -extern guint32 GetFileAttributes (const gunichar2 *name); -extern gboolean GetFileAttributesEx (const gunichar2 *name, - WapiGetFileExInfoLevels level, - gpointer info); -extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs); -extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer); -extern gboolean SetCurrentDirectory (const gunichar2 *path); -extern gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe, - WapiSecurityAttributes *security, guint32 size); -extern gint32 GetLogicalDriveStrings (guint32 len, gunichar2 *buf); -extern gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, ULARGE_INTEGER *free_bytes_avail, - ULARGE_INTEGER *total_number_of_bytes, - ULARGE_INTEGER *total_number_of_free_bytes); -extern guint32 GetDriveType(const gunichar2 *root_path_name); -extern gboolean LockFile (gpointer handle, guint32 offset_low, - guint32 offset_high, guint32 length_low, - guint32 length_high); -extern gboolean UnlockFile (gpointer handle, guint32 offset_low, - guint32 offset_high, guint32 length_low, - guint32 length_high); -extern gboolean GetVolumeInformation (const gunichar2 *path, gunichar2 *volumename, int volumesize, int *outserial, int *maxcomp, int *fsflags, gunichar2 *fsbuffer, int fsbuffersize); - - -extern void _wapi_io_init (void); -extern void _wapi_io_cleanup (void); - -G_END_DECLS - -#endif /* _WAPI_IO_H_ */ diff --git a/mono/io-layer/locking.c b/mono/io-layer/locking.c deleted file mode 100644 index 86eb864365f..00000000000 --- a/mono/io-layer/locking.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * io.c: File, console and find handles - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - * Copyright (c) 2002-2009 Novell, Inc. - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -gboolean -_wapi_lock_file_region (int fd, off_t offset, off_t length) -{ -#if defined(__native_client__) - printf("WARNING: locking.c: _wapi_lock_file_region(): fcntl() not available on Native Client!\n"); - // behave as below -- locks are not available - return(TRUE); -#else - struct flock lock_data; - int ret; - - if (offset < 0 || length < 0) { - SetLastError (ERROR_INVALID_PARAMETER); - return(FALSE); - } - - lock_data.l_type = F_WRLCK; - lock_data.l_whence = SEEK_SET; - lock_data.l_start = offset; - lock_data.l_len = length; - - do { - ret = fcntl (fd, F_SETLK, &lock_data); - } while(ret == -1 && errno == EINTR); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret); - - if (ret == -1) { - /* - * if locks are not available (NFS for example), - * ignore the error - */ - if (errno == ENOLCK -#ifdef EOPNOTSUPP - || errno == EOPNOTSUPP -#endif -#ifdef ENOTSUP - || errno == ENOTSUP -#endif - ) { - return (TRUE); - } - - SetLastError (ERROR_LOCK_VIOLATION); - return(FALSE); - } - - return(TRUE); -#endif /* __native_client__ */ -} - -gboolean -_wapi_unlock_file_region (int fd, off_t offset, off_t length) -{ -#if defined(__native_client__) - printf("WARNING: locking.c: _wapi_unlock_file_region(): fcntl() not available on Native Client!\n"); - return (TRUE); -#else - struct flock lock_data; - int ret; - - lock_data.l_type = F_UNLCK; - lock_data.l_whence = SEEK_SET; - lock_data.l_start = offset; - lock_data.l_len = length; - - do { - ret = fcntl (fd, F_SETLK, &lock_data); - } while(ret == -1 && errno == EINTR); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret); - - if (ret == -1) { - /* - * if locks are not available (NFS for example), - * ignore the error - */ - if (errno == ENOLCK -#ifdef EOPNOTSUPP - || errno == EOPNOTSUPP -#endif -#ifdef ENOTSUP - || errno == ENOTSUP -#endif - ) { - return (TRUE); - } - - SetLastError (ERROR_LOCK_VIOLATION); - return(FALSE); - } - - return(TRUE); -#endif /* __native_client__ */ -} - -gboolean -LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, - guint32 length_low, guint32 length_high) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - off_t offset, length; - int fd = GPOINTER_TO_UINT(handle); - - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - if (!(file_handle->fileaccess & GENERIC_READ) && - !(file_handle->fileaccess & GENERIC_WRITE) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - -#ifdef HAVE_LARGE_FILE_SUPPORT - offset = ((gint64)offset_high << 32) | offset_low; - length = ((gint64)length_high << 32) | length_low; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %lld, length %lld", __func__, handle, offset, length); -#else - if (offset_high > 0 || length_high > 0) { - SetLastError (ERROR_INVALID_PARAMETER); - return (FALSE); - } - offset = offset_low; - length = length_low; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %ld, length %ld", __func__, - handle, offset, length); -#endif - - return(_wapi_lock_file_region (fd, offset, length)); -} - -gboolean -UnlockFile (gpointer handle, guint32 offset_low, - guint32 offset_high, guint32 length_low, - guint32 length_high) -{ - struct _WapiHandle_file *file_handle; - gboolean ok; - off_t offset, length; - int fd = GPOINTER_TO_UINT(handle); - - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, - (gpointer *)&file_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up file handle %p", __func__, - handle); - SetLastError (ERROR_INVALID_HANDLE); - return(FALSE); - } - - if (!(file_handle->fileaccess & GENERIC_READ) && - !(file_handle->fileaccess & GENERIC_WRITE) && - !(file_handle->fileaccess & GENERIC_ALL)) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); - } - -#ifdef HAVE_LARGE_FILE_SUPPORT - offset = ((gint64)offset_high << 32) | offset_low; - length = ((gint64)length_high << 32) | length_low; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %lld, length %lld", __func__, handle, offset, length); -#else - offset = offset_low; - length = length_low; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %ld, length %ld", __func__, handle, offset, length); -#endif - - return(_wapi_unlock_file_region (fd, offset, length)); -} diff --git a/mono/io-layer/posix.c b/mono/io-layer/posix.c deleted file mode 100644 index 995795bb326..00000000000 --- a/mono/io-layer/posix.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * posix.c: Posix-specific support. - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - * Copyright (c) 2002-2009 Novell, Inc. - * Copyright 2011 Xamarin Inc - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static guint32 -convert_from_flags(int flags) -{ - guint32 fileaccess=0; - -#ifndef O_ACCMODE -#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) -#endif - - if((flags & O_ACCMODE) == O_RDONLY) { - fileaccess=GENERIC_READ; - } else if ((flags & O_ACCMODE) == O_WRONLY) { - fileaccess=GENERIC_WRITE; - } else if ((flags & O_ACCMODE) == O_RDWR) { - fileaccess=GENERIC_READ|GENERIC_WRITE; - } else { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't figure out flags 0x%x", __func__, flags); - } - - /* Maybe sort out create mode too */ - - return(fileaccess); -} - - -gpointer _wapi_stdhandle_create (int fd, const gchar *name) -{ - struct _WapiHandle_file file_handle = {0}; - gpointer handle; - int flags; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating standard handle type %s, fd %d", __func__, - name, fd); - -#if !defined(__native_client__) - /* Check if fd is valid */ - do { - flags=fcntl(fd, F_GETFL); - } while (flags == -1 && errno == EINTR); - - if(flags==-1) { - /* Invalid fd. Not really much point checking for EBADF - * specifically - */ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl error on fd %d: %s", __func__, fd, - strerror(errno)); - - SetLastError (_wapi_get_win32_file_error (errno)); - return(INVALID_HANDLE_VALUE); - } - file_handle.fileaccess=convert_from_flags(flags); -#else - /* - * fcntl will return -1 in nacl, as there is no real file system API. - * Yet, standard streams are available. - */ - file_handle.fileaccess = (fd == STDIN_FILENO) ? GENERIC_READ : GENERIC_WRITE; -#endif - - file_handle.fd = fd; - file_handle.filename = g_strdup(name); - /* some default security attributes might be needed */ - file_handle.security_attributes=0; - - /* Apparently input handles can't be written to. (I don't - * know if output or error handles can't be read from.) - */ - if (fd == 0) { - file_handle.fileaccess &= ~GENERIC_WRITE; - } - - file_handle.sharemode=0; - file_handle.attrs=0; - - handle = mono_w32handle_new_fd (MONO_W32HANDLE_CONSOLE, fd, &file_handle); - if (handle == INVALID_HANDLE_VALUE) { - g_warning ("%s: error creating file handle", __func__); - SetLastError (ERROR_GEN_FAILURE); - return(INVALID_HANDLE_VALUE); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle); - - return(handle); -} - diff --git a/mono/io-layer/uglify.h b/mono/io-layer/uglify.h deleted file mode 100644 index 0335d0a7200..00000000000 --- a/mono/io-layer/uglify.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * uglify.h: Optional header to provide the nasty w32 typedefs - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - */ - -#ifndef _WAPI_UGLIFY_H_ -#define _WAPI_UGLIFY_H_ - -/* Include this file if you insist on using the nasty Win32 typedefs */ - -#include - -#include "mono/io-layer/wapi.h" - -typedef const gunichar2 *LPCTSTR; -typedef gunichar2 *LPTSTR; -typedef const char *LPCSTR; -typedef char *LPSTR; -typedef guint8 BYTE; -typedef guint8 *LPBYTE; -typedef guint16 WORD; -typedef guint32 DWORD; -typedef gpointer PVOID; -typedef gpointer LPVOID; -typedef gboolean BOOL; -typedef guint32 *LPDWORD; -typedef gint32 LONG; -typedef guint32 ULONG; -typedef gint32 *PLONG; -typedef guint64 LONGLONG; -typedef gunichar2 TCHAR; -typedef size_t SIZE_T; -typedef guint64 ULONG64; -typedef guint UINT; -typedef gconstpointer LPCVOID; - -typedef gpointer HANDLE; -typedef gpointer *LPHANDLE; -typedef gpointer HMODULE; -typedef gpointer HINSTANCE; -typedef gpointer HWND; -typedef gpointer HKEY; - -typedef WapiSecurityAttributes SECURITY_ATTRIBUTES; -typedef WapiSecurityAttributes *LPSECURITY_ATTRIBUTES; -typedef WapiOverlapped *LPOVERLAPPED; -typedef WapiOverlappedCB LPOVERLAPPED_COMPLETION_ROUTINE; -typedef WapiFileTime FILETIME; -typedef WapiFileTime *LPFILETIME; -typedef WapiSystemTime SYSTEMTIME; -typedef WapiSystemTime *LPSYSTEMTIME; -typedef WapiFindData WIN32_FIND_DATA; -typedef WapiFindData *LPWIN32_FIND_DATA; -typedef WapiFileAttributesData WIN32_FILE_ATTRIBUTE_DATA; -typedef WapiGetFileExInfoLevels GET_FILEEX_INFO_LEVELS; - -#define CONST const -#define VOID void - -#define IN -#define OUT -#define WINAPI - -#endif /* _WAPI_UGLIFY_H_ */ diff --git a/mono/io-layer/wapi-private.h b/mono/io-layer/wapi-private.h deleted file mode 100644 index f39d4e4fab4..00000000000 --- a/mono/io-layer/wapi-private.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * wapi-private.h: internal definitions of handles and shared memory layout - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002-2006 Novell, Inc. - */ - -#ifndef _WAPI_PRIVATE_H_ -#define _WAPI_PRIVATE_H_ - -#include -#include -#include - -#include -#include - -#include - -/* There doesn't seem to be a defined symbol for this */ -#define _WAPI_THREAD_CURRENT (gpointer)0xFFFFFFFE - -extern gboolean _wapi_has_shut_down; - -#include -#include - -struct _WapiHandle_shared_ref -{ - /* This will be split 16:16 with the shared file segment in - * the top half, when I implement space increases - */ - guint32 offset; -}; - -struct _WapiFileShare -{ -#ifdef WAPI_FILE_SHARE_PLATFORM_EXTRA_DATA - WAPI_FILE_SHARE_PLATFORM_EXTRA_DATA -#endif - guint64 device; - guint64 inode; - pid_t opened_by_pid; - guint32 sharemode; - guint32 access; - guint32 handle_refs; - guint32 timestamp; -}; - -typedef struct _WapiFileShare _WapiFileShare; - -#endif /* _WAPI_PRIVATE_H_ */ diff --git a/mono/io-layer/wapi-remap.h b/mono/io-layer/wapi-remap.h index 7cc3b89ff38..80034f50be7 100644 --- a/mono/io-layer/wapi-remap.h +++ b/mono/io-layer/wapi-remap.h @@ -15,41 +15,5 @@ #define GetLastError wapi_GetLastError #define SetLastError wapi_SetLastError #define CloseHandle wapi_CloseHandle -#define CreateFile wapi_CreateFile -#define DeleteFile wapi_DeleteFile -#define GetStdHandle wapi_GetStdHandle -#define ReadFile wapi_ReadFile -#define WriteFile wapi_WriteFile -#define FlushFileBuffers wapi_FlushFileBuffers -#define SetEndOfFile wapi_SetEndOfFile -#define SetFilePointer wapi_SetFilePointer -#define GetFileType wapi_GetFileType -#define GetFileSize wapi_GetFileSize -#define GetFileTime wapi_GetFileTime -#define SetFileTime wapi_SetFileTime -#define FileTimeToSystemTime wapi_FileTimeToSystemTime -#define FindFirstFile wapi_FindFirstFile -#define FindNextFile wapi_FindNextFile -#define FindClose wapi_FindClose -#define CreateDirectory wapi_CreateDirectory -#define RemoveDirectory wapi_RemoveDirectory -#define MoveFile wapi_MoveFile -#define CopyFile wapi_CopyFile -#define ReplaceFile wapi_ReplaceFile -#define GetFileAttributes wapi_GetFileAttributes -#define GetFileAttributesEx wapi_GetFileAttributesEx -#define SetFileAttributes wapi_SetFileAttributes -#define GetCurrentDirectory wapi_GetCurrentDirectory -#define SetCurrentDirectory wapi_SetCurrentDirectory -#define CreatePipe wapi_CreatePipe -#define GetLogicalDriveStrings wapi_GetLogicalDriveStrings -#define GetDiskFreeSpaceEx wapi_GetDiskFreeSpaceEx -#define GetDriveType wapi_GetDriveType -#define LockFile wapi_LockFile -#define UnlockFile wapi_UnlockFile -#define GetVolumeInformation wapi_GetVolumeInformation -#define ImpersonateLoggedOnUser wapi_ImpersonateLoggedOnUser -#define RevertToSelf wapi_RevertToSelf -#define GetSystemInfo wapi_GetSystemInfo #endif /* __WAPI_REMAP_H__ */ diff --git a/mono/io-layer/wapi.c b/mono/io-layer/wapi.c index d4da229603e..14dc672e8ee 100644 --- a/mono/io-layer/wapi.c +++ b/mono/io-layer/wapi.c @@ -1,30 +1,9 @@ #include "wapi.h" -#include "io-trace.h" -#include "io.h" - #include "mono/utils/mono-lazy-init.h" #include "mono/metadata/w32handle.h" -gboolean _wapi_has_shut_down = FALSE; - -void -wapi_init (void) -{ - _wapi_io_init (); -} - -void -wapi_cleanup (void) -{ - g_assert (_wapi_has_shut_down == FALSE); - _wapi_has_shut_down = TRUE; - - _wapi_error_cleanup (); - _wapi_io_cleanup (); -} - /* Use this instead of getpid(), to cope with linuxthreads. It's a * function rather than a variable lookup because we need to get at * this before share_init() might have been called. */ diff --git a/mono/io-layer/wapi.h b/mono/io-layer/wapi.h index fb0bf4fbce9..4409210db2d 100644 --- a/mono/io-layer/wapi.h +++ b/mono/io-layer/wapi.h @@ -10,28 +10,45 @@ #ifndef _WAPI_WAPI_H_ #define _WAPI_WAPI_H_ +#include #include +#ifdef HAVE_DIRENT_H +#include +#endif +#include +#include #include +#include #include -#include -#include #include +#include G_BEGIN_DECLS -#define WAIT_FAILED ((int) 0xFFFFFFFF) -#define WAIT_OBJECT_0 ((int) 0x00000000) -#define WAIT_ABANDONED_0 ((int) 0x00000080) -#define WAIT_TIMEOUT ((int) 0x00000102) -#define WAIT_IO_COMPLETION ((int) 0x000000C0) +#define WAIT_FAILED ((gint) 0xFFFFFFFF) +#define WAIT_OBJECT_0 ((gint) 0x00000000) +#define WAIT_ABANDONED_0 ((gint) 0x00000080) +#define WAIT_TIMEOUT ((gint) 0x00000102) +#define WAIT_IO_COMPLETION ((gint) 0x000000C0) -void -wapi_init (void); +#ifdef DISABLE_IO_LAYER_TRACE +#define MONO_TRACE(...) +#else +#define MONO_TRACE(...) mono_trace (__VA_ARGS__) +#endif -void -wapi_cleanup (void); +#define WINAPI + +typedef guint32 DWORD; +typedef gboolean BOOL; +typedef gint32 LONG; +typedef guint32 ULONG; +typedef guint UINT; + +typedef gpointer HANDLE; +typedef gpointer HMODULE; gboolean CloseHandle (gpointer handle); diff --git a/mono/io-layer/wapi_glob.c b/mono/io-layer/wapi_glob.c deleted file mode 100644 index 013b778a865..00000000000 --- a/mono/io-layer/wapi_glob.c +++ /dev/null @@ -1,398 +0,0 @@ -/* $OpenBSD: glob.c,v 1.26 2005/11/28 17:50:12 deraadt Exp $ */ -/* - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Guido van Rossum. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * _wapi_glob(3) -- a subset of the one defined in POSIX 1003.2. - * - * Optional extra services, controlled by flags not defined by POSIX: - * - * GLOB_MAGCHAR: - * Set in gl_flags if pattern contained a globbing character. - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "wapi_glob.h" - -#define EOS '\0' -#define NOT '!' -#define QUESTION '?' -#define QUOTE '\\' -#define STAR '*' - -#ifndef DEBUG - -#define M_QUOTE 0x8000 -#define M_PROTECT 0x4000 -#define M_MASK 0xffff -#define M_ASCII 0x00ff - -typedef unsigned short Char; - -#else - -#define M_QUOTE 0x80 -#define M_PROTECT 0x40 -#define M_MASK 0xff -#define M_ASCII 0x7f - -typedef char Char; - -#endif - - -#define CHAR(c) ((gchar)((c)&M_ASCII)) -#define META(c) ((gchar)((c)|M_QUOTE)) -#define M_ALL META('*') -#define M_ONE META('?') -#define ismeta(c) (((c)&M_QUOTE) != 0) - - -static int g_Ctoc(const gchar *, char *, unsigned int); -static int glob0(GDir *dir, const gchar *, wapi_glob_t *, gboolean, - gboolean); -static int glob1(GDir *dir, gchar *, gchar *, wapi_glob_t *, size_t *, - gboolean, gboolean); -static int glob3(GDir *dir, gchar *, gchar *, wapi_glob_t *, size_t *, - gboolean, gboolean); -static int globextend(const gchar *, wapi_glob_t *, size_t *); -static int match(const gchar *, gchar *, gchar *, gboolean); -#ifdef DEBUG_ENABLED -static void qprintf(const char *, Char *); -#endif - -int -_wapi_glob(GDir *dir, const char *pattern, int flags, wapi_glob_t *pglob) -{ - const unsigned char *patnext; - int c; - gchar *bufnext, *bufend, patbuf[PATH_MAX]; - - patnext = (unsigned char *) pattern; - if (!(flags & WAPI_GLOB_APPEND)) { - pglob->gl_pathc = 0; - pglob->gl_pathv = NULL; - pglob->gl_offs = 0; - } - pglob->gl_flags = flags & ~WAPI_GLOB_MAGCHAR; - - bufnext = patbuf; - bufend = bufnext + PATH_MAX - 1; - - /* Protect the quoted characters. */ - while (bufnext < bufend && (c = *patnext++) != EOS) - if (c == QUOTE) { - if ((c = *patnext++) == EOS) { - c = QUOTE; - --patnext; - } - *bufnext++ = c | M_PROTECT; - } else - *bufnext++ = c; - - *bufnext = EOS; - - return glob0(dir, patbuf, pglob, flags & WAPI_GLOB_IGNORECASE, - flags & WAPI_GLOB_UNIQUE); -} - -/* - * The main glob() routine: compiles the pattern (optionally processing - * quotes), calls glob1() to do the real pattern matching, and finally - * sorts the list (unless unsorted operation is requested). Returns 0 - * if things went well, nonzero if errors occurred. It is not an error - * to find no matches. - */ -static int -glob0(GDir *dir, const gchar *pattern, wapi_glob_t *pglob, gboolean ignorecase, - gboolean unique) -{ - const gchar *qpatnext; - int c, err, oldpathc; - gchar *bufnext, patbuf[PATH_MAX]; - size_t limit = 0; - - qpatnext = pattern; - oldpathc = pglob->gl_pathc; - bufnext = patbuf; - - /* We don't need to check for buffer overflow any more. */ - while ((c = *qpatnext++) != EOS) { - switch (c) { - case QUESTION: - pglob->gl_flags |= WAPI_GLOB_MAGCHAR; - *bufnext++ = M_ONE; - break; - case STAR: - pglob->gl_flags |= WAPI_GLOB_MAGCHAR; - /* collapse adjacent stars to one, - * to avoid exponential behavior - */ - if (bufnext == patbuf || bufnext[-1] != M_ALL) - *bufnext++ = M_ALL; - break; - default: - *bufnext++ = CHAR(c); - break; - } - } - *bufnext = EOS; -#ifdef DEBUG_ENABLED - qprintf("glob0:", patbuf); -#endif - - if ((err = glob1(dir, patbuf, patbuf+PATH_MAX-1, pglob, &limit, - ignorecase, unique)) != 0) - return(err); - - if (pglob->gl_pathc == oldpathc) { - return(WAPI_GLOB_NOMATCH); - } - - return(0); -} - -static int -glob1(GDir *dir, gchar *pattern, gchar *pattern_last, wapi_glob_t *pglob, - size_t *limitp, gboolean ignorecase, gboolean unique) -{ - /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ - if (*pattern == EOS) - return(0); - return(glob3(dir, pattern, pattern_last, pglob, limitp, ignorecase, - unique)); -} - -static gboolean contains (wapi_glob_t *pglob, const gchar *name) -{ - int i; - char **pp; - - if (pglob->gl_pathv != NULL) { - pp = pglob->gl_pathv + pglob->gl_offs; - for (i = pglob->gl_pathc; i--; ++pp) { - if (*pp) { - if (!strcmp (*pp, name)) { - return(TRUE); - } - } - } - } - - return(FALSE); -} - -static int -glob3(GDir *dir, gchar *pattern, gchar *pattern_last, wapi_glob_t *pglob, - size_t *limitp, gboolean ignorecase, gboolean unique) -{ - const gchar *name; - - /* Search directory for matching names. */ - while ((name = g_dir_read_name(dir))) { - if (!match(name, pattern, pattern + strlen (pattern), - ignorecase)) { - continue; - } - if (!unique || - !contains (pglob, name)) { - globextend (name, pglob, limitp); - } - } - - return(0); -} - - -/* - * Extend the gl_pathv member of a wapi_glob_t structure to accommodate a new item, - * add the new item, and update gl_pathc. - * - * This assumes the BSD realloc, which only copies the block when its size - * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic - * behavior. - * - * Return 0 if new item added, error code if memory couldn't be allocated. - * - * Invariant of the wapi_glob_t structure: - * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and - * gl_pathv points to (gl_offs + gl_pathc + 1) items. - */ -static int -globextend(const gchar *path, wapi_glob_t *pglob, size_t *limitp) -{ - char **pathv; - int i; - unsigned int newsize, len; - char *copy; - const gchar *p; - - newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); - /* FIXME: Can just use realloc(). */ - pathv = (char **)(pglob->gl_pathv ? g_realloc ((char *)pglob->gl_pathv, newsize) : - g_malloc (newsize)); - if (pathv == NULL) { - if (pglob->gl_pathv) { - g_free (pglob->gl_pathv); - pglob->gl_pathv = NULL; - } - return(WAPI_GLOB_NOSPACE); - } - - if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { - /* first time around -- clear initial gl_offs items */ - pathv += pglob->gl_offs; - for (i = pglob->gl_offs; --i >= 0; ) - *--pathv = NULL; - } - pglob->gl_pathv = pathv; - - for (p = path; *p++;) - ; - len = (size_t)(p - path); - *limitp += len; - if ((copy = (char *)malloc(len)) != NULL) { - if (g_Ctoc(path, copy, len)) { - g_free (copy); - return(WAPI_GLOB_NOSPACE); - } - pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; - } - pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; - -#if 0 - /* Broken on opensuse 11 */ - if ((pglob->gl_flags & WAPI_GLOB_LIMIT) && - newsize + *limitp >= ARG_MAX) { - errno = 0; - return(WAPI_GLOB_NOSPACE); - } -#endif - - return(copy == NULL ? WAPI_GLOB_NOSPACE : 0); -} - - -/* - * pattern matching function for filenames. Each occurrence of the * - * pattern causes a recursion level. - */ -static int -match(const gchar *name, gchar *pat, gchar *patend, gboolean ignorecase) -{ - gchar c; - - while (pat < patend) { - c = *pat++; - switch (c & M_MASK) { - case M_ALL: - if (pat == patend) - return(1); - do { - if (match(name, pat, patend, ignorecase)) - return(1); - } while (*name++ != EOS); - return(0); - case M_ONE: - if (*name++ == EOS) - return(0); - break; - default: - if (ignorecase) { - if (g_ascii_tolower (*name++) != g_ascii_tolower (c)) - return(0); - } else { - if (*name++ != c) - return(0); - } - - break; - } - } - return(*name == EOS); -} - -/* Free allocated data belonging to a wapi_glob_t structure. */ -void -_wapi_globfree(wapi_glob_t *pglob) -{ - int i; - char **pp; - - if (pglob->gl_pathv != NULL) { - pp = pglob->gl_pathv + pglob->gl_offs; - for (i = pglob->gl_pathc; i--; ++pp) - if (*pp) - g_free (*pp); - g_free (pglob->gl_pathv); - pglob->gl_pathv = NULL; - } -} - -static int -g_Ctoc(const gchar *str, char *buf, unsigned int len) -{ - - while (len--) { - if ((*buf++ = *str++) == EOS) - return (0); - } - return (1); -} - -#ifdef DEBUG_ENABLED -static void -qprintf(const char *str, Char *s) -{ - Char *p; - - (void)printf("%s:\n", str); - for (p = s; *p; p++) - (void)printf("%c", CHAR(*p)); - (void)printf("\n"); - for (p = s; *p; p++) - (void)printf("%c", *p & M_PROTECT ? '"' : ' '); - (void)printf("\n"); - for (p = s; *p; p++) - (void)printf("%c", ismeta(*p) ? '_' : ' '); - (void)printf("\n"); -} -#endif diff --git a/mono/io-layer/wapi_glob.h b/mono/io-layer/wapi_glob.h deleted file mode 100644 index fc9d7ba8870..00000000000 --- a/mono/io-layer/wapi_glob.h +++ /dev/null @@ -1,68 +0,0 @@ -/* $OpenBSD: glob.h,v 1.10 2005/12/13 00:35:22 millert Exp $ */ -/* $NetBSD: glob.h,v 1.5 1994/10/26 00:55:56 cgd Exp $ */ - -/* - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Guido van Rossum. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)glob.h 8.1 (Berkeley) 6/2/93 - */ - -#ifndef _WAPI_GLOB_H_ -#define _WAPI_GLOB_H_ - -#include - -struct stat; -typedef struct { - int gl_pathc; /* Count of total paths so far. */ - int gl_offs; /* Reserved at beginning of gl_pathv. */ - int gl_flags; /* Copy of flags parameter to glob. */ - char **gl_pathv; /* List of paths matching pattern. */ -} wapi_glob_t; - -#define WAPI_GLOB_APPEND 0x0001 /* Append to output from previous call. */ -#define WAPI_GLOB_UNIQUE 0x0040 /* When appending only add items that aren't already in the list */ -#define WAPI_GLOB_NOSPACE (-1) /* Malloc call failed. */ -#define WAPI_GLOB_ABORTED (-2) /* Unignored error. */ -#define WAPI_GLOB_NOMATCH (-3) /* No match and WAPI_GLOB_NOCHECK not set. */ -#define WAPI_GLOB_NOSYS (-4) /* Function not supported. */ - -#define WAPI_GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */ -#define WAPI_GLOB_LIMIT 0x2000 /* Limit pattern match output to ARG_MAX */ -#define WAPI_GLOB_IGNORECASE 0x4000 /* Ignore case when matching */ -#define WAPI_GLOB_ABEND WAPI_GLOB_ABORTED /* backward compatibility */ - -G_BEGIN_DECLS -int _wapi_glob(GDir *dir, const char *, int, wapi_glob_t *); -void _wapi_globfree(wapi_glob_t *); -G_END_DECLS - -#endif /* !_WAPI_GLOB_H_ */ diff --git a/mono/metadata/Makefile.am b/mono/metadata/Makefile.am index 5deda477e2d..4a997d3fc4c 100644 --- a/mono/metadata/Makefile.am +++ b/mono/metadata/Makefile.am @@ -3,8 +3,8 @@ win32_sources = \ console-win32.c \ console-win32-internals.h \ cominterop-win32-internals.h \ - file-io-windows.c \ - file-io-windows-internals.h \ + w32file-win32.c \ + w32file-win32-internals.h \ icall-windows.c \ icall-windows-internals.h \ marshal-windows.c \ @@ -49,7 +49,10 @@ unix_sources = \ w32process-unix-bsd.c \ w32process-unix-haiku.c \ w32process-unix-default.c \ - w32socket-unix.c + w32socket-unix.c \ + w32file-unix.c \ + w32file-unix-glob.c \ + w32file-unix-glob.h platform_sources = $(unix_sources) endif @@ -151,9 +154,9 @@ common_sources = \ exception.c \ exception.h \ exception-internals.h \ - file-io.c \ - file-io.h \ - file-io-internals.h \ + w32file.c \ + w32file.h \ + w32file-internals.h \ filewatcher.c \ filewatcher.h \ gc-internals.h \ diff --git a/mono/metadata/appdomain.c b/mono/metadata/appdomain.c index 74323ab37b3..a2c8c353fd3 100644 --- a/mono/metadata/appdomain.c +++ b/mono/metadata/appdomain.c @@ -51,7 +51,7 @@ #include #include #include -#include +#include #include #include #include @@ -1398,6 +1398,7 @@ shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *targ { guint16 *orig, *dest; gboolean copy_result; + gint32 copy_error; strcpy (src + srclen - tail_len, extension); @@ -1417,18 +1418,14 @@ shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *targ strcpy (target + targetlen - tail_len, extension); dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL); - DeleteFile (dest); + mono_w32file_delete (dest); -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) - copy_result = CopyFile (orig, dest, FALSE); -#else - copy_result = SUCCEEDED (CopyFile2 (orig, dest, NULL)); -#endif + copy_result = mono_w32file_copy (orig, dest, TRUE, ©_error); /* Fix for bug #556884 - make sure the files have the correct mode so that they can be * overwritten when updated in their original locations. */ if (copy_result) - copy_result = SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL); + copy_result = mono_w32file_set_attributes (dest, FILE_ATTRIBUTE_NORMAL); g_free (orig); g_free (dest); @@ -1592,15 +1589,14 @@ shadow_copy_create_ini (const char *shadow, const char *filename) if (!u16_ini) { return FALSE; } - handle = (void **)CreateFile (u16_ini, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, CREATE_NEW, FileAttributes_Normal, NULL); + handle = (void **)mono_w32file_create (u16_ini, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, CREATE_NEW, FileAttributes_Normal); g_free (u16_ini); if (handle == INVALID_HANDLE_VALUE) { return FALSE; } full_path = mono_path_resolve_symlinks (filename); - result = WriteFile (handle, full_path, strlen (full_path), &n, NULL); + result = mono_w32file_write (handle, full_path, strlen (full_path), &n); g_free (full_path); CloseHandle (handle); return result; @@ -1695,6 +1691,7 @@ mono_make_shadow_copy (const char *filename, MonoError *oerror) char *dir_name = g_path_get_dirname (filename); MonoDomain *domain = mono_domain_get (); char *shadow_dir; + gint32 copy_error; mono_error_init (oerror); @@ -1740,27 +1737,23 @@ mono_make_shadow_copy (const char *filename, MonoError *oerror) orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL); dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL); - DeleteFile (dest); + mono_w32file_delete (dest); /* Fix for bug #17066 - make sure we can read the file. if not then don't error but rather * let the assembly fail to load. This ensures you can do Type.GetType("NS.T, NonExistantAssembly) * and not have it runtime error" */ - attrs = GetFileAttributes (orig); + attrs = mono_w32file_get_attributes (orig); if (attrs == INVALID_FILE_ATTRIBUTES) { g_free (shadow); return (char *)filename; } -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) - copy_result = CopyFile (orig, dest, FALSE); -#else - copy_result = SUCCEEDED (CopyFile2 (orig, dest, NULL)); -#endif + copy_result = mono_w32file_copy (orig, dest, TRUE, ©_error); /* Fix for bug #556884 - make sure the files have the correct mode so that they can be * overwritten when updated in their original locations. */ if (copy_result) - copy_result = SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL); + copy_result = mono_w32file_set_attributes (dest, FILE_ATTRIBUTE_NORMAL); g_free (dest); g_free (orig); @@ -1772,7 +1765,7 @@ mono_make_shadow_copy (const char *filename, MonoError *oerror) if (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND) return NULL; /* file not found, shadow copy failed */ - mono_error_set_execution_engine (oerror, "Failed to create shadow copy (CopyFile)."); + mono_error_set_execution_engine (oerror, "Failed to create shadow copy (mono_w32file_copy)."); return NULL; } @@ -1791,7 +1784,7 @@ mono_make_shadow_copy (const char *filename, MonoError *oerror) if (!copy_result) { g_free (shadow); - mono_error_set_execution_engine (oerror, "Failed to create shadow copy of sibling data (CopyFile)."); + mono_error_set_execution_engine (oerror, "Failed to create shadow copy of sibling data (mono_w32file_copy)."); return NULL; } diff --git a/mono/metadata/console-null.c b/mono/metadata/console-null.c index b8367e888f7..233e2aef04e 100644 --- a/mono/metadata/console-null.c +++ b/mono/metadata/console-null.c @@ -30,7 +30,7 @@ mono_console_handle_async_ops (void) MonoBoolean ves_icall_System_ConsoleDriver_Isatty (HANDLE handle) { - return (GetFileType (handle) == FILE_TYPE_CHAR); + return mono_w32file_get_type (handle) == FILE_TYPE_CHAR; } MonoBoolean diff --git a/mono/metadata/domain.c b/mono/metadata/domain.c index 76cfe9979db..908f572a5ff 100644 --- a/mono/metadata/domain.c +++ b/mono/metadata/domain.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -484,13 +485,13 @@ mono_init_internal (const char *filename, const char *exe_filename, const char * #ifndef HOST_WIN32 mono_w32handle_init (); mono_w32handle_namespace_init (); - wapi_init (); #endif mono_w32mutex_init (); mono_w32semaphore_init (); mono_w32event_init (); mono_w32process_init (); + mono_w32file_init (); #ifndef DISABLE_PERFCOUNTERS mono_perfcounters_init (); @@ -852,10 +853,7 @@ mono_cleanup (void) mono_coop_mutex_destroy (&appdomains_mutex); mono_w32process_cleanup (); - -#ifndef HOST_WIN32 - wapi_cleanup (); -#endif + mono_w32file_cleanup (); } void diff --git a/mono/metadata/file-io-internals.h b/mono/metadata/file-io-internals.h deleted file mode 100644 index ad455297d11..00000000000 --- a/mono/metadata/file-io-internals.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2016 Microsoft - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ -#ifndef __MONO_FILE_IO_INTERNALS_H__ -#define __MONO_FILE_IO_INTERNALS_H__ - -#include -#include -#include "mono/metadata/object.h" -#include "mono/metadata/object-internals.h" - -gboolean -mono_file_io_move_file (gunichar2 *path, gunichar2 *dest, gint32 *error); - -gboolean -mono_file_io_copy_file (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error); - -gint64 -mono_file_io_get_file_size (HANDLE handle, gint32 *error); - -gboolean -mono_file_io_lock_file (HANDLE handle, gint64 position, gint64 length, gint32 *error); - -gboolean -mono_file_io_replace_file (gunichar2 *destinationFileName, gunichar2 *sourceFileName, - gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error); - -gboolean -mono_file_io_unlock_file (HANDLE handle, gint64 position, gint64 length, gint32 *error); - -HANDLE -mono_file_io_get_console_output (void); - -HANDLE -mono_file_io_get_console_error (void); - -HANDLE -mono_file_io_get_console_input (void); - -#endif /* __MONO_FILE_IO_INTERNALS_H__ */ diff --git a/mono/metadata/file-io-windows-internals.h b/mono/metadata/file-io-windows-internals.h deleted file mode 100644 index df372e8e747..00000000000 --- a/mono/metadata/file-io-windows-internals.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2016 Microsoft - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ -#ifndef _MONO_METADATA_FILEIO_WINDOWS_H_ -#define _MONO_METADATA_FILEIO_WINDOWS_H_ - -#include -#include - -#ifdef HOST_WIN32 -#include "mono/metadata/file-io.h" -#include "mono/metadata/file-io-internals.h" -#endif /* HOST_WIN32 */ -#endif /* _MONO_METADATA_FILEIO_WINDOWS_H_ */ diff --git a/mono/metadata/file-io-windows-uwp.c b/mono/metadata/file-io-windows-uwp.c deleted file mode 100644 index 3af6c056c85..00000000000 --- a/mono/metadata/file-io-windows-uwp.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * file-io-windows-uwp.c: UWP file-io support for Mono. - * - * Copyright 2016 Microsoft - * Licensed under the MIT license. See LICENSE file in the project root for full license information. -*/ -#include -#include -#include "mono/utils/mono-compiler.h" - -#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) -#include -#include "mono/metadata/file-io-windows-internals.h" - -gboolean -mono_file_io_move_file (gunichar2 *path, gunichar2 *dest, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = MoveFileEx (path, dest, MOVEFILE_COPY_ALLOWED); - if (result == FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} - -gboolean -mono_file_io_replace_file (gunichar2 *destinationFileName, gunichar2 *sourceFileName, - gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL); - if (result == FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} - -gboolean -mono_file_io_copy_file (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error) -{ - gboolean result = FALSE; - COPYFILE2_EXTENDED_PARAMETERS copy_param = {0}; - - copy_param.dwSize = sizeof (COPYFILE2_EXTENDED_PARAMETERS); - copy_param.dwCopyFlags = (!overwrite) ? COPY_FILE_FAIL_IF_EXISTS : 0; - - MONO_ENTER_GC_SAFE; - - result = SUCCEEDED (CopyFile2 (path, dest, ©_param)); - if (result == FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} - -gint64 -mono_file_io_get_file_size (HANDLE handle, gint32 *error) -{ - LARGE_INTEGER length; - - MONO_ENTER_GC_SAFE; - - if (!GetFileSizeEx (handle, &length)) { - *error=GetLastError (); - length.QuadPart = INVALID_FILE_SIZE; - } - - MONO_EXIT_GC_SAFE; - return length.QuadPart; -} - -gboolean -mono_file_io_lock_file (HANDLE handle, gint64 position, gint64 length, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, - length & 0xFFFFFFFF, length >> 32); - - if (result == FALSE) { - *error = GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} - -gboolean -mono_file_io_unlock_file (HANDLE handle, gint64 position, gint64 length, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, - length & 0xFFFFFFFF, length >> 32); - - if (result == FALSE) { - *error = GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} - -HANDLE -mono_file_io_get_console_output (void) -{ - MonoError mono_error; - mono_error_init (&mono_error); - - g_unsupported_api ("GetStdHandle (STD_OUTPUT_HANDLE)"); - - mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_OUTPUT_HANDLE)"); - mono_error_set_pending_exception (&mono_error); - - SetLastError (ERROR_NOT_SUPPORTED); - - return INVALID_HANDLE_VALUE; -} - -HANDLE -mono_file_io_get_console_input (void) -{ - MonoError mono_error; - mono_error_init (&mono_error); - - g_unsupported_api ("GetStdHandle (STD_INPUT_HANDLE)"); - - mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_INPUT_HANDLE)"); - mono_error_set_pending_exception (&mono_error); - - SetLastError (ERROR_NOT_SUPPORTED); - - return INVALID_HANDLE_VALUE; -} - -HANDLE -mono_file_io_get_console_error (void) -{ - MonoError mono_error; - mono_error_init (&mono_error); - - g_unsupported_api ("GetStdHandle (STD_ERROR_HANDLE)"); - - mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_ERROR_HANDLE)"); - mono_error_set_pending_exception (&mono_error); - - SetLastError (ERROR_NOT_SUPPORTED); - - return INVALID_HANDLE_VALUE; -} - -#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ - -MONO_EMPTY_SOURCE_FILE (file_io_windows_uwp); -#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/mono/metadata/file-io-windows.c b/mono/metadata/file-io-windows.c deleted file mode 100644 index e7dd11167ce..00000000000 --- a/mono/metadata/file-io-windows.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * file-io-windows.c: Windows File IO internal calls. - * - * Copyright 2016 Microsoft - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ -#include -#include - -#if defined(HOST_WIN32) -#include -#include -#include "mono/metadata/file-io-windows-internals.h" - -gunichar2 -ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar () -{ - return (gunichar2) ':'; /* colon */ -} - -gunichar2 -ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar () -{ - return (gunichar2) '\\'; /* backslash */ -} - -gunichar2 -ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar () -{ - return (gunichar2) '/'; /* forward slash */ -} - -gunichar2 -ves_icall_System_IO_MonoIO_get_PathSeparator () -{ - return (gunichar2) ';'; /* semicolon */ -} - -void ves_icall_System_IO_MonoIO_DumpHandles (void) -{ - return; -} -#endif /* HOST_WIN32 */ diff --git a/mono/metadata/file-io.c b/mono/metadata/file-io.c deleted file mode 100644 index 44fde40538c..00000000000 --- a/mono/metadata/file-io.c +++ /dev/null @@ -1,1408 +0,0 @@ -/* - * file-io.c: File IO internal calls - * - * Author: - * Dick Porter (dick@ximian.com) - * Gonzalo Paniagua Javier (gonzalo@ximian.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) - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#include - -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef DEBUG - -/* conversion functions */ - -static guint32 convert_mode(MonoFileMode mono_mode) -{ - guint32 mode; - - switch(mono_mode) { - case FileMode_CreateNew: - mode=CREATE_NEW; - break; - case FileMode_Create: - mode=CREATE_ALWAYS; - break; - case FileMode_Open: - mode=OPEN_EXISTING; - break; - case FileMode_OpenOrCreate: - mode=OPEN_ALWAYS; - break; - case FileMode_Truncate: - mode=TRUNCATE_EXISTING; - break; - case FileMode_Append: - mode=OPEN_ALWAYS; - break; - default: - g_warning("System.IO.FileMode has unknown value 0x%x", - mono_mode); - /* Safe fallback */ - mode=OPEN_EXISTING; - } - - return(mode); -} - -static guint32 convert_access(MonoFileAccess mono_access) -{ - guint32 access; - - switch(mono_access) { - case FileAccess_Read: - access=GENERIC_READ; - break; - case FileAccess_Write: - access=GENERIC_WRITE; - break; - case FileAccess_ReadWrite: - access=GENERIC_READ|GENERIC_WRITE; - break; - default: - g_warning("System.IO.FileAccess has unknown value 0x%x", - mono_access); - /* Safe fallback */ - access=GENERIC_READ; - } - - return(access); -} - -static guint32 convert_share(MonoFileShare mono_share) -{ - guint32 share = 0; - - 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); -} - -#if 0 -static guint32 convert_stdhandle(guint32 fd) -{ - guint32 stdhandle; - - switch(fd) { - case 0: - stdhandle=STD_INPUT_HANDLE; - break; - case 1: - stdhandle=STD_OUTPUT_HANDLE; - break; - case 2: - stdhandle=STD_ERROR_HANDLE; - break; - default: - g_warning("unknown standard file descriptor %d", fd); - stdhandle=STD_INPUT_HANDLE; - } - - return(stdhandle); -} -#endif - -static guint32 convert_seekorigin(MonoSeekOrigin origin) -{ - guint32 w32origin; - - switch(origin) { - case SeekOrigin_Begin: - w32origin=FILE_BEGIN; - break; - case SeekOrigin_Current: - w32origin=FILE_CURRENT; - break; - case SeekOrigin_End: - w32origin=FILE_END; - break; - default: - g_warning("System.IO.SeekOrigin has unknown value 0x%x", - origin); - /* Safe fallback */ - w32origin=FILE_CURRENT; - } - - return(w32origin); -} - -static gint64 convert_filetime (const FILETIME *filetime) -{ - guint64 ticks = filetime->dwHighDateTime; - ticks <<= 32; - ticks += filetime->dwLowDateTime; - return (gint64)ticks; -} - -static void convert_win32_file_attribute_data (const WIN32_FILE_ATTRIBUTE_DATA *data, MonoIOStat *stat) -{ - 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; -} - -/* Managed file attributes have nearly but not quite the same values - * as the w32 equivalents. - */ -static guint32 convert_attrs(MonoFileAttributes attrs) -{ - if(attrs & FileAttributes_Encrypted) { - attrs = (MonoFileAttributes)(attrs | FILE_ATTRIBUTE_ENCRYPTED); - } - - 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 -ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error) -{ - gboolean ret; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - ret=CreateDirectory (mono_string_chars (path), NULL); - if(ret==FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -MonoBoolean -ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error) -{ - gboolean ret; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - ret=RemoveDirectory (mono_string_chars (path)); - if(ret==FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -static gchar * -get_search_dir (const gunichar2 *pattern) -{ - gchar *p; - gchar *result; - - p = g_utf16_to_utf8 (pattern, -1, NULL, NULL, NULL); - result = g_path_get_dirname (p); - g_free (p); - return result; -} - -static GPtrArray * -get_filesystem_entries (const gunichar2 *path, - const gunichar2 *path_with_pattern, - gint attrs, gint mask, - gint32 *error) -{ - int i; - WIN32_FIND_DATA data; - HANDLE find_handle; - GPtrArray *names = NULL; - gchar *utf8_path = NULL, *utf8_result, *full_name; - gint32 attributes; - - mask = convert_attrs ((MonoFileAttributes)mask); - attributes = get_file_attributes (path); - if (attributes != -1) { - if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { - *error = ERROR_INVALID_NAME; - goto fail; - } - } else { - *error = GetLastError (); - goto fail; - } - - find_handle = FindFirstFile (path_with_pattern, &data); - if (find_handle == INVALID_HANDLE_VALUE) { - gint32 find_error = GetLastError (); - - if (find_error == ERROR_FILE_NOT_FOUND || find_error == ERROR_NO_MORE_FILES) { - /* No files, so just return an empty array */ - goto fail; - } - - *error = find_error; - goto fail; - } - - utf8_path = get_search_dir (path_with_pattern); - 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 (); - goto fail; - } - - g_free (utf8_path); - return names; -fail: - if (names) { - 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); - return FALSE; -} - - -MonoArray * -ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path, - MonoString *path_with_pattern, - gint attrs, gint mask, - gint32 *ioerror) -{ - MonoError error; - MonoDomain *domain = mono_domain_get (); - MonoArray *result; - int i; - GPtrArray *names; - - *ioerror = ERROR_SUCCESS; - - MONO_ENTER_GC_SAFE; - names = get_filesystem_entries (mono_string_chars (path), mono_string_chars (path_with_pattern), attrs, mask, ioerror); - MONO_EXIT_GC_SAFE; - - if (!names) { - // If there's no array and no error, then return an empty array. - if (*ioerror == ERROR_SUCCESS) { - MonoArray *arr = mono_array_new_checked (domain, mono_defaults.string_class, 0, &error); - mono_error_set_pending_exception (&error); - return arr; - } - return NULL; - } - - result = mono_array_new_checked (domain, mono_defaults.string_class, names->len, &error); - if (mono_error_set_pending_exception (&error)) - goto leave; - for (i = 0; i < names->len; i++) { - mono_array_setref (result, i, mono_string_new (domain, (const char *)g_ptr_array_index (names, i))); - g_free (g_ptr_array_index (names, i)); - } -leave: - g_ptr_array_free (names, TRUE); - 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; -} - -HANDLE -ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, MonoString **file_name, gint32 *file_attr, gint32 *ioerror) -{ - HANDLE hnd; - WIN32_FIND_DATA data; - MonoError error; - - hnd = FindFirstFile (mono_string_chars (path_with_pattern), &data); - - if (hnd == INVALID_HANDLE_VALUE) { - *file_name = NULL; - *file_attr = 0; - *ioerror = GetLastError (); - return hnd; - } - - mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error)); - mono_error_set_pending_exception (&error); - - *file_attr = data.dwFileAttributes; - *ioerror = ERROR_SUCCESS; - - return hnd; -} - -MonoBoolean -ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd, MonoString **file_name, gint32 *file_attr, gint32 *ioerror) -{ - MonoBoolean res; - WIN32_FIND_DATA data; - MonoError error; - - res = FindNextFile (hnd, &data); - - if (res == FALSE) { - *file_name = NULL; - *file_attr = 0; - *ioerror = GetLastError (); - return res; - } - - mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error)); - mono_error_set_pending_exception (&error); - - *file_attr = data.dwFileAttributes; - *ioerror = ERROR_SUCCESS; - - return res; -} - -MonoBoolean -ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd) -{ - return FindClose (hnd); -} - -/* FIXME make gc suspendable */ -MonoString * -ves_icall_System_IO_MonoIO_FindFirst (MonoString *path, - MonoString *path_with_pattern, - gint32 *result_attr, gint32 *ioerror, - gpointer *handle) -{ - MonoError error; - WIN32_FIND_DATA data; - HANDLE find_handle; - IncrementalFind *ifh; - MonoString *result; - - *ioerror = 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; - - *ioerror = find_error; - return NULL; - } - - 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)) { - MONO_ENTER_GC_SAFE; - FindClose (find_handle); - MONO_EXIT_GC_SAFE; - g_free (ifh); - return NULL; - } - 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) - *ioerror = e; - return NULL; - } - } - *result_attr = data.dwFileAttributes; - - return result; -} - -/* FIXME make gc suspendable */ -MonoString * -ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *error) -{ - IncrementalFind *ifh = (IncrementalFind *)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 = (IncrementalFind *)handle; - gint32 error; - - MONO_ENTER_GC_SAFE; - if (FindClose (ifh->find_handle) == FALSE){ - error = GetLastError (); - } else - error = ERROR_SUCCESS; - g_free (ifh->utf8_path); - g_free (ifh); - MONO_EXIT_GC_SAFE; - - return error; -} - -MonoString * -ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error) -{ - MonoError error; - MonoString *result; - gunichar2 *buf; - int len, res_len; - - len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/ - buf = g_new (gunichar2, len); - - mono_error_init (&error); - *io_error=ERROR_SUCCESS; - result = NULL; - - 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; - - result = mono_string_new_utf16_checked (mono_domain_get (), buf, len, &error); - } else { - *io_error=GetLastError (); - } - - g_free (buf); - mono_error_set_pending_exception (&error); - return result; -} - -MonoBoolean -ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path, - gint32 *error) -{ - gboolean ret; - - *error=ERROR_SUCCESS; - - ret=SetCurrentDirectory (mono_string_chars (path)); - if(ret==FALSE) { - *error=GetLastError (); - } - - return(ret); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -gboolean -mono_file_io_move_file (gunichar2 *path, gunichar2 *dest, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = MoveFile (path, dest); - if (result == FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} -#endif /* HAVE_CLASSIC_WINAPI_SUPPORT */ - -MonoBoolean -ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, gint32 *error) -{ - *error=ERROR_SUCCESS; - return mono_file_io_move_file (mono_string_chars (path), mono_string_chars (dest), error); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -gboolean -mono_file_io_replace_file (gunichar2 *destinationFileName, gunichar2 *sourceFileName, - gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL); - if (result == FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} -#endif /* HAVE_CLASSIC_WINAPI_SUPPORT */ - -MonoBoolean -ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName, - MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors, - gint32 *error) -{ - gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL; - guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH; - - 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; - - /* FIXME: source and destination file names must not be NULL, but apparently they might be! */ - return mono_file_io_replace_file (utf16_destinationFileName, utf16_sourceFileName, - utf16_destinationBackupFileName, replaceFlags, error); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -gboolean -mono_file_io_copy_file (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = CopyFile (path, dest, !overwrite); - if (result == FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} -#endif /* HAVE_CLASSIC_WINAPI_SUPPORT */ - -MonoBoolean -ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest, - MonoBoolean overwrite, gint32 *error) -{ - *error=ERROR_SUCCESS; - return mono_file_io_copy_file (mono_string_chars (path), mono_string_chars (dest), overwrite, error); -} - -MonoBoolean -ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error) -{ - gboolean ret; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - ret=DeleteFile (mono_string_chars (path)); - if(ret==FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -gint32 -ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error) -{ - gint32 ret; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - ret=get_file_attributes (mono_string_chars (path)); - - /* - * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32 - * headers is wrong, hence this temporary workaround. - * See - * http://cygwin.com/ml/cygwin/2003-09/msg01771.html - */ - if (ret==-1) { - /* if(ret==INVALID_FILE_ATTRIBUTES) { */ - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -MonoBoolean -ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs, - gint32 *error) -{ - gboolean ret; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - ret=SetFileAttributes (mono_string_chars (path), - convert_attrs ((MonoFileAttributes)attrs)); - if(ret==FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -gint32 -ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error) -{ - gboolean ret; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - ret=GetFileType (handle); - if(ret==FILE_TYPE_UNKNOWN) { - /* Not necessarily an error, but the caller will have - * to decide based on the error value. - */ - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -MonoBoolean -ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, - gint32 *error) -{ - gboolean result; - WIN32_FILE_ATTRIBUTE_DATA data; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - 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)); - } - - MONO_EXIT_GC_SAFE; - return result; -} - -HANDLE -ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, - gint32 access_mode, gint32 share, gint32 options, - gint32 *error) -{ - HANDLE ret; - int attributes, attrs; - gunichar2 *chars; - MONO_ENTER_GC_SAFE; - - chars = mono_string_chars (filename); - *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; - - 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 (chars, convert_access ((MonoFileAccess)access_mode), - convert_share ((MonoFileShare)share), NULL, convert_mode ((MonoFileMode)mode), - attributes, NULL); - if(ret==INVALID_HANDLE_VALUE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -MonoBoolean -ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error) -{ - gboolean ret; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - ret=CloseHandle (handle); - if(ret==FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -gint32 -ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest, - gint32 dest_offset, gint32 count, - gint32 *error) -{ - guchar *buffer; - gboolean result; - guint32 n; - - *error=ERROR_SUCCESS; - - MONO_CHECK_ARG_NULL (dest, 0); - - if (dest_offset > mono_array_length (dest) - count) { - mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong.")); - return 0; - } - - buffer = mono_array_addr (dest, guchar, dest_offset); - - MONO_ENTER_GC_SAFE; - result = ReadFile (handle, buffer, count, &n, NULL); - MONO_EXIT_GC_SAFE; - - if (!result) { - *error=GetLastError (); - return -1; - } - - return (gint32)n; -} - -gint32 -ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src, - gint32 src_offset, gint32 count, - gint32 *error) -{ - guchar *buffer; - gboolean result; - guint32 n; - - *error=ERROR_SUCCESS; - - MONO_CHECK_ARG_NULL (src, 0); - - if (src_offset > mono_array_length (src) - count) { - mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong.")); - return 0; - } - - buffer = mono_array_addr (src, guchar, src_offset); - MONO_ENTER_GC_SAFE; - result = WriteFile (handle, buffer, count, &n, NULL); - MONO_EXIT_GC_SAFE; - - if (!result) { - *error=GetLastError (); - return -1; - } - - return (gint32)n; -} - -gint64 -ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin, - gint32 *error) -{ - gint32 offset_hi; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - offset_hi = offset >> 32; - offset = SetFilePointer (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi, - convert_seekorigin ((MonoSeekOrigin)origin)); - - if(offset==INVALID_SET_FILE_POINTER) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return offset | ((gint64)offset_hi << 32); -} - -MonoBoolean -ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error) -{ - gboolean ret; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - ret=FlushFileBuffers (handle); - if(ret==FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -gint64 -mono_file_io_get_file_size (HANDLE handle, gint32 *error) -{ - gint64 length; - guint32 length_hi; - - MONO_ENTER_GC_SAFE; - - length = GetFileSize (handle, &length_hi); - if(length==INVALID_FILE_SIZE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return length | ((gint64)length_hi << 32); -} -#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ - -gint64 -ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error) -{ - *error=ERROR_SUCCESS; - return mono_file_io_get_file_size (handle, error); -} - -/* FIXME make gc suspendable */ -MonoBoolean -ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length, - gint32 *error) -{ - gint64 offset, offset_set; - gint32 offset_hi; - gint32 length_hi; - gboolean result; - - *error=ERROR_SUCCESS; - - /* save file pointer */ - - offset_hi = 0; - offset = SetFilePointer (handle, 0, &offset_hi, FILE_CURRENT); - if(offset==INVALID_SET_FILE_POINTER) { - *error=GetLastError (); - return(FALSE); - } - - /* extend or truncate */ - - length_hi = length >> 32; - offset_set=SetFilePointer (handle, length & 0xFFFFFFFF, &length_hi, - FILE_BEGIN); - if(offset_set==INVALID_SET_FILE_POINTER) { - *error=GetLastError (); - return(FALSE); - } - - result = SetEndOfFile (handle); - if(result==FALSE) { - *error=GetLastError (); - return(FALSE); - } - - /* restore file pointer */ - - offset_set=SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi, - FILE_BEGIN); - if(offset_set==INVALID_SET_FILE_POINTER) { - *error=GetLastError (); - return(FALSE); - } - - return result; -} - -MonoBoolean -ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time, - gint64 last_access_time, - gint64 last_write_time, gint32 *error) -{ - gboolean ret; - const FILETIME *creation_filetime; - const FILETIME *last_access_filetime; - const FILETIME *last_write_filetime; - MONO_ENTER_GC_SAFE; - - *error=ERROR_SUCCESS; - - if (creation_time < 0) - creation_filetime = NULL; - else - creation_filetime = (FILETIME *)&creation_time; - - if (last_access_time < 0) - last_access_filetime = NULL; - else - last_access_filetime = (FILETIME *)&last_access_time; - - if (last_write_time < 0) - last_write_filetime = NULL; - else - last_write_filetime = (FILETIME *)&last_write_time; - - ret=SetFileTime (handle, creation_filetime, last_access_filetime, last_write_filetime); - if(ret==FALSE) { - *error=GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return(ret); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -HANDLE -mono_file_io_get_console_output (void) -{ - return GetStdHandle (STD_OUTPUT_HANDLE); -} -#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ - -HANDLE -ves_icall_System_IO_MonoIO_get_ConsoleOutput () -{ - return mono_file_io_get_console_output (); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -HANDLE -mono_file_io_get_console_input (void) -{ - return GetStdHandle (STD_INPUT_HANDLE); -} -#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ - -HANDLE -ves_icall_System_IO_MonoIO_get_ConsoleInput () -{ - return mono_file_io_get_console_input (); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -HANDLE -mono_file_io_get_console_error (void) -{ - return GetStdHandle (STD_ERROR_HANDLE); -} -#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ - -HANDLE -ves_icall_System_IO_MonoIO_get_ConsoleError () -{ - return mono_file_io_get_console_error (); -} - -MonoBoolean -ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, HANDLE *write_handle, gint32 *error) -{ - SECURITY_ATTRIBUTES attr; - gboolean ret; - - attr.nLength=sizeof(SECURITY_ATTRIBUTES); - attr.bInheritHandle=TRUE; - attr.lpSecurityDescriptor=NULL; - - MONO_ENTER_GC_SAFE; - ret=CreatePipe (read_handle, write_handle, &attr, 0); - MONO_EXIT_GC_SAFE; - - if(ret==FALSE) { - *error = GetLastError (); - /* FIXME: throw an exception? */ - return(FALSE); - } - - 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, gint32 *error) -{ - /* This is only used on Windows */ - gboolean ret; - - MONO_ENTER_GC_SAFE; -#ifdef HOST_WIN32 - ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options); -#else - mono_w32handle_ref (source_handle); - *target_handle = source_handle; - ret = TRUE; -#endif - MONO_EXIT_GC_SAFE; - - if(ret==FALSE) { - *error = GetLastError (); - /* FIXME: throw an exception? */ - return(FALSE); - } - - return(TRUE); -} - -#ifndef HOST_WIN32 -gunichar2 -ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar () -{ - return (gunichar2) '/'; /* forward slash */ -} - -gunichar2 -ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar () -{ - return (gunichar2) '/'; /* forward slash */ -} - -gunichar2 -ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar () -{ - if (IS_PORTABILITY_SET) - return (gunichar2) '\\'; /* backslash */ - else - return (gunichar2) '/'; /* forward slash */ -} - -gunichar2 -ves_icall_System_IO_MonoIO_get_PathSeparator () -{ - return (gunichar2) ':'; /* colon */ -} -#endif /* !HOST_WIN32 */ - -static const gunichar2 -invalid_path_chars [] = { -#if defined (TARGET_WIN32) - 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */ - 0x003c, /* less than */ - 0x003e, /* greater than */ - 0x007c, /* pipe */ - 0x0008, - 0x0010, - 0x0011, - 0x0012, - 0x0014, - 0x0015, - 0x0016, - 0x0017, - 0x0018, - 0x0019, -#endif - 0x0000 /* null */ -}; - -MonoArray * -ves_icall_System_IO_MonoIO_get_InvalidPathChars () -{ - MonoError error; - MonoArray *chars; - MonoDomain *domain; - int i, n; - - domain = mono_domain_get (); - n = sizeof (invalid_path_chars) / sizeof (gunichar2); - chars = mono_array_new_checked (domain, mono_defaults.char_class, n, &error); - if (mono_error_set_pending_exception (&error)) - return NULL; - - for (i = 0; i < n; ++ i) - mono_array_set (chars, gunichar2, i, invalid_path_chars [i]); - - return chars; -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -gboolean -mono_file_io_lock_file (HANDLE handle, gint64 position, gint64 length, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, - length & 0xFFFFFFFF, length >> 32); - - if (result == FALSE) { - *error = GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} -#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ - -void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position, - gint64 length, gint32 *error) -{ - *error=ERROR_SUCCESS; - mono_file_io_lock_file (handle, position, length, error); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) -gboolean -mono_file_io_unlock_file (HANDLE handle, gint64 position, gint64 length, gint32 *error) -{ - gboolean result = FALSE; - MONO_ENTER_GC_SAFE; - - result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, - length & 0xFFFFFFFF, length >> 32); - - if (result == FALSE) { - *error = GetLastError (); - } - - MONO_EXIT_GC_SAFE; - return result; -} -#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ - -void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position, - gint64 length, gint32 *error) -{ - *error=ERROR_SUCCESS; - mono_file_io_unlock_file (handle, position, length, error); -} - -//Support for io-layer free mmap'd files. - -#if defined (TARGET_IOS) || defined (TARGET_ANDROID) - -gint64 -mono_filesize_from_path (MonoString *string) -{ - MonoError error; - struct stat buf; - gint64 res; - char *path = mono_string_to_utf8_checked (string, &error); - mono_error_raise_exception (&error); /* OK to throw, external only without a good alternative */ - - MONO_ENTER_GC_SAFE; - if (stat (path, &buf) == -1) - res = -1; - else - res = (gint64)buf.st_size; - - g_free (path); - - MONO_EXIT_GC_SAFE; - return res; -} - -gint64 -mono_filesize_from_fd (int fd) -{ - struct stat buf; - int res; - - MONO_ENTER_GC_SAFE; - res = fstat (fd, &buf); - MONO_EXIT_GC_SAFE; - - if (res == -1) - return (gint64)-1; - - return (gint64)buf.st_size; -} - -#endif - -#ifndef HOST_WIN32 -void mono_w32handle_dump (void); - -void ves_icall_System_IO_MonoIO_DumpHandles (void) -{ - - mono_w32handle_dump (); -} -#endif /* !HOST_WIN32 */ diff --git a/mono/metadata/file-io.h b/mono/metadata/file-io.h deleted file mode 100644 index b7b7320debb..00000000000 --- a/mono/metadata/file-io.h +++ /dev/null @@ -1,277 +0,0 @@ -/* - * file-io.h: File IO internal calls - * - * Authors: - * Dick Porter (dick@ximian.com) - * Dan Lewis (dihlewis@yahoo.co.uk) - * - * (C) 2001 Ximian, Inc. - * Copyright 2012 Xamarin Inc (http://www.xamarin.com) - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#ifndef _MONO_METADATA_FILEIO_H_ -#define _MONO_METADATA_FILEIO_H_ - -#include -#include - -#include -#include - -G_BEGIN_DECLS - -/* This is a copy of System.IO.FileAccess */ -typedef enum { - FileAccess_Read=0x01, - FileAccess_Write=0x02, - FileAccess_ReadWrite=FileAccess_Read|FileAccess_Write -} MonoFileAccess; - -/* This is a copy of System.IO.FileMode */ -typedef enum { - FileMode_CreateNew=1, - FileMode_Create=2, - FileMode_Open=3, - FileMode_OpenOrCreate=4, - FileMode_Truncate=5, - FileMode_Append=6 -} MonoFileMode; - -/* This is a copy of System.IO.FileShare */ -typedef enum { - FileShare_None=0x0, - FileShare_Read=0x01, - FileShare_Write=0x02, - FileShare_ReadWrite=FileShare_Read|FileShare_Write, - FileShare_Delete=0x04 -} MonoFileShare; - -/* This is a copy of System.IO.FileOptions */ -typedef enum { - FileOptions_None = 0, - FileOptions_Temporary = 1, // Internal. See note in System.IO.FileOptions - FileOptions_Encrypted = 0x4000, - FileOptions_DeleteOnClose = 0x4000000, - FileOptions_SequentialScan = 0x8000000, - FileOptions_RandomAccess = 0x10000000, - FileOptions_Asynchronous = 0x40000000, - FileOptions_WriteThrough = 0x80000000 -} MonoFileOptions; - -/* This is a copy of System.IO.SeekOrigin */ -typedef enum { - SeekOrigin_Begin=0, - SeekOrigin_Current=1, - SeekOrigin_End=2 -} MonoSeekOrigin; - -/* This is a copy of System.IO.MonoIOStat */ -typedef struct _MonoIOStat { - gint32 attributes; - gint64 length; - gint64 creation_time; - gint64 last_access_time; - gint64 last_write_time; -} MonoIOStat; - -/* This is a copy of System.IO.FileAttributes */ -typedef enum { - FileAttributes_ReadOnly=0x00001, - FileAttributes_Hidden=0x00002, - FileAttributes_System=0x00004, - FileAttributes_Directory=0x00010, - FileAttributes_Archive=0x00020, - FileAttributes_Device=0x00040, - FileAttributes_Normal=0x00080, - FileAttributes_Temporary=0x00100, - FileAttributes_SparseFile=0x00200, - FileAttributes_ReparsePoint=0x00400, - FileAttributes_Compressed=0x00800, - FileAttributes_Offline=0x01000, - FileAttributes_NotContentIndexed=0x02000, - FileAttributes_Encrypted=0x04000, - FileAttributes_MonoExecutable= (int) 0x80000000 -} MonoFileAttributes; -/* This is not used anymore -typedef struct _MonoFSAsyncResult { - MonoObject obj; - MonoObject *state; - MonoBoolean completed; - MonoBoolean done; - MonoException *exc; - MonoWaitHandle *wait_handle; - MonoDelegate *async_callback; - MonoBoolean completed_synch; - MonoArray *buffer; - gint offset; - gint count; - gint original_count; - gint bytes_read; - MonoDelegate *real_cb; -} MonoFSAsyncResult; -*/ -/* System.IO.MonoIO */ - -extern MonoBoolean -ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error); - -MonoArray * -ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path, - MonoString *path_with_pattern, - gint mask, gint attrs, - gint32 *error); - -extern gpointer -ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, - MonoString **file_name, - gint32 *file_attr, - gint32 *ioerror); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_FindNextFile (gpointer hnd, - MonoString **file_name, - gint32 *file_attr, - gint32 *ioerror); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_FindCloseFile (gpointer hnd); - -extern MonoString * -ves_icall_System_IO_MonoIO_FindFirst (MonoString *path, - MonoString *path_with_pattern, - gint32 *result_mask, - gint32 *error, - gpointer *handle); -extern MonoString * -ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_mask, gint32 *error); - -extern int -ves_icall_System_IO_MonoIO_FindClose (gpointer handle); - -extern MonoString * -ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path, - gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, - gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest, - MonoBoolean overwrite, gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error); - -extern gint32 -ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs, - gint32 *error); - -extern gint32 -ves_icall_System_IO_MonoIO_GetFileType (gpointer handle, gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, - gint32 *error); - -extern gpointer -ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, - gint32 access_mode, gint32 share, gint32 options, - gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_Close (gpointer handle, gint32 *error); - -extern gint32 -ves_icall_System_IO_MonoIO_Read (gpointer handle, MonoArray *dest, - gint32 dest_offset, gint32 count, - gint32 *error); - -extern gint32 -ves_icall_System_IO_MonoIO_Write (gpointer handle, MonoArray *src, - gint32 src_offset, gint32 count, - gint32 *error); - -extern gint64 -ves_icall_System_IO_MonoIO_Seek (gpointer handle, gint64 offset, gint32 origin, - gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_Flush (gpointer handle, gint32 *error); - -extern gint64 -ves_icall_System_IO_MonoIO_GetLength (gpointer handle, gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_SetLength (gpointer handle, gint64 length, - gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_SetFileTime (gpointer handle, gint64 creation_time, - gint64 last_access_time, - gint64 last_write_time, gint32 *error); - -extern gpointer -ves_icall_System_IO_MonoIO_get_ConsoleOutput (void); - -extern gpointer -ves_icall_System_IO_MonoIO_get_ConsoleInput (void); - -extern gpointer -ves_icall_System_IO_MonoIO_get_ConsoleError (void); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_CreatePipe (gpointer *read_handle, gpointer *write_handle, gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_DuplicateHandle (gpointer source_process_handle, gpointer source_handle, - gpointer target_process_handle, gpointer *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error); - -extern gunichar2 -ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar (void); - -extern gunichar2 -ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar (void); - -extern gunichar2 -ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar (void); - -extern gunichar2 -ves_icall_System_IO_MonoIO_get_PathSeparator (void); - -extern MonoArray * -ves_icall_System_IO_MonoIO_get_InvalidPathChars (void); - -extern void ves_icall_System_IO_MonoIO_Lock (gpointer handle, gint64 position, - gint64 length, gint32 *error); -extern void ves_icall_System_IO_MonoIO_Unlock (gpointer handle, gint64 position, - gint64 length, gint32 *error); - -extern MonoBoolean -ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName, - MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors, - gint32 *error); - -MONO_RT_EXTERNAL_ONLY -extern gint64 -mono_filesize_from_path (MonoString *path); - -extern gint64 -mono_filesize_from_fd (int fd); - -void -ves_icall_System_IO_MonoIO_DumpHandles (void); - -G_END_DECLS - -#endif /* _MONO_METADATA_FILEIO_H_ */ diff --git a/mono/metadata/file-mmap-posix.c b/mono/metadata/file-mmap-posix.c index 4b9ce74499e..64332ed18d1 100644 --- a/mono/metadata/file-mmap-posix.c +++ b/mono/metadata/file-mmap-posix.c @@ -32,7 +32,7 @@ #include -#include +#include #include #include #include diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c index b798a6e7c29..f0dc4ef1238 100644 --- a/mono/metadata/icall.c +++ b/mono/metadata/icall.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include #include @@ -6691,7 +6691,7 @@ mono_icall_get_logical_drives (void) ptr = buf; while (size > initial_size) { - size = (guint) GetLogicalDriveStrings (initial_size, ptr); + size = (guint) mono_w32file_get_logical_drive (initial_size, ptr); if (size > initial_size) { if (ptr != buf) g_free (ptr); @@ -6747,7 +6747,7 @@ ves_icall_System_IO_DriveInfo_GetDriveFormat (MonoString *path) MonoError error; gunichar2 volume_name [MAX_PATH + 1]; - if (GetVolumeInformation (mono_string_chars (path), NULL, 0, NULL, NULL, NULL, volume_name, MAX_PATH + 1) == FALSE) + if (mono_w32file_get_volume_information (mono_string_chars (path), NULL, 0, NULL, NULL, NULL, volume_name, MAX_PATH + 1) == FALSE) return NULL; MonoString *result = mono_string_from_utf16_checked (volume_name, &error); mono_error_set_pending_exception (&error); @@ -7026,24 +7026,12 @@ ves_icall_System_IO_DriveInfo_GetDiskFreeSpace (MonoString *path_name, guint64 * gint32 *error) { gboolean result; - ULARGE_INTEGER wapi_free_bytes_avail; - ULARGE_INTEGER wapi_total_number_of_bytes; - ULARGE_INTEGER wapi_total_number_of_free_bytes; *error = ERROR_SUCCESS; - result = GetDiskFreeSpaceEx (mono_string_chars (path_name), &wapi_free_bytes_avail, &wapi_total_number_of_bytes, - &wapi_total_number_of_free_bytes); - if (result) { - *free_bytes_avail = wapi_free_bytes_avail.QuadPart; - *total_number_of_bytes = wapi_total_number_of_bytes.QuadPart; - *total_number_of_free_bytes = wapi_total_number_of_free_bytes.QuadPart; - } else { - *free_bytes_avail = 0; - *total_number_of_bytes = 0; - *total_number_of_free_bytes = 0; + result = mono_w32file_get_disk_free_space (mono_string_chars (path_name), free_bytes_avail, total_number_of_bytes, total_number_of_free_bytes); + if (!result) *error = GetLastError (); - } return result; } @@ -7052,7 +7040,7 @@ ves_icall_System_IO_DriveInfo_GetDiskFreeSpace (MonoString *path_name, guint64 * static inline guint32 mono_icall_drive_info_get_drive_type (MonoString *root_path_name) { - return GetDriveType (mono_string_chars (root_path_name)); + return mono_w32file_get_drive_type (mono_string_chars (root_path_name)); } #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ diff --git a/mono/metadata/sre-save.c b/mono/metadata/sre-save.c index 421216d8113..4d94b5524b9 100644 --- a/mono/metadata/sre-save.c +++ b/mono/metadata/sre-save.c @@ -24,6 +24,7 @@ #include "mono/metadata/security-manager.h" #include "mono/metadata/tabledefs.h" #include "mono/metadata/tokentype.h" +#include "mono/metadata/w32file.h" #include "mono/utils/checked-build.h" #include "mono/utils/mono-digest.h" @@ -2737,8 +2738,8 @@ static void checked_write_file (HANDLE f, gconstpointer buffer, guint32 numbytes) { guint32 dummy; - if (!WriteFile (f, buffer, numbytes, &dummy, NULL)) - g_error ("WriteFile returned %d\n", GetLastError ()); + if (!mono_w32file_write (f, buffer, numbytes, &dummy)) + g_error ("mono_w32file_write returned %d\n", GetLastError ()); } /* @@ -3032,8 +3033,8 @@ mono_image_create_pefile (MonoReflectionModuleBuilder *mb, HANDLE file, MonoErro if (!assembly->sections [i].size) continue; - if (SetFilePointer (file, assembly->sections [i].offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - g_error ("SetFilePointer returned %d\n", GetLastError ()); + if (mono_w32file_seek (file, assembly->sections [i].offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + g_error ("mono_w32file_seek returned %d\n", GetLastError ()); switch (i) { case MONO_SECTION_TEXT: @@ -3092,10 +3093,10 @@ mono_image_create_pefile (MonoReflectionModuleBuilder *mb, HANDLE file, MonoErro } /* check that the file is properly padded */ - if (SetFilePointer (file, file_offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - g_error ("SetFilePointer returned %d\n", GetLastError ()); - if (! SetEndOfFile (file)) - g_error ("SetEndOfFile returned %d\n", GetLastError ()); + if (mono_w32file_seek (file, file_offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + g_error ("mono_w32file_seek returned %d\n", GetLastError ()); + if (! mono_w32file_truncate (file)) + g_error ("mono_w32file_truncate returned %d\n", GetLastError ()); mono_dynamic_stream_reset (&assembly->code); mono_dynamic_stream_reset (&assembly->us); diff --git a/mono/metadata/threads-types.h b/mono/metadata/threads-types.h index 4387f013cdb..626a26bb31b 100644 --- a/mono/metadata/threads-types.h +++ b/mono/metadata/threads-types.h @@ -53,10 +53,6 @@ typedef enum { #define SPECIAL_STATIC_THREAD 1 #define SPECIAL_STATIC_CONTEXT 2 -#ifdef HOST_WIN32 -typedef SECURITY_ATTRIBUTES WapiSecurityAttributes; -#endif - typedef struct _MonoInternalThread MonoInternalThread; typedef void (*MonoThreadCleanupFunc) (MonoNativeThreadId tid); diff --git a/mono/metadata/w32event-unix.c b/mono/metadata/w32event-unix.c index af5b890d162..8c9b1b1f765 100644 --- a/mono/metadata/w32event-unix.c +++ b/mono/metadata/w32event-unix.c @@ -14,6 +14,8 @@ #include "mono/utils/mono-logger-internals.h" #include "mono/metadata/w32handle.h" +#define MAX_PATH 260 + typedef struct { gboolean manual; guint32 set_count; diff --git a/mono/metadata/w32file-internals.h b/mono/metadata/w32file-internals.h new file mode 100644 index 00000000000..0458a64d30b --- /dev/null +++ b/mono/metadata/w32file-internals.h @@ -0,0 +1,11 @@ +/* + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef _MONO_METADATA_W32FILE_INTERNALS_H_ +#define _MONO_METADATA_W32FILE_INTERNALS_H_ + +#include +#include + +#endif /* _MONO_METADATA_W32FILE_INTERNALS_H_ */ diff --git a/mono/metadata/w32file-unix-glob.c b/mono/metadata/w32file-unix-glob.c new file mode 100644 index 00000000000..99e02a47e0b --- /dev/null +++ b/mono/metadata/w32file-unix-glob.c @@ -0,0 +1,406 @@ +/* $OpenBSD: glob.c,v 1.26 2005/11/28 17:50:12 deraadt Exp $ */ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * mono_w32file_unix_glob(3) -- a subset of the one defined in POSIX 1003.2. + * + * Optional extra services, controlled by flags not defined by POSIX: + * + * GLOB_MAGCHAR: + * Set in gl_flags if pattern contained a globbing character. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "w32file-unix-glob.h" + +#define EOS '\0' +#define NOT '!' +#define QUESTION '?' +#define QUOTE '\\' +#define STAR '*' + +#ifndef DEBUG + +#define M_QUOTE 0x8000 +#define M_PROTECT 0x4000 +#define M_MASK 0xffff +#define M_ASCII 0x00ff + +typedef unsigned short Char; + +#else + +#define M_QUOTE 0x80 +#define M_PROTECT 0x40 +#define M_MASK 0xff +#define M_ASCII 0x7f + +typedef char Char; + +#endif + + +#define CHAR(c) ((gchar)((c)&M_ASCII)) +#define META(c) ((gchar)((c)|M_QUOTE)) +#define M_ALL META('*') +#define M_ONE META('?') +#define ismeta(c) (((c)&M_QUOTE) != 0) + + +static int +g_Ctoc(const gchar *, char *, unsigned int); + +static int +glob0(GDir *dir, const gchar *, mono_w32file_unix_glob_t *, gboolean, gboolean); +static int +glob1(GDir *dir, gchar *, gchar *, mono_w32file_unix_glob_t *, size_t *, gboolean, gboolean); + +static int +glob3(GDir *dir, gchar *, gchar *, mono_w32file_unix_glob_t *, size_t *, gboolean, gboolean); + +static int +globextend(const gchar *, mono_w32file_unix_glob_t *, size_t *); + +static int +match(const gchar *, gchar *, gchar *, gboolean); + +#ifdef DEBUG_ENABLED +static void qprintf(const char *, Char *); +#endif + +int +mono_w32file_unix_glob(GDir *dir, const char *pattern, int flags, mono_w32file_unix_glob_t *pglob) +{ + const unsigned char *patnext; + int c; + gchar *bufnext, *bufend, patbuf[PATH_MAX]; + + patnext = (unsigned char *) pattern; + if (!(flags & W32FILE_UNIX_GLOB_APPEND)) { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + pglob->gl_offs = 0; + } + pglob->gl_flags = flags & ~W32FILE_UNIX_GLOB_MAGCHAR; + + bufnext = patbuf; + bufend = bufnext + PATH_MAX - 1; + + /* Protect the quoted characters. */ + while (bufnext < bufend && (c = *patnext++) != EOS) + if (c == QUOTE) { + if ((c = *patnext++) == EOS) { + c = QUOTE; + --patnext; + } + *bufnext++ = c | M_PROTECT; + } else + *bufnext++ = c; + + *bufnext = EOS; + + return glob0(dir, patbuf, pglob, flags & W32FILE_UNIX_GLOB_IGNORECASE, + flags & W32FILE_UNIX_GLOB_UNIQUE); +} + +/* + * The main glob() routine: compiles the pattern (optionally processing + * quotes), calls glob1() to do the real pattern matching, and finally + * sorts the list (unless unsorted operation is requested). Returns 0 + * if things went well, nonzero if errors occurred. It is not an error + * to find no matches. + */ +static int +glob0(GDir *dir, const gchar *pattern, mono_w32file_unix_glob_t *pglob, gboolean ignorecase, + gboolean unique) +{ + const gchar *qpatnext; + int c, err, oldpathc; + gchar *bufnext, patbuf[PATH_MAX]; + size_t limit = 0; + + qpatnext = pattern; + oldpathc = pglob->gl_pathc; + bufnext = patbuf; + + /* We don't need to check for buffer overflow any more. */ + while ((c = *qpatnext++) != EOS) { + switch (c) { + case QUESTION: + pglob->gl_flags |= W32FILE_UNIX_GLOB_MAGCHAR; + *bufnext++ = M_ONE; + break; + case STAR: + pglob->gl_flags |= W32FILE_UNIX_GLOB_MAGCHAR; + /* collapse adjacent stars to one, + * to avoid exponential behavior + */ + if (bufnext == patbuf || bufnext[-1] != M_ALL) + *bufnext++ = M_ALL; + break; + default: + *bufnext++ = CHAR(c); + break; + } + } + *bufnext = EOS; +#ifdef DEBUG_ENABLED + qprintf("glob0:", patbuf); +#endif + + if ((err = glob1(dir, patbuf, patbuf+PATH_MAX-1, pglob, &limit, + ignorecase, unique)) != 0) + return(err); + + if (pglob->gl_pathc == oldpathc) { + return(W32FILE_UNIX_GLOB_NOMATCH); + } + + return(0); +} + +static int +glob1(GDir *dir, gchar *pattern, gchar *pattern_last, mono_w32file_unix_glob_t *pglob, + size_t *limitp, gboolean ignorecase, gboolean unique) +{ + /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ + if (*pattern == EOS) + return(0); + return(glob3(dir, pattern, pattern_last, pglob, limitp, ignorecase, + unique)); +} + +static gboolean contains (mono_w32file_unix_glob_t *pglob, const gchar *name) +{ + int i; + char **pp; + + if (pglob->gl_pathv != NULL) { + pp = pglob->gl_pathv + pglob->gl_offs; + for (i = pglob->gl_pathc; i--; ++pp) { + if (*pp) { + if (!strcmp (*pp, name)) { + return(TRUE); + } + } + } + } + + return(FALSE); +} + +static int +glob3(GDir *dir, gchar *pattern, gchar *pattern_last, mono_w32file_unix_glob_t *pglob, + size_t *limitp, gboolean ignorecase, gboolean unique) +{ + const gchar *name; + + /* Search directory for matching names. */ + while ((name = g_dir_read_name(dir))) { + if (!match(name, pattern, pattern + strlen (pattern), + ignorecase)) { + continue; + } + if (!unique || + !contains (pglob, name)) { + globextend (name, pglob, limitp); + } + } + + return(0); +} + + +/* + * Extend the gl_pathv member of a mono_w32file_unix_glob_t structure to accommodate a new item, + * add the new item, and update gl_pathc. + * + * This assumes the BSD realloc, which only copies the block when its size + * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic + * behavior. + * + * Return 0 if new item added, error code if memory couldn't be allocated. + * + * Invariant of the mono_w32file_unix_glob_t structure: + * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and + * gl_pathv points to (gl_offs + gl_pathc + 1) items. + */ +static int +globextend(const gchar *path, mono_w32file_unix_glob_t *pglob, size_t *limitp) +{ + char **pathv; + int i; + unsigned int newsize, len; + char *copy; + const gchar *p; + + newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); + /* FIXME: Can just use realloc(). */ + pathv = (char **)(pglob->gl_pathv ? g_realloc ((char *)pglob->gl_pathv, newsize) : + g_malloc (newsize)); + if (pathv == NULL) { + if (pglob->gl_pathv) { + g_free (pglob->gl_pathv); + pglob->gl_pathv = NULL; + } + return(W32FILE_UNIX_GLOB_NOSPACE); + } + + if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { + /* first time around -- clear initial gl_offs items */ + pathv += pglob->gl_offs; + for (i = pglob->gl_offs; --i >= 0; ) + *--pathv = NULL; + } + pglob->gl_pathv = pathv; + + for (p = path; *p++;) + ; + len = (size_t)(p - path); + *limitp += len; + if ((copy = (char *)malloc(len)) != NULL) { + if (g_Ctoc(path, copy, len)) { + g_free (copy); + return(W32FILE_UNIX_GLOB_NOSPACE); + } + pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; + } + pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; + +#if 0 + /* Broken on opensuse 11 */ + if ((pglob->gl_flags & W32FILE_UNIX_GLOB_LIMIT) && + newsize + *limitp >= ARG_MAX) { + errno = 0; + return(W32FILE_UNIX_GLOB_NOSPACE); + } +#endif + + return(copy == NULL ? W32FILE_UNIX_GLOB_NOSPACE : 0); +} + + +/* + * pattern matching function for filenames. Each occurrence of the * + * pattern causes a recursion level. + */ +static int +match(const gchar *name, gchar *pat, gchar *patend, gboolean ignorecase) +{ + gchar c; + + while (pat < patend) { + c = *pat++; + switch (c & M_MASK) { + case M_ALL: + if (pat == patend) + return(1); + do { + if (match(name, pat, patend, ignorecase)) + return(1); + } while (*name++ != EOS); + return(0); + case M_ONE: + if (*name++ == EOS) + return(0); + break; + default: + if (ignorecase) { + if (g_ascii_tolower (*name++) != g_ascii_tolower (c)) + return(0); + } else { + if (*name++ != c) + return(0); + } + + break; + } + } + return(*name == EOS); +} + +/* Free allocated data belonging to a mono_w32file_unix_glob_t structure. */ +void +mono_w32file_unix_globfree(mono_w32file_unix_glob_t *pglob) +{ + int i; + char **pp; + + if (pglob->gl_pathv != NULL) { + pp = pglob->gl_pathv + pglob->gl_offs; + for (i = pglob->gl_pathc; i--; ++pp) + if (*pp) + g_free (*pp); + g_free (pglob->gl_pathv); + pglob->gl_pathv = NULL; + } +} + +static int +g_Ctoc(const gchar *str, char *buf, unsigned int len) +{ + + while (len--) { + if ((*buf++ = *str++) == EOS) + return (0); + } + return (1); +} + +#ifdef DEBUG_ENABLED +static void +qprintf(const char *str, Char *s) +{ + Char *p; + + (void)printf("%s:\n", str); + for (p = s; *p; p++) + (void)printf("%c", CHAR(*p)); + (void)printf("\n"); + for (p = s; *p; p++) + (void)printf("%c", *p & M_PROTECT ? '"' : ' '); + (void)printf("\n"); + for (p = s; *p; p++) + (void)printf("%c", ismeta(*p) ? '_' : ' '); + (void)printf("\n"); +} +#endif diff --git a/mono/metadata/w32file-unix-glob.h b/mono/metadata/w32file-unix-glob.h new file mode 100644 index 00000000000..b1923089625 --- /dev/null +++ b/mono/metadata/w32file-unix-glob.h @@ -0,0 +1,73 @@ +/* $OpenBSD: glob.h,v 1.10 2005/12/13 00:35:22 millert Exp $ */ +/* $NetBSD: glob.h,v 1.5 1994/10/26 00:55:56 cgd Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)glob.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef __MONO_METADATA_W32FILE_UNIX_GLOB_H__ +#define __MONO_METADATA_W32FILE_UNIX_GLOB_H__ + +#include + +struct stat; +typedef struct { + int gl_pathc; /* Count of total paths so far. */ + int gl_offs; /* Reserved at beginning of gl_pathv. */ + int gl_flags; /* Copy of flags parameter to glob. */ + char **gl_pathv; /* List of paths matching pattern. */ +} mono_w32file_unix_glob_t; + +#define W32FILE_UNIX_GLOB_APPEND 0x0001 /* Append to output from previous call. */ +#define W32FILE_UNIX_GLOB_UNIQUE 0x0040 /* When appending only add items that aren't already in the list */ +#define W32FILE_UNIX_GLOB_NOSPACE (-1) /* Malloc call failed. */ +#define W32FILE_UNIX_GLOB_ABORTED (-2) /* Unignored error. */ +#define W32FILE_UNIX_GLOB_NOMATCH (-3) /* No match and W32FILE_UNIX_GLOB_NOCHECK not set. */ +#define W32FILE_UNIX_GLOB_NOSYS (-4) /* Function not supported. */ + +#define W32FILE_UNIX_GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */ +#define W32FILE_UNIX_GLOB_LIMIT 0x2000 /* Limit pattern match output to ARG_MAX */ +#define W32FILE_UNIX_GLOB_IGNORECASE 0x4000 /* Ignore case when matching */ +#define W32FILE_UNIX_GLOB_ABEND W32FILE_UNIX_GLOB_ABORTED /* backward compatibility */ + +G_BEGIN_DECLS + +int +mono_w32file_unix_glob (GDir *dir, const char *, int, mono_w32file_unix_glob_t *); + +void +mono_w32file_unix_globfree (mono_w32file_unix_glob_t *); + +G_END_DECLS + +#endif /* !__MONO_METADATA_W32FILE_UNIX_GLOB_H__ */ diff --git a/mono/metadata/w32file-unix.c b/mono/metadata/w32file-unix.c new file mode 100644 index 00000000000..ecca05ec6aa --- /dev/null +++ b/mono/metadata/w32file-unix.c @@ -0,0 +1,4971 @@ + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#if defined(HAVE_SYS_STATFS_H) +#include +#endif +#if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H) +#include +#include +#endif +#include +#include +#include +#ifdef __linux__ +#include +#include +#include +#endif +#include +#ifdef HAVE_DIRENT_H +# include +#endif + +#include "w32file.h" +#include "w32file-internals.h" + +#include "w32file-unix-glob.h" +#include "w32handle.h" +#include "utils/mono-io-portability.h" +#include "utils/mono-logger-internals.h" +#include "utils/mono-os-mutex.h" +#include "utils/mono-threads.h" +#include "utils/mono-threads-api.h" +#include "utils/strenc.h" + +typedef struct { + guint64 device; + guint64 inode; + pid_t opened_by_pid; + guint32 sharemode; + guint32 access; + guint32 handle_refs; + guint32 timestamp; +} FileShare; + +/* Currently used for both FILE, CONSOLE and PIPE handle types. + * This may have to change in future. */ +typedef struct { + gchar *filename; + FileShare *share_info; /* Pointer into shared mem */ + gint fd; + guint32 security_attributes; + guint32 fileaccess; + guint32 sharemode; + guint32 attrs; +} MonoW32HandleFile; + +typedef struct { + gchar **namelist; + gchar *dir_part; + gint num; + gsize count; +} MonoW32HandleFind; + +/* + * If SHM is disabled, this will point to a hash of FileShare structures, otherwise + * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a + * 4MB array. + */ +static GHashTable *file_share_table; +static mono_mutex_t file_share_mutex; + +static void +time_t_to_filetime (time_t timeval, FILETIME *filetime) +{ + guint64 ticks; + + ticks = ((guint64)timeval * 10000000) + 116444736000000000ULL; + filetime->dwLowDateTime = ticks & 0xFFFFFFFF; + filetime->dwHighDateTime = ticks >> 32; +} + +static void +file_share_release (FileShare *share_info) +{ + /* Prevent new entries racing with us */ + mono_os_mutex_lock (&file_share_mutex); + + g_assert (share_info->handle_refs > 0); + share_info->handle_refs -= 1; + + if (share_info->handle_refs == 0) + g_hash_table_remove (file_share_table, share_info); + + mono_os_mutex_unlock (&file_share_mutex); +} + +static gint +file_share_equal (gconstpointer ka, gconstpointer kb) +{ + const FileShare *s1 = (const FileShare *)ka; + const FileShare *s2 = (const FileShare *)kb; + + return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0; +} + +static guint +file_share_hash (gconstpointer data) +{ + const FileShare *s = (const FileShare *)data; + + return s->inode; +} + +static gboolean +file_share_get (guint64 device, guint64 inode, guint32 new_sharemode, guint32 new_access, + guint32 *old_sharemode, guint32 *old_access, FileShare **share_info) +{ + FileShare *file_share; + gboolean exists = FALSE; + + /* Prevent new entries racing with us */ + mono_os_mutex_lock (&file_share_mutex); + + FileShare tmp; + + /* + * Instead of allocating a 4MB array, we use a hash table to keep track of this + * info. This is needed even if SHM is disabled, to track sharing inside + * the current process. + */ + if (!file_share_table) + file_share_table = g_hash_table_new_full (file_share_hash, file_share_equal, NULL, g_free); + + tmp.device = device; + tmp.inode = inode; + + file_share = (FileShare *)g_hash_table_lookup (file_share_table, &tmp); + if (file_share) { + *old_sharemode = file_share->sharemode; + *old_access = file_share->access; + *share_info = file_share; + + g_assert (file_share->handle_refs > 0); + file_share->handle_refs += 1; + + exists = TRUE; + } else { + file_share = g_new0 (FileShare, 1); + + file_share->device = device; + file_share->inode = inode; + file_share->opened_by_pid = wapi_getpid (); + file_share->sharemode = new_sharemode; + file_share->access = new_access; + file_share->handle_refs = 1; + *share_info = file_share; + + g_hash_table_insert (file_share_table, file_share, file_share); + } + + mono_os_mutex_unlock (&file_share_mutex); + + return(exists); +} + +static gint +_wapi_open (const gchar *pathname, gint flags, mode_t mode) +{ + gint fd; + gchar *located_filename; + + if (flags & O_CREAT) { + located_filename = mono_portability_find_file (pathname, FALSE); + if (located_filename == NULL) { + fd = open (pathname, flags, mode); + } else { + fd = open (located_filename, flags, mode); + g_free (located_filename); + } + } else { + fd = open (pathname, flags, mode); + if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + fd = open (located_filename, flags, mode); + g_free (located_filename); + } + } + + return(fd); +} + +static gint +_wapi_access (const gchar *pathname, gint mode) +{ + gint ret; + + ret = access (pathname, mode); + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = access (located_filename, mode); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_chmod (const gchar *pathname, mode_t mode) +{ + gint ret; + + ret = chmod (pathname, mode); + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = chmod (located_filename, mode); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_utime (const gchar *filename, const struct utimbuf *buf) +{ + gint ret; + + ret = utime (filename, buf); + if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (filename, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = utime (located_filename, buf); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_unlink (const gchar *pathname) +{ + gint ret; + + ret = unlink (pathname); + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = unlink (located_filename); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_rename (const gchar *oldpath, const gchar *newpath) +{ + gint ret; + gchar *located_newpath = mono_portability_find_file (newpath, FALSE); + + if (located_newpath == NULL) { + ret = rename (oldpath, newpath); + } else { + ret = rename (oldpath, located_newpath); + + if (ret == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE); + + if (located_oldpath == NULL) { + g_free (located_oldpath); + g_free (located_newpath); + + errno = saved_errno; + return -1; + } + + ret = rename (located_oldpath, located_newpath); + g_free (located_oldpath); + } + g_free (located_newpath); + } + + return ret; +} + +static gint +_wapi_stat (const gchar *path, struct stat *buf) +{ + gint ret; + + ret = stat (path, buf); + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (path, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = stat (located_filename, buf); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_lstat (const gchar *path, struct stat *buf) +{ + gint ret; + + ret = lstat (path, buf); + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (path, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = lstat (located_filename, buf); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_mkdir (const gchar *pathname, mode_t mode) +{ + gint ret; + gchar *located_filename = mono_portability_find_file (pathname, FALSE); + + if (located_filename == NULL) { + ret = mkdir (pathname, mode); + } else { + ret = mkdir (located_filename, mode); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_rmdir (const gchar *pathname) +{ + gint ret; + + ret = rmdir (pathname); + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (pathname, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = rmdir (located_filename); + g_free (located_filename); + } + + return ret; +} + +static gint +_wapi_chdir (const gchar *path) +{ + gint ret; + + ret = chdir (path); + if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) { + gint saved_errno = errno; + gchar *located_filename = mono_portability_find_file (path, TRUE); + + if (located_filename == NULL) { + errno = saved_errno; + return -1; + } + + ret = chdir (located_filename); + g_free (located_filename); + } + + return ret; +} + +static gchar* +_wapi_basename (const gchar *filename) +{ + gchar *new_filename = g_strdup (filename), *ret; + + if (IS_PORTABILITY_SET) { + g_strdelimit (new_filename, "\\", '/'); + } + + if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) { + gint len = strlen (new_filename); + + g_memmove (new_filename, new_filename + 2, len - 2); + new_filename[len - 2] = '\0'; + } + + ret = g_path_get_basename (new_filename); + g_free (new_filename); + + return ret; +} + +static gchar* +_wapi_dirname (const gchar *filename) +{ + gchar *new_filename = g_strdup (filename), *ret; + + if (IS_PORTABILITY_SET) { + g_strdelimit (new_filename, "\\", '/'); + } + + if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) { + gint len = strlen (new_filename); + + g_memmove (new_filename, new_filename + 2, len - 2); + new_filename[len - 2] = '\0'; + } + + ret = g_path_get_dirname (new_filename); + g_free (new_filename); + + return ret; +} + +static GDir* +_wapi_g_dir_open (const gchar *path, guint flags, GError **error) +{ + GDir *ret; + + ret = g_dir_open (path, flags, error); + if (ret == NULL && ((*error)->code == G_FILE_ERROR_NOENT || (*error)->code == G_FILE_ERROR_NOTDIR || (*error)->code == G_FILE_ERROR_NAMETOOLONG) && IS_PORTABILITY_SET) { + gchar *located_filename = mono_portability_find_file (path, TRUE); + GError *tmp_error = NULL; + + if (located_filename == NULL) { + return(NULL); + } + + ret = g_dir_open (located_filename, flags, &tmp_error); + g_free (located_filename); + if (tmp_error == NULL) { + g_clear_error (error); + } + } + + return ret; +} + +static gint +get_errno_from_g_file_error (gint error) +{ + switch (error) { +#ifdef EACCESS + case G_FILE_ERROR_ACCES: return EACCES; +#endif +#ifdef ENAMETOOLONG + case G_FILE_ERROR_NAMETOOLONG: return ENAMETOOLONG; +#endif +#ifdef ENOENT + case G_FILE_ERROR_NOENT: return ENOENT; +#endif +#ifdef ENOTDIR + case G_FILE_ERROR_NOTDIR: return ENOTDIR; +#endif +#ifdef ENXIO + case G_FILE_ERROR_NXIO: return ENXIO; +#endif +#ifdef ENODEV + case G_FILE_ERROR_NODEV: return ENODEV; +#endif +#ifdef EROFS + case G_FILE_ERROR_ROFS: return EROFS; +#endif +#ifdef ETXTBSY + case G_FILE_ERROR_TXTBSY: return ETXTBSY; +#endif +#ifdef EFAULT + case G_FILE_ERROR_FAULT: return EFAULT; +#endif +#ifdef ELOOP + case G_FILE_ERROR_LOOP: return ELOOP; +#endif +#ifdef ENOSPC + case G_FILE_ERROR_NOSPC: return ENOSPC; +#endif +#ifdef ENOMEM + case G_FILE_ERROR_NOMEM: return ENOMEM; +#endif +#ifdef EMFILE + case G_FILE_ERROR_MFILE: return EMFILE; +#endif +#ifdef ENFILE + case G_FILE_ERROR_NFILE: return ENFILE; +#endif +#ifdef EBADF + case G_FILE_ERROR_BADF: return EBADF; +#endif +#ifdef EINVAL + case G_FILE_ERROR_INVAL: return EINVAL; +#endif +#ifdef EPIPE + case G_FILE_ERROR_PIPE: return EPIPE; +#endif +#ifdef EAGAIN + case G_FILE_ERROR_AGAIN: return EAGAIN; +#endif +#ifdef EINTR + case G_FILE_ERROR_INTR: return EINTR; +#endif +#ifdef EWIO + case G_FILE_ERROR_IO: return EIO; +#endif +#ifdef EPERM + case G_FILE_ERROR_PERM: return EPERM; +#endif + case G_FILE_ERROR_FAILED: return ERROR_INVALID_PARAMETER; + default: + g_assert_not_reached (); + } +} + +static gint +file_compare (gconstpointer a, gconstpointer b) +{ + gchar *astr = *(gchar **) a; + gchar *bstr = *(gchar **) b; + + return strcmp (astr, bstr); +} + +/* scandir using glib */ +static gint +_wapi_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist) +{ + GError *error = NULL; + GDir *dir; + GPtrArray *names; + gint result; + mono_w32file_unix_glob_t glob_buf; + gint flags = 0, i; + + dir = _wapi_g_dir_open (dirname, 0, &error); + if (dir == NULL) { + /* g_dir_open returns ENOENT on directories on which we don't + * have read/x permission */ + gint errnum = get_errno_from_g_file_error (error->code); + g_error_free (error); + if (errnum == ENOENT && + !_wapi_access (dirname, F_OK) && + _wapi_access (dirname, R_OK|X_OK)) { + errnum = EACCES; + } + + errno = errnum; + return -1; + } + + if (IS_PORTABILITY_CASE) { + flags = W32FILE_UNIX_GLOB_IGNORECASE; + } + + result = mono_w32file_unix_glob (dir, pattern, flags, &glob_buf); + if (g_str_has_suffix (pattern, ".*")) { + /* Special-case the patterns ending in '.*', as + * windows also matches entries with no extension with + * this pattern. + * + * TODO: should this be a MONO_IOMAP option? + */ + gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2); + gint result2; + + g_dir_rewind (dir); + result2 = mono_w32file_unix_glob (dir, pattern2, flags | W32FILE_UNIX_GLOB_APPEND | W32FILE_UNIX_GLOB_UNIQUE, &glob_buf); + + g_free (pattern2); + + if (result != 0) { + result = result2; + } + } + + g_dir_close (dir); + if (glob_buf.gl_pathc == 0) { + return(0); + } else if (result != 0) { + return -1; + } + + names = g_ptr_array_new (); + for (i = 0; i < glob_buf.gl_pathc; i++) { + g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i])); + } + + mono_w32file_unix_globfree (&glob_buf); + + result = names->len; + if (result > 0) { + g_ptr_array_sort (names, file_compare); + g_ptr_array_set_size (names, result + 1); + + *namelist = (gchar **) g_ptr_array_free (names, FALSE); + } else { + g_ptr_array_free (names, TRUE); + } + + return result; +} + +static gboolean +_wapi_lock_file_region (gint fd, off_t offset, off_t length) +{ +#if defined(__native_client__) + printf("WARNING: %s: fcntl() not available on Native Client!\n", __func__); + // behave as below -- locks are not available + return TRUE; +#else + struct flock lock_data; + gint ret; + + if (offset < 0 || length < 0) { + SetLastError (ERROR_INVALID_PARAMETER); + return FALSE; + } + + lock_data.l_type = F_WRLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = offset; + lock_data.l_len = length; + + do { + ret = fcntl (fd, F_SETLK, &lock_data); + } while(ret == -1 && errno == EINTR); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret); + + if (ret == -1) { + /* + * if locks are not available (NFS for example), + * ignore the error + */ + if (errno == ENOLCK +#ifdef EOPNOTSUPP + || errno == EOPNOTSUPP +#endif +#ifdef ENOTSUP + || errno == ENOTSUP +#endif + ) { + return TRUE; + } + + SetLastError (ERROR_LOCK_VIOLATION); + return FALSE; + } + + return TRUE; +#endif /* __native_client__ */ +} + +static gboolean +_wapi_unlock_file_region (gint fd, off_t offset, off_t length) +{ +#if defined(__native_client__) + printf("WARNING: %s: fcntl() not available on Native Client!\n", __func__); + return TRUE; +#else + struct flock lock_data; + gint ret; + + lock_data.l_type = F_UNLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = offset; + lock_data.l_len = length; + + do { + ret = fcntl (fd, F_SETLK, &lock_data); + } while(ret == -1 && errno == EINTR); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret); + + if (ret == -1) { + /* + * if locks are not available (NFS for example), + * ignore the error + */ + if (errno == ENOLCK +#ifdef EOPNOTSUPP + || errno == EOPNOTSUPP +#endif +#ifdef ENOTSUP + || errno == ENOTSUP +#endif + ) { + return TRUE; + } + + SetLastError (ERROR_LOCK_VIOLATION); + return FALSE; + } + + return TRUE; +#endif /* __native_client__ */ +} + +static void file_close (gpointer handle, gpointer data); +static void file_details (gpointer data); +static const gchar* file_typename (void); +static gsize file_typesize (void); +static gint file_getfiletype(void); +static gboolean file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread); +static gboolean file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten); +static gboolean file_flush(gpointer handle); +static guint32 file_seek(gpointer handle, gint32 movedistance, + gint32 *highmovedistance, gint method); +static gboolean file_setendoffile(gpointer handle); +static guint32 file_getfilesize(gpointer handle, guint32 *highsize); +static gboolean file_getfiletime(gpointer handle, FILETIME *create_time, + FILETIME *access_time, + FILETIME *write_time); +static gboolean file_setfiletime(gpointer handle, + const FILETIME *create_time, + const FILETIME *access_time, + const FILETIME *write_time); +static guint32 GetDriveTypeFromPath (const gchar *utf8_root_path_name); + +/* File handle is only signalled for overlapped IO */ +static MonoW32HandleOps _wapi_file_ops = { + file_close, /* close */ + NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + file_details, /* details */ + file_typename, /* typename */ + file_typesize, /* typesize */ +}; + +static void console_close (gpointer handle, gpointer data); +static void console_details (gpointer data); +static const gchar* console_typename (void); +static gsize console_typesize (void); +static gint console_getfiletype(void); +static gboolean console_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread); +static gboolean console_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten); + +/* Console is mostly the same as file, except it can block waiting for + * input or output + */ +static MonoW32HandleOps _wapi_console_ops = { + console_close, /* close */ + NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + console_details, /* details */ + console_typename, /* typename */ + console_typesize, /* typesize */ +}; + +static const gchar* find_typename (void); +static gsize find_typesize (void); + +static MonoW32HandleOps _wapi_find_ops = { + NULL, /* close */ + NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + NULL, /* details */ + find_typename, /* typename */ + find_typesize, /* typesize */ +}; + +static void pipe_close (gpointer handle, gpointer data); +static void pipe_details (gpointer data); +static const gchar* pipe_typename (void); +static gsize pipe_typesize (void); +static gint pipe_getfiletype (void); +static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread); +static gboolean pipe_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten); + +/* Pipe handles + */ +static MonoW32HandleOps _wapi_pipe_ops = { + pipe_close, /* close */ + NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + pipe_details, /* details */ + pipe_typename, /* typename */ + pipe_typesize, /* typesize */ +}; + +static const struct { + /* File, console and pipe handles */ + gint (*getfiletype)(void); + + /* File, console and pipe handles */ + gboolean (*readfile)(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread); + gboolean (*writefile)(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten); + gboolean (*flushfile)(gpointer handle); + + /* File handles */ + guint32 (*seek)(gpointer handle, gint32 movedistance, + gint32 *highmovedistance, gint method); + gboolean (*setendoffile)(gpointer handle); + guint32 (*getfilesize)(gpointer handle, guint32 *highsize); + gboolean (*getfiletime)(gpointer handle, FILETIME *create_time, + FILETIME *access_time, + FILETIME *write_time); + gboolean (*setfiletime)(gpointer handle, + const FILETIME *create_time, + const FILETIME *access_time, + const FILETIME *write_time); +} io_ops[MONO_W32HANDLE_COUNT]={ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* file */ + {file_getfiletype, + file_read, file_write, + file_flush, file_seek, + file_setendoffile, + file_getfilesize, + file_getfiletime, + file_setfiletime}, + /* console */ + {console_getfiletype, + console_read, + console_write, + NULL, NULL, NULL, NULL, NULL, NULL}, + /* thread */ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* sem */ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* mutex */ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* event */ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* socket (will need at least read and write) */ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* find */ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* process */ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* pipe */ + {pipe_getfiletype, + pipe_read, + pipe_write, + NULL, NULL, NULL, NULL, NULL, NULL}, +}; + +static gboolean lock_while_writing = FALSE; + +/* Some utility functions. + */ + +/* + * Check if a file is writable by the current user. + * + * This is is a best effort kind of thing. It assumes a reasonable sane set + * of permissions by the underlying OS. + * + * We generally assume that basic unix permission bits are authoritative. Which might not + * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc) + * + * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file. + * + * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent + * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous + * and should not be used with a dynamic runtime. + */ +static gboolean +is_file_writable (struct stat *st, const gchar *path) +{ +#if __APPLE__ + // OS X Finder "locked" or `ls -lO` "uchg". + // This only covers one of several cases where an OS X file could be unwritable through special flags. + if (st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE)) + return 0; +#endif + + /* Is it globally writable? */ + if (st->st_mode & S_IWOTH) + return 1; + + /* Am I the owner? */ + if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR)) + return 1; + + /* Am I in the same group? */ + if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP)) + return 1; + + /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid + * but it's the only sane option we have on unix. + */ + return access (path, W_OK) == 0; +} + + +static guint32 _wapi_stat_to_file_attributes (const gchar *pathname, + struct stat *buf, + struct stat *lbuf) +{ + guint32 attrs = 0; + gchar *filename; + + /* FIXME: this could definitely be better, but there seems to + * be no pattern to the attributes that are set + */ + + /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */ + if (S_ISSOCK (buf->st_mode)) + buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */ + + filename = _wapi_basename (pathname); + + if (S_ISDIR (buf->st_mode)) { + attrs = FILE_ATTRIBUTE_DIRECTORY; + if (!is_file_writable (buf, pathname)) { + attrs |= FILE_ATTRIBUTE_READONLY; + } + if (filename[0] == '.') { + attrs |= FILE_ATTRIBUTE_HIDDEN; + } + } else { + if (!is_file_writable (buf, pathname)) { + attrs = FILE_ATTRIBUTE_READONLY; + + if (filename[0] == '.') { + attrs |= FILE_ATTRIBUTE_HIDDEN; + } + } else if (filename[0] == '.') { + attrs = FILE_ATTRIBUTE_HIDDEN; + } else { + attrs = FILE_ATTRIBUTE_NORMAL; + } + } + + if (lbuf != NULL) { + if (S_ISLNK (lbuf->st_mode)) { + attrs |= FILE_ATTRIBUTE_REPARSE_POINT; + } + } + + g_free (filename); + + return attrs; +} + +static void +_wapi_set_last_error_from_errno (void) +{ + SetLastError (_wapi_get_win32_file_error (errno)); +} + +static void _wapi_set_last_path_error_from_errno (const gchar *dir, + const gchar *path) +{ + if (errno == ENOENT) { + /* Check the path - if it's a missing directory then + * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND + */ + gchar *dirname; + + + if (dir == NULL) { + dirname = _wapi_dirname (path); + } else { + dirname = g_strdup (dir); + } + + if (_wapi_access (dirname, F_OK) == 0) { + SetLastError (ERROR_FILE_NOT_FOUND); + } else { + SetLastError (ERROR_PATH_NOT_FOUND); + } + + g_free (dirname); + } else { + _wapi_set_last_error_from_errno (); + } +} + +/* Handle ops. + */ +static void file_close (gpointer handle, gpointer data) +{ + MonoW32HandleFile *file_handle = (MonoW32HandleFile *)data; + gint fd = file_handle->fd; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing file handle %p [%s]", __func__, handle, + file_handle->filename); + + if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE) + _wapi_unlink (file_handle->filename); + + g_free (file_handle->filename); + + if (file_handle->share_info) + file_share_release (file_handle->share_info); + + close (fd); +} + +static void file_details (gpointer data) +{ + MonoW32HandleFile *file = (MonoW32HandleFile *)data; + + g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u", + file->filename, + file->fileaccess&GENERIC_READ?'R':'.', + file->fileaccess&GENERIC_WRITE?'W':'.', + file->fileaccess&GENERIC_EXECUTE?'X':'.', + file->sharemode&FILE_SHARE_READ?'R':'.', + file->sharemode&FILE_SHARE_WRITE?'W':'.', + file->sharemode&FILE_SHARE_DELETE?'D':'.', + file->attrs); +} + +static const gchar* file_typename (void) +{ + return "File"; +} + +static gsize file_typesize (void) +{ + return sizeof (MonoW32HandleFile); +} + +static gint file_getfiletype(void) +{ + return(FILE_TYPE_DISK); +} + +static gboolean +file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + MonoW32HandleFile *file_handle; + gboolean ok; + gint fd, ret; + MonoThreadInfo *info = mono_thread_info_current (); + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, + (gpointer *)&file_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up file handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + fd = file_handle->fd; + if(bytesread!=NULL) { + *bytesread=0; + } + + if(!(file_handle->fileaccess & GENERIC_READ) && + !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u", + __func__, handle, file_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + do { + ret = read (fd, buffer, numbytes); + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if(ret==-1) { + gint err = errno; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, + handle, strerror(err)); + SetLastError (_wapi_get_win32_file_error (err)); + return(FALSE); + } + + if (bytesread != NULL) { + *bytesread = ret; + } + + return(TRUE); +} + +static gboolean +file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + MonoW32HandleFile *file_handle; + gboolean ok; + gint ret, fd; + off_t current_pos = 0; + MonoThreadInfo *info = mono_thread_info_current (); + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, + (gpointer *)&file_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up file handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + fd = file_handle->fd; + + if(byteswritten!=NULL) { + *byteswritten=0; + } + + if(!(file_handle->fileaccess & GENERIC_WRITE) && + !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + if (lock_while_writing) { + /* Need to lock the region we're about to write to, + * because we only do advisory locking on POSIX + * systems + */ + current_pos = lseek (fd, (off_t)0, SEEK_CUR); + if (current_pos == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__, + handle, strerror (errno)); + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + if (_wapi_lock_file_region (fd, current_pos, + numbytes) == FALSE) { + /* The error has already been set */ + return(FALSE); + } + } + + do { + ret = write (fd, buffer, numbytes); + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if (lock_while_writing) { + _wapi_unlock_file_region (fd, current_pos, numbytes); + } + + if (ret == -1) { + if (errno == EINTR) { + ret = 0; + } else { + _wapi_set_last_error_from_errno (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", + __func__, handle, strerror(errno)); + + return(FALSE); + } + } + if (byteswritten != NULL) { + *byteswritten = ret; + } + return(TRUE); +} + +static gboolean file_flush(gpointer handle) +{ + MonoW32HandleFile *file_handle; + gboolean ok; + gint ret, fd; + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, + (gpointer *)&file_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up file handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + fd = file_handle->fd; + + if(!(file_handle->fileaccess & GENERIC_WRITE) && + !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + ret=fsync(fd); + if (ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fsync of handle %p error: %s", __func__, handle, + strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + return(TRUE); +} + +static guint32 file_seek(gpointer handle, gint32 movedistance, + gint32 *highmovedistance, gint method) +{ + MonoW32HandleFile *file_handle; + gboolean ok; + gint64 offset, newpos; + gint whence, fd; + guint32 ret; + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, + (gpointer *)&file_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up file handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(INVALID_SET_FILE_POINTER); + } + + fd = file_handle->fd; + + if(!(file_handle->fileaccess & GENERIC_READ) && + !(file_handle->fileaccess & GENERIC_WRITE) && + !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(INVALID_SET_FILE_POINTER); + } + + switch(method) { + case FILE_BEGIN: + whence=SEEK_SET; + break; + case FILE_CURRENT: + whence=SEEK_CUR; + break; + case FILE_END: + whence=SEEK_END; + break; + default: + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: invalid seek type %d", __func__, method); + + SetLastError (ERROR_INVALID_PARAMETER); + return(INVALID_SET_FILE_POINTER); + } + +#ifdef HAVE_LARGE_FILE_SUPPORT + if(highmovedistance==NULL) { + offset=movedistance; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld (low %d)", __func__, + offset, movedistance); + } else { + offset=((gint64) *highmovedistance << 32) | (guint32)movedistance; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance); + } +#else + offset=movedistance; +#endif + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: moving handle %p by %lld bytes from %d", __func__, + handle, (long long)offset, whence); + +#ifdef PLATFORM_ANDROID + /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */ + newpos=lseek64(fd, offset, whence); +#else + newpos=lseek(fd, offset, whence); +#endif + if(newpos==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek on handle %p returned error %s", + __func__, handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(INVALID_SET_FILE_POINTER); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek returns %lld", __func__, newpos); + +#ifdef HAVE_LARGE_FILE_SUPPORT + ret=newpos & 0xFFFFFFFF; + if(highmovedistance!=NULL) { + *highmovedistance=newpos>>32; + } +#else + ret=newpos; + if(highmovedistance!=NULL) { + /* Accurate, but potentially dodgy :-) */ + *highmovedistance=0; + } +#endif + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: move of handle %p returning %d/%d", __func__, + handle, ret, highmovedistance==NULL?0:*highmovedistance); + + return(ret); +} + +static gboolean file_setendoffile(gpointer handle) +{ + MonoW32HandleFile *file_handle; + gboolean ok; + struct stat statbuf; + off_t pos; + gint ret, fd; + MonoThreadInfo *info = mono_thread_info_current (); + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, + (gpointer *)&file_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up file handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + fd = file_handle->fd; + + if(!(file_handle->fileaccess & GENERIC_WRITE) && + !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + /* Find the current file position, and the file length. If + * the file position is greater than the length, write to + * extend the file with a hole. If the file position is less + * than the length, truncate the file. + */ + + ret=fstat(fd, &statbuf); + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, + handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + pos=lseek(fd, (off_t)0, SEEK_CUR); + if(pos==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__, + handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + +#ifdef FTRUNCATE_DOESNT_EXTEND + off_t size = statbuf.st_size; + /* I haven't bothered to write the configure.ac stuff for this + * because I don't know if any platform needs it. I'm leaving + * this code just in case though + */ + if(pos>size) { + /* Extend the file. Use write() here, because some + * manuals say that ftruncate() behaviour is undefined + * when the file needs extending. The POSIX spec says + * that on XSI-conformant systems it extends, so if + * every system we care about conforms, then we can + * drop this write. + */ + do { + ret = write (fd, "", 1); + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p extend write failed: %s", __func__, handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + /* And put the file position back after the write */ + ret = lseek (fd, pos, SEEK_SET); + if (ret == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p second lseek failed: %s", + __func__, handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + } +#endif + +/* Native Client has no ftruncate function, even in standalone sel_ldr. */ +#ifndef __native_client__ + /* always truncate, because the extend write() adds an extra + * byte to the end of the file + */ + do { + ret=ftruncate(fd, pos); + } + while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ftruncate failed: %s", __func__, + handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } +#endif + + return(TRUE); +} + +static guint32 file_getfilesize(gpointer handle, guint32 *highsize) +{ + MonoW32HandleFile *file_handle; + gboolean ok; + struct stat statbuf; + guint32 size; + gint ret; + gint fd; + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, + (gpointer *)&file_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up file handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(INVALID_FILE_SIZE); + } + fd = file_handle->fd; + + if(!(file_handle->fileaccess & GENERIC_READ) && + !(file_handle->fileaccess & GENERIC_WRITE) && + !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(INVALID_FILE_SIZE); + } + + /* If the file has a size with the low bits 0xFFFFFFFF the + * caller can't tell if this is an error, so clear the error + * value + */ + SetLastError (ERROR_SUCCESS); + + ret = fstat(fd, &statbuf); + if (ret == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, + handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(INVALID_FILE_SIZE); + } + + /* fstat indicates block devices as zero-length, so go a different path */ +#ifdef BLKGETSIZE64 + if (S_ISBLK(statbuf.st_mode)) { + guint64 bigsize; + if (ioctl(fd, BLKGETSIZE64, &bigsize) < 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ioctl BLKGETSIZE64 failed: %s", + __func__, handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(INVALID_FILE_SIZE); + } + + size = bigsize & 0xFFFFFFFF; + if (highsize != NULL) { + *highsize = bigsize>>32; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning block device size %d/%d", + __func__, size, *highsize); + + return(size); + } +#endif + +#ifdef HAVE_LARGE_FILE_SUPPORT + size = statbuf.st_size & 0xFFFFFFFF; + if (highsize != NULL) { + *highsize = statbuf.st_size>>32; + } +#else + if (highsize != NULL) { + /* Accurate, but potentially dodgy :-) */ + *highsize = 0; + } + size = statbuf.st_size; +#endif + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning size %d/%d", __func__, size, *highsize); + + return(size); +} + +static gboolean file_getfiletime(gpointer handle, FILETIME *create_time, + FILETIME *access_time, + FILETIME *write_time) +{ + MonoW32HandleFile *file_handle; + gboolean ok; + struct stat statbuf; + guint64 create_ticks, access_ticks, write_ticks; + gint ret, fd; + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, + (gpointer *)&file_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up file handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + fd = file_handle->fd; + + if(!(file_handle->fileaccess & GENERIC_READ) && + !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u", + __func__, handle, file_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + ret=fstat(fd, &statbuf); + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle, + strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: atime: %ld ctime: %ld mtime: %ld", __func__, + statbuf.st_atime, statbuf.st_ctime, + statbuf.st_mtime); + + /* Try and guess a meaningful create time by using the older + * of atime or ctime + */ + /* The magic constant comes from msdn documentation + * "Converting a time_t Value to a File Time" + */ + if(statbuf.st_atime < statbuf.st_ctime) { + create_ticks=((guint64)statbuf.st_atime*10000000) + + 116444736000000000ULL; + } else { + create_ticks=((guint64)statbuf.st_ctime*10000000) + + 116444736000000000ULL; + } + + access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL; + write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: aticks: %llu cticks: %llu wticks: %llu", __func__, + access_ticks, create_ticks, write_ticks); + + if(create_time!=NULL) { + create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF; + create_time->dwHighDateTime = create_ticks >> 32; + } + + if(access_time!=NULL) { + access_time->dwLowDateTime = access_ticks & 0xFFFFFFFF; + access_time->dwHighDateTime = access_ticks >> 32; + } + + if(write_time!=NULL) { + write_time->dwLowDateTime = write_ticks & 0xFFFFFFFF; + write_time->dwHighDateTime = write_ticks >> 32; + } + + return(TRUE); +} + +static gboolean file_setfiletime(gpointer handle, + const FILETIME *create_time G_GNUC_UNUSED, + const FILETIME *access_time, + const FILETIME *write_time) +{ + MonoW32HandleFile *file_handle; + gboolean ok; + struct utimbuf utbuf; + struct stat statbuf; + guint64 access_ticks, write_ticks; + gint ret, fd; + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, + (gpointer *)&file_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up file handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + fd = file_handle->fd; + + if(!(file_handle->fileaccess & GENERIC_WRITE) && + !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + if(file_handle->filename == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p unknown filename", __func__, handle); + + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + /* Get the current times, so we can put the same times back in + * the event that one of the FileTime structs is NULL + */ + ret=fstat (fd, &statbuf); + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle, + strerror(errno)); + + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + if(access_time!=NULL) { + access_ticks=((guint64)access_time->dwHighDateTime << 32) + + access_time->dwLowDateTime; + /* This is (time_t)0. We can actually go to INT_MIN, + * but this will do for now. + */ + if (access_ticks < 116444736000000000ULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set access time too early", + __func__); + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + if (sizeof (utbuf.actime) == 4 && ((access_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time that is too big for a 32bits time_t", + __func__); + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000; + } else { + utbuf.actime=statbuf.st_atime; + } + + if(write_time!=NULL) { + write_ticks=((guint64)write_time->dwHighDateTime << 32) + + write_time->dwLowDateTime; + /* This is (time_t)0. We can actually go to INT_MIN, + * but this will do for now. + */ + if (write_ticks < 116444736000000000ULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time too early", + __func__); + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + if (sizeof (utbuf.modtime) == 4 && ((write_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time that is too big for a 32bits time_t", + __func__); + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000; + } else { + utbuf.modtime=statbuf.st_mtime; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting handle %p access %ld write %ld", __func__, + handle, utbuf.actime, utbuf.modtime); + + ret = _wapi_utime (file_handle->filename, &utbuf); + if (ret == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p [%s] utime failed: %s", __func__, + handle, file_handle->filename, strerror(errno)); + + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + return(TRUE); +} + +static void console_close (gpointer handle, gpointer data) +{ + MonoW32HandleFile *console_handle = (MonoW32HandleFile *)data; + gint fd = console_handle->fd; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing console handle %p", __func__, handle); + + g_free (console_handle->filename); + + if (fd > 2) { + if (console_handle->share_info) + file_share_release (console_handle->share_info); + close (fd); + } +} + +static void console_details (gpointer data) +{ + file_details (data); +} + +static const gchar* console_typename (void) +{ + return "Console"; +} + +static gsize console_typesize (void) +{ + return sizeof (MonoW32HandleFile); +} + +static gint console_getfiletype(void) +{ + return(FILE_TYPE_CHAR); +} + +static gboolean +console_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + MonoW32HandleFile *console_handle; + gboolean ok; + gint ret, fd; + MonoThreadInfo *info = mono_thread_info_current (); + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE, + (gpointer *)&console_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up console handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + fd = console_handle->fd; + + if(bytesread!=NULL) { + *bytesread=0; + } + + if(!(console_handle->fileaccess & GENERIC_READ) && + !(console_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u", + __func__, handle, console_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + do { + ret=read(fd, buffer, numbytes); + } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); + + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, handle, + strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + if(bytesread!=NULL) { + *bytesread=ret; + } + + return(TRUE); +} + +static gboolean +console_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + MonoW32HandleFile *console_handle; + gboolean ok; + gint ret, fd; + MonoThreadInfo *info = mono_thread_info_current (); + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE, + (gpointer *)&console_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up console handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + fd = console_handle->fd; + + if(byteswritten!=NULL) { + *byteswritten=0; + } + + if(!(console_handle->fileaccess & GENERIC_WRITE) && + !(console_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, console_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + do { + ret = write(fd, buffer, numbytes); + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1) { + if (errno == EINTR) { + ret = 0; + } else { + _wapi_set_last_error_from_errno (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", + __func__, handle, strerror(errno)); + + return(FALSE); + } + } + if(byteswritten!=NULL) { + *byteswritten=ret; + } + + return(TRUE); +} + +static const gchar* find_typename (void) +{ + return "Find"; +} + +static gsize find_typesize (void) +{ + return sizeof (MonoW32HandleFind); +} + +static void pipe_close (gpointer handle, gpointer data) +{ + MonoW32HandleFile *pipe_handle = (MonoW32HandleFile*)data; + gint fd = pipe_handle->fd; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing pipe handle %p fd %d", __func__, handle, fd); + + /* No filename with pipe handles */ + + if (pipe_handle->share_info) + file_share_release (pipe_handle->share_info); + + close (fd); +} + +static void pipe_details (gpointer data) +{ + file_details (data); +} + +static const gchar* pipe_typename (void) +{ + return "Pipe"; +} + +static gsize pipe_typesize (void) +{ + return sizeof (MonoW32HandleFile); +} + +static gint pipe_getfiletype(void) +{ + return(FILE_TYPE_PIPE); +} + +static gboolean +pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + MonoW32HandleFile *pipe_handle; + gboolean ok; + gint ret, fd; + MonoThreadInfo *info = mono_thread_info_current (); + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE, + (gpointer *)&pipe_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up pipe handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + fd = pipe_handle->fd; + + if(bytesread!=NULL) { + *bytesread=0; + } + + if(!(pipe_handle->fileaccess & GENERIC_READ) && + !(pipe_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u", + __func__, handle, pipe_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: reading up to %d bytes from pipe %p", __func__, + numbytes, handle); + + do { + ret=read(fd, buffer, numbytes); + } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1) { + if (errno == EINTR) { + ret = 0; + } else { + _wapi_set_last_error_from_errno (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, + handle, strerror(errno)); + + return(FALSE); + } + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read %d bytes from pipe %p", __func__, ret, handle); + + if(bytesread!=NULL) { + *bytesread=ret; + } + + return(TRUE); +} + +static gboolean +pipe_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + MonoW32HandleFile *pipe_handle; + gboolean ok; + gint ret, fd; + MonoThreadInfo *info = mono_thread_info_current (); + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE, + (gpointer *)&pipe_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up pipe handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + fd = pipe_handle->fd; + + if(byteswritten!=NULL) { + *byteswritten=0; + } + + if(!(pipe_handle->fileaccess & GENERIC_WRITE) && + !(pipe_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, pipe_handle->fileaccess); + + SetLastError (ERROR_ACCESS_DENIED); + return(FALSE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: writing up to %d bytes to pipe %p", __func__, numbytes, + handle); + + do { + ret = write (fd, buffer, numbytes); + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1) { + if (errno == EINTR) { + ret = 0; + } else { + _wapi_set_last_error_from_errno (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", __func__, + handle, strerror(errno)); + + return(FALSE); + } + } + if(byteswritten!=NULL) { + *byteswritten=ret; + } + + return(TRUE); +} + +static gint convert_flags(guint32 fileaccess, guint32 createmode) +{ + gint flags=0; + + switch(fileaccess) { + case GENERIC_READ: + flags=O_RDONLY; + break; + case GENERIC_WRITE: + flags=O_WRONLY; + break; + case GENERIC_READ|GENERIC_WRITE: + flags=O_RDWR; + break; + default: + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown access type 0x%x", __func__, + fileaccess); + break; + } + + switch(createmode) { + case CREATE_NEW: + flags|=O_CREAT|O_EXCL; + break; + case CREATE_ALWAYS: + flags|=O_CREAT|O_TRUNC; + break; + case OPEN_EXISTING: + break; + case OPEN_ALWAYS: + flags|=O_CREAT; + break; + case TRUNCATE_EXISTING: + flags|=O_TRUNC; + break; + default: + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown create mode 0x%x", __func__, + createmode); + break; + } + + return(flags); +} + +#if 0 /* unused */ +static mode_t convert_perms(guint32 sharemode) +{ + mode_t perms=0600; + + if(sharemode&FILE_SHARE_READ) { + perms|=044; + } + if(sharemode&FILE_SHARE_WRITE) { + perms|=022; + } + + return(perms); +} +#endif + +static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode, + guint32 fileaccess, + FileShare **share_info) +{ + gboolean file_already_shared; + guint32 file_existing_share, file_existing_access; + + file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info); + + if (file_already_shared) { + /* The reference to this share info was incremented + * when we looked it up, so be careful to put it back + * if we conclude we can't use this file. + */ + if (file_existing_share == 0) { + /* Quick and easy, no possibility to share */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess); + + file_share_release (*share_info); + + return(FALSE); + } + + if (((file_existing_share == FILE_SHARE_READ) && + (fileaccess != GENERIC_READ)) || + ((file_existing_share == FILE_SHARE_WRITE) && + (fileaccess != GENERIC_WRITE))) { + /* New access mode doesn't match up */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, fileaccess, file_existing_share); + + file_share_release (*share_info); + + return(FALSE); + } + + if (((file_existing_access & GENERIC_READ) && + !(sharemode & FILE_SHARE_READ)) || + ((file_existing_access & GENERIC_WRITE) && + !(sharemode & FILE_SHARE_WRITE))) { + /* New share mode doesn't match up */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__, sharemode, file_existing_access); + + file_share_release (*share_info); + + return(FALSE); + } + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__); + } + + return(TRUE); +} + + +static gboolean +share_allows_delete (struct stat *statbuf, FileShare **share_info) +{ + gboolean file_already_shared; + guint32 file_existing_share, file_existing_access; + + file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, FILE_SHARE_DELETE, GENERIC_READ, &file_existing_share, &file_existing_access, share_info); + + if (file_already_shared) { + /* The reference to this share info was incremented + * when we looked it up, so be careful to put it back + * if we conclude we can't use this file. + */ + if (file_existing_share == 0) { + /* Quick and easy, no possibility to share */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, (*share_info)->access); + + file_share_release (*share_info); + + return(FALSE); + } + + if (!(file_existing_share & FILE_SHARE_DELETE)) { + /* New access mode doesn't match up */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, (*share_info)->access, file_existing_share); + + file_share_release (*share_info); + + return(FALSE); + } + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__); + } + + return(TRUE); +} + +gpointer +mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs) +{ + MonoW32HandleFile file_handle = {0}; + gpointer handle; + gint flags=convert_flags(fileaccess, createmode); + /*mode_t perms=convert_perms(sharemode);*/ + /* we don't use sharemode, because that relates to sharing of + * the file when the file is open and is already handled by + * other code, perms instead are the on-disk permissions and + * this is a sane default. + */ + mode_t perms=0666; + gchar *filename; + gint fd, ret; + MonoW32HandleType handle_type; + struct stat statbuf; + + if (attrs & FILE_ATTRIBUTE_TEMPORARY) + perms = 0600; + + if (attrs & FILE_ATTRIBUTE_ENCRYPTED){ + SetLastError (ERROR_ENCRYPTION_FAILED); + return INVALID_HANDLE_VALUE; + } + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(INVALID_HANDLE_VALUE); + } + + filename = mono_unicode_to_external (name); + if (filename == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(INVALID_HANDLE_VALUE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening %s with share 0x%x and access 0x%x", __func__, + filename, sharemode, fileaccess); + + fd = _wapi_open (filename, flags, perms); + + /* If we were trying to open a directory with write permissions + * (e.g. O_WRONLY or O_RDWR), this call will fail with + * EISDIR. However, this is a bit bogus because calls to + * manipulate the directory (e.g. mono_w32file_set_times) will still work on + * the directory because they use other API calls + * (e.g. utime()). Hence, if we failed with the EISDIR error, try + * to open the directory again without write permission. + */ + if (fd == -1 && errno == EISDIR) + { + /* Try again but don't try to make it writable */ + fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms); + } + + if (fd == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s: %s", __func__, filename, + strerror(errno)); + _wapi_set_last_path_error_from_errno (NULL, filename); + g_free (filename); + + return(INVALID_HANDLE_VALUE); + } + + if (fd >= mono_w32handle_fd_reserve) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__); + + SetLastError (ERROR_TOO_MANY_OPEN_FILES); + + close (fd); + g_free (filename); + + return(INVALID_HANDLE_VALUE); + } + + ret = fstat (fd, &statbuf); + if (ret == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fstat error of file %s: %s", __func__, + filename, strerror (errno)); + _wapi_set_last_error_from_errno (); + g_free (filename); + close (fd); + + return(INVALID_HANDLE_VALUE); + } +#ifdef __native_client__ + /* Workaround: Native Client currently returns the same fake inode + * for all files, so do a simple hash on the filename so we don't + * use the same share info for each file. + */ + statbuf.st_ino = g_str_hash(filename); +#endif + + if (share_allows_open (&statbuf, sharemode, fileaccess, + &file_handle.share_info) == FALSE) { + SetLastError (ERROR_SHARING_VIOLATION); + g_free (filename); + close (fd); + + return (INVALID_HANDLE_VALUE); + } + if (file_handle.share_info == NULL) { + /* No space, so no more files can be opened */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No space in the share table", __func__); + + SetLastError (ERROR_TOO_MANY_OPEN_FILES); + close (fd); + g_free (filename); + + return(INVALID_HANDLE_VALUE); + } + + file_handle.filename = filename; + + file_handle.fd = fd; + file_handle.fileaccess=fileaccess; + file_handle.sharemode=sharemode; + file_handle.attrs=attrs; + +#ifdef HAVE_POSIX_FADVISE + if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) + posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL); + if (attrs & FILE_FLAG_RANDOM_ACCESS) + posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM); +#endif + +#ifdef F_RDAHEAD + if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) + fcntl(fd, F_RDAHEAD, 1); +#endif + +#ifndef S_ISFIFO +#define S_ISFIFO(m) ((m & S_IFIFO) != 0) +#endif + if (S_ISFIFO (statbuf.st_mode)) { + handle_type = MONO_W32HANDLE_PIPE; + /* maintain invariant that pipes have no filename */ + file_handle.filename = NULL; + g_free (filename); + filename = NULL; + } else if (S_ISCHR (statbuf.st_mode)) { + handle_type = MONO_W32HANDLE_CONSOLE; + } else { + handle_type = MONO_W32HANDLE_FILE; + } + + handle = mono_w32handle_new_fd (handle_type, fd, &file_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating file handle", __func__); + g_free (filename); + close (fd); + + SetLastError (ERROR_GEN_FAILURE); + return(INVALID_HANDLE_VALUE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle); + + return(handle); +} + +gboolean mono_w32file_delete(const gunichar2 *name) +{ + gchar *filename; + gint retval; + gboolean ret = FALSE; + guint32 attrs; +#if 0 + struct stat statbuf; + FileShare *shareinfo; +#endif + + if(name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + filename=mono_unicode_to_external(name); + if(filename==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + attrs = mono_w32file_get_attributes (name); + if (attrs == INVALID_FILE_ATTRIBUTES) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file attributes error", __func__); + /* Error set by mono_w32file_get_attributes() */ + g_free (filename); + return(FALSE); + } + +#if 0 + /* Check to make sure sharing allows us to open the file for + * writing. See bug 323389. + * + * Do the checks that don't need an open file descriptor, for + * simplicity's sake. If we really have to do the full checks + * then we can implement that later. + */ + if (_wapi_stat (filename, &statbuf) < 0) { + _wapi_set_last_path_error_from_errno (NULL, filename); + g_free (filename); + return(FALSE); + } + + if (share_allows_open (&statbuf, 0, GENERIC_WRITE, + &shareinfo) == FALSE) { + SetLastError (ERROR_SHARING_VIOLATION); + g_free (filename); + return FALSE; + } + if (shareinfo) + file_share_release (shareinfo); +#endif + + retval = _wapi_unlink (filename); + + if (retval == -1) { + _wapi_set_last_path_error_from_errno (NULL, filename); + } else { + ret = TRUE; + } + + g_free(filename); + + return(ret); +} + +static gboolean +MoveFile (gunichar2 *name, gunichar2 *dest_name) +{ + gchar *utf8_name, *utf8_dest_name; + gint result, errno_copy; + struct stat stat_src, stat_dest; + gboolean ret = FALSE; + FileShare *shareinfo; + + if(name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return FALSE; + } + + if(dest_name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + g_free (utf8_name); + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_dest_name = mono_unicode_to_external (dest_name); + if (utf8_dest_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + g_free (utf8_name); + SetLastError (ERROR_INVALID_NAME); + return FALSE; + } + + /* + * In C# land we check for the existence of src, but not for dest. + * We check it here and return the failure if dest exists and is not + * the same file as src. + */ + if (_wapi_stat (utf8_name, &stat_src) < 0) { + if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + g_free (utf8_dest_name); + return FALSE; + } + } + + if (!_wapi_stat (utf8_dest_name, &stat_dest)) { + if (stat_dest.st_dev != stat_src.st_dev || + stat_dest.st_ino != stat_src.st_ino) { + g_free (utf8_name); + g_free (utf8_dest_name); + SetLastError (ERROR_ALREADY_EXISTS); + return FALSE; + } + } + + /* Check to make that we have delete sharing permission. + * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009 + * + * Do the checks that don't need an open file descriptor, for + * simplicity's sake. If we really have to do the full checks + * then we can implement that later. + */ + if (share_allows_delete (&stat_src, &shareinfo) == FALSE) { + SetLastError (ERROR_SHARING_VIOLATION); + return FALSE; + } + if (shareinfo) + file_share_release (shareinfo); + + result = _wapi_rename (utf8_name, utf8_dest_name); + errno_copy = errno; + + if (result == -1) { + switch(errno_copy) { + case EEXIST: + SetLastError (ERROR_ALREADY_EXISTS); + break; + + case EXDEV: + /* Ignore here, it is dealt with below */ + break; + + case ENOENT: + /* We already know src exists. Must be dest that doesn't exist. */ + _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name); + break; + + default: + _wapi_set_last_error_from_errno (); + } + } + + g_free (utf8_name); + g_free (utf8_dest_name); + + if (result != 0 && errno_copy == EXDEV) { + gint32 copy_error; + + if (S_ISDIR (stat_src.st_mode)) { + SetLastError (ERROR_NOT_SAME_DEVICE); + return FALSE; + } + /* Try a copy to the new location, and delete the source */ + if (!mono_w32file_copy (name, dest_name, FALSE, ©_error)) { + /* mono_w32file_copy will set the error */ + return(FALSE); + } + + return(mono_w32file_delete (name)); + } + + if (result == 0) { + ret = TRUE; + } + + return(ret); +} + +static gboolean +write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_errors) +{ + gint remain, n; + gchar *buf, *wbuf; + gint buf_size = st_src->st_blksize; + MonoThreadInfo *info = mono_thread_info_current (); + + buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size); + buf = (gchar *) g_malloc (buf_size); + + for (;;) { + remain = read (src_fd, buf, buf_size); + if (remain < 0) { + if (errno == EINTR && !mono_thread_info_is_interrupt_state (info)) + continue; + + if (report_errors) + _wapi_set_last_error_from_errno (); + + g_free (buf); + return FALSE; + } + if (remain == 0) { + break; + } + + wbuf = buf; + while (remain > 0) { + if ((n = write (dest_fd, wbuf, remain)) < 0) { + if (errno == EINTR && !mono_thread_info_is_interrupt_state (info)) + continue; + + if (report_errors) + _wapi_set_last_error_from_errno (); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write failed.", __func__); + g_free (buf); + return FALSE; + } + + remain -= n; + wbuf += n; + } + } + + g_free (buf); + return TRUE ; +} + +static gboolean +CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists) +{ + gchar *utf8_src, *utf8_dest; + gint src_fd, dest_fd; + struct stat st, dest_st; + struct utimbuf dest_time; + gboolean ret = TRUE; + gint ret_utime; + + if(name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_src = mono_unicode_to_external (name); + if (utf8_src == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of source returned NULL", + __func__); + + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + if(dest_name==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: dest is NULL", __func__); + + g_free (utf8_src); + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_dest = mono_unicode_to_external (dest_name); + if (utf8_dest == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of dest returned NULL", + __func__); + + SetLastError (ERROR_INVALID_PARAMETER); + + g_free (utf8_src); + + return(FALSE); + } + + src_fd = _wapi_open (utf8_src, O_RDONLY, 0); + if (src_fd < 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_src); + + g_free (utf8_src); + g_free (utf8_dest); + + return(FALSE); + } + + if (fstat (src_fd, &st) < 0) { + _wapi_set_last_error_from_errno (); + + g_free (utf8_src); + g_free (utf8_dest); + close (src_fd); + + return(FALSE); + } + + /* Before trying to open/create the dest, we need to report a 'file busy' + * error if src and dest are actually the same file. We do the check here to take + * advantage of the IOMAP capability */ + if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev && + st.st_ino == dest_st.st_ino) { + + g_free (utf8_src); + g_free (utf8_dest); + close (src_fd); + + SetLastError (ERROR_SHARING_VIOLATION); + return (FALSE); + } + + if (fail_if_exists) { + dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode); + } else { + /* FIXME: it kinda sucks that this code path potentially scans + * the directory twice due to the weird SetLastError() + * behavior. */ + dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode); + if (dest_fd < 0) { + /* The file does not exist, try creating it */ + dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode); + } else { + /* Apparently this error is set if we + * overwrite the dest file + */ + SetLastError (ERROR_ALREADY_EXISTS); + } + } + if (dest_fd < 0) { + _wapi_set_last_error_from_errno (); + + g_free (utf8_src); + g_free (utf8_dest); + close (src_fd); + + return(FALSE); + } + + if (!write_file (src_fd, dest_fd, &st, TRUE)) + ret = FALSE; + + close (src_fd); + close (dest_fd); + + dest_time.modtime = st.st_mtime; + dest_time.actime = st.st_atime; + ret_utime = utime (utf8_dest, &dest_time); + if (ret_utime == -1) + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file [%s] utime failed: %s", __func__, utf8_dest, strerror(errno)); + + g_free (utf8_src); + g_free (utf8_dest); + + return ret; +} + +static gchar* +convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name) +{ + gchar *utf8_ret; + + if (arg == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s is NULL", __func__, arg_name); + SetLastError (ERROR_INVALID_NAME); + return NULL; + } + + utf8_ret = mono_unicode_to_external (arg); + if (utf8_ret == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of %s returned NULL", + __func__, arg_name); + SetLastError (ERROR_INVALID_PARAMETER); + return NULL; + } + + return utf8_ret; +} + +static gboolean +ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved) +{ + gint result, backup_fd = -1,replaced_fd = -1; + gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL; + struct stat stBackup; + gboolean ret = FALSE; + + if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName"))) + return FALSE; + if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName"))) + goto replace_cleanup; + if (backupFileName != NULL) { + if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName"))) + goto replace_cleanup; + } + + if (utf8_backupFileName) { + // Open the backup file for read so we can restore the file if an error occurs. + backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0); + result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName); + if (result == -1) + goto replace_cleanup; + } + + result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName); + if (result == -1) { + _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName); + _wapi_rename (utf8_backupFileName, utf8_replacedFileName); + if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) { + replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC, + stBackup.st_mode); + + if (replaced_fd == -1) + goto replace_cleanup; + + write_file (backup_fd, replaced_fd, &stBackup, FALSE); + } + + goto replace_cleanup; + } + + ret = TRUE; + +replace_cleanup: + g_free (utf8_replacedFileName); + g_free (utf8_replacementFileName); + g_free (utf8_backupFileName); + if (backup_fd != -1) + close (backup_fd); + if (replaced_fd != -1) + close (replaced_fd); + return ret; +} + +static mono_mutex_t stdhandle_mutex; + +static gpointer +_wapi_stdhandle_create (gint fd, const gchar *name) +{ + gpointer handle; + gint flags; + MonoW32HandleFile file_handle = {0}; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating standard handle type %s, fd %d", __func__, name, fd); + +#if !defined(__native_client__) + /* Check if fd is valid */ + do { + flags = fcntl(fd, F_GETFL); + } while (flags == -1 && errno == EINTR); + + if (flags == -1) { + /* Invalid fd. Not really much point checking for EBADF + * specifically + */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl error on fd %d: %s", __func__, fd, strerror(errno)); + + SetLastError (_wapi_get_win32_file_error (errno)); + return INVALID_HANDLE_VALUE; + } + + switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) { + case O_RDONLY: + file_handle.fileaccess = GENERIC_READ; + break; + case O_WRONLY: + file_handle.fileaccess = GENERIC_WRITE; + break; + case O_RDWR: + file_handle.fileaccess = GENERIC_READ | GENERIC_WRITE; + break; + default: + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't figure out flags 0x%x", __func__, flags); + file_handle.fileaccess = 0; + break; + } +#else + /* + * fcntl will return -1 in nacl, as there is no real file system API. + * Yet, standard streams are available. + */ + file_handle.fileaccess = (fd == STDIN_FILENO) ? GENERIC_READ : GENERIC_WRITE; +#endif + + file_handle.fd = fd; + file_handle.filename = g_strdup(name); + /* some default security attributes might be needed */ + file_handle.security_attributes = 0; + + /* Apparently input handles can't be written to. (I don't + * know if output or error handles can't be read from.) + */ + if (fd == 0) + file_handle.fileaccess &= ~GENERIC_WRITE; + + file_handle.sharemode = 0; + file_handle.attrs = 0; + + handle = mono_w32handle_new_fd (MONO_W32HANDLE_CONSOLE, fd, &file_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating file handle", __func__); + SetLastError (ERROR_GEN_FAILURE); + return INVALID_HANDLE_VALUE; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle); + + return handle; +} + +enum { + STD_INPUT_HANDLE = -10, + STD_OUTPUT_HANDLE = -11, + STD_ERROR_HANDLE = -12, +}; + +static gpointer +mono_w32file_get_std_handle (gint stdhandle) +{ + MonoW32HandleFile *file_handle; + gpointer handle; + gint fd; + const gchar *name; + gboolean ok; + + switch(stdhandle) { + case STD_INPUT_HANDLE: + fd = 0; + name = ""; + break; + + case STD_OUTPUT_HANDLE: + fd = 1; + name = ""; + break; + + case STD_ERROR_HANDLE: + fd = 2; + name = ""; + break; + + default: + g_assert_not_reached (); + } + + handle = GINT_TO_POINTER (fd); + + mono_os_mutex_lock (&stdhandle_mutex); + + ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE, + (gpointer *)&file_handle); + if (ok == FALSE) { + /* Need to create this console handle */ + handle = _wapi_stdhandle_create (fd, name); + + if (handle == INVALID_HANDLE_VALUE) { + SetLastError (ERROR_NO_MORE_FILES); + goto done; + } + } else { + /* Add a reference to this handle */ + mono_w32handle_ref (handle); + } + + done: + mono_os_mutex_unlock (&stdhandle_mutex); + + return(handle); +} + +gboolean +mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if(io_ops[type].readfile==NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(io_ops[type].readfile (handle, buffer, numbytes, bytesread)); +} + +gboolean +mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if(io_ops[type].writefile==NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten)); +} + +gboolean +mono_w32file_flush (gpointer handle) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if(io_ops[type].flushfile==NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(io_ops[type].flushfile (handle)); +} + +gboolean +mono_w32file_truncate (gpointer handle) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if (io_ops[type].setendoffile == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(io_ops[type].setendoffile (handle)); +} + +guint32 +mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if (io_ops[type].seek == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(INVALID_SET_FILE_POINTER); + } + + return(io_ops[type].seek (handle, movedistance, highmovedistance, + method)); +} + +gint +mono_w32file_get_type(gpointer handle) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if (io_ops[type].getfiletype == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FILE_TYPE_UNKNOWN); + } + + return(io_ops[type].getfiletype ()); +} + +static guint32 +GetFileSize(gpointer handle, guint32 *highsize) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if (io_ops[type].getfilesize == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(INVALID_FILE_SIZE); + } + + return(io_ops[type].getfilesize (handle, highsize)); +} + +gboolean +mono_w32file_get_times(gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if (io_ops[type].getfiletime == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(io_ops[type].getfiletime (handle, create_time, access_time, + write_time)); +} + +gboolean +mono_w32file_set_times(gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time) +{ + MonoW32HandleType type; + + type = mono_w32handle_get_type (handle); + + if (io_ops[type].setfiletime == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + return(io_ops[type].setfiletime (handle, create_time, access_time, + write_time)); +} + +/* A tick is a 100-nanosecond interval. File time epoch is Midnight, + * January 1 1601 GMT + */ + +#define TICKS_PER_MILLISECOND 10000L +#define TICKS_PER_SECOND 10000000L +#define TICKS_PER_MINUTE 600000000L +#define TICKS_PER_HOUR 36000000000LL +#define TICKS_PER_DAY 864000000000LL + +#define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) + +static const guint16 mon_yday[2][13]={ + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, +}; + +gboolean +mono_w32file_filetime_to_systemtime(const FILETIME *file_time, SYSTEMTIME *system_time) +{ + gint64 file_ticks, totaldays, rem, y; + const guint16 *ip; + + if(system_time==NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: system_time NULL", __func__); + + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + file_ticks=((gint64)file_time->dwHighDateTime << 32) + + file_time->dwLowDateTime; + + /* Really compares if file_ticks>=0x8000000000000000 + * (LLONG_MAX+1) but we're working with a signed value for the + * year and day calculation to work later + */ + if(file_ticks<0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file_time too big", __func__); + + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + totaldays=(file_ticks / TICKS_PER_DAY); + rem = file_ticks % TICKS_PER_DAY; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld rem: %lld", __func__, totaldays, rem); + + system_time->wHour=rem/TICKS_PER_HOUR; + rem %= TICKS_PER_HOUR; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem); + + system_time->wMinute = rem / TICKS_PER_MINUTE; + rem %= TICKS_PER_MINUTE; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Minute: %d rem: %lld", __func__, system_time->wMinute, + rem); + + system_time->wSecond = rem / TICKS_PER_SECOND; + rem %= TICKS_PER_SECOND; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Second: %d rem: %lld", __func__, system_time->wSecond, + rem); + + system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Milliseconds: %d", __func__, + system_time->wMilliseconds); + + /* January 1, 1601 was a Monday, according to Emacs calendar */ + system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day of week: %d", __func__, system_time->wDayOfWeek); + + /* This algorithm to find year and month given days from epoch + * from glibc + */ + y=1601; + +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400)) + + while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) { + /* Guess a corrected year, assuming 365 days per year */ + gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld yg: %lld y: %lld", __func__, + totaldays, yg, + y); + g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__, + LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1)); + + /* Adjust days and y to match the guessed year. */ + totaldays -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays); + y = yg; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: y: %lld", __func__, y); + } + + system_time->wYear = y; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Year: %d", __func__, system_time->wYear); + + ip = mon_yday[isleap(y)]; + + for(y=11; totaldays < ip[y]; --y) { + continue; + } + totaldays-=ip[y]; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays); + + system_time->wMonth = y + 1; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Month: %d", __func__, system_time->wMonth); + + system_time->wDay = totaldays + 1; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day: %d", __func__, system_time->wDay); + + return(TRUE); +} + +gpointer +mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data) +{ + MonoW32HandleFind find_handle = {0}; + gpointer handle; + gchar *utf8_pattern = NULL, *dir_part, *entry_part; + gint result; + + if (pattern == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pattern is NULL", __func__); + + SetLastError (ERROR_PATH_NOT_FOUND); + return(INVALID_HANDLE_VALUE); + } + + utf8_pattern = mono_unicode_to_external (pattern); + if (utf8_pattern == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(INVALID_HANDLE_VALUE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for [%s]", __func__, utf8_pattern); + + /* Figure out which bit of the pattern is the directory */ + dir_part = _wapi_dirname (utf8_pattern); + entry_part = _wapi_basename (utf8_pattern); + +#if 0 + /* Don't do this check for now, it breaks if directories + * really do have metachars in their names (see bug 58116). + * FIXME: Figure out a better solution to keep some checks... + */ + if (strchr (dir_part, '*') || strchr (dir_part, '?')) { + SetLastError (ERROR_INVALID_NAME); + g_free (dir_part); + g_free (entry_part); + g_free (utf8_pattern); + return(INVALID_HANDLE_VALUE); + } +#endif + + /* The pattern can specify a directory or a set of files. + * + * The pattern can have wildcard characters ? and *, but only + * in the section after the last directory delimiter. (Return + * ERROR_INVALID_NAME if there are wildcards in earlier path + * sections.) "*" has the usual 0-or-more chars meaning. "?" + * means "match one character", "??" seems to mean "match one + * or two characters", "???" seems to mean "match one, two or + * three characters", etc. Windows will also try and match + * the mangled "short name" of files, so 8 character patterns + * with wildcards will show some surprising results. + * + * All the written documentation I can find says that '?' + * should only match one character, and doesn't mention '??', + * '???' etc. I'm going to assume that the strict behaviour + * (ie '???' means three and only three characters) is the + * correct one, because that lets me use fnmatch(3) rather + * than mess around with regexes. + */ + + find_handle.namelist = NULL; + result = _wapi_io_scandir (dir_part, entry_part, + &find_handle.namelist); + + if (result == 0) { + /* No files, which windows seems to call + * FILE_NOT_FOUND + */ + SetLastError (ERROR_FILE_NOT_FOUND); + g_free (utf8_pattern); + g_free (entry_part); + g_free (dir_part); + return (INVALID_HANDLE_VALUE); + } + + if (result < 0) { + _wapi_set_last_path_error_from_errno (dir_part, NULL); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: scandir error: %s", __func__, g_strerror (errno)); + g_free (utf8_pattern); + g_free (entry_part); + g_free (dir_part); + return (INVALID_HANDLE_VALUE); + } + + g_free (utf8_pattern); + g_free (entry_part); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Got %d matches", __func__, result); + + find_handle.dir_part = dir_part; + find_handle.num = result; + find_handle.count = 0; + + handle = mono_w32handle_new (MONO_W32HANDLE_FIND, &find_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating find handle", __func__); + g_free (dir_part); + g_free (entry_part); + g_free (utf8_pattern); + SetLastError (ERROR_GEN_FAILURE); + + return(INVALID_HANDLE_VALUE); + } + + if (handle != INVALID_HANDLE_VALUE && + !mono_w32file_find_next (handle, find_data)) { + mono_w32file_find_close (handle); + SetLastError (ERROR_NO_MORE_FILES); + handle = INVALID_HANDLE_VALUE; + } + + return (handle); +} + +gboolean +mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data) +{ + MonoW32HandleFind *find_handle; + gboolean ok; + struct stat buf, linkbuf; + gint result; + gchar *filename; + gchar *utf8_filename, *utf8_basename; + gunichar2 *utf16_basename; + time_t create_time; + glong bytes; + gboolean ret = FALSE; + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND, + (gpointer *)&find_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up find handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + mono_w32handle_lock_handle (handle); + +retry: + if (find_handle->count >= find_handle->num) { + SetLastError (ERROR_NO_MORE_FILES); + goto cleanup; + } + + /* stat next match */ + + filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL); + + result = _wapi_stat (filename, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink */ + result = _wapi_lstat (filename, &buf); + } + + if (result != 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: stat failed: %s", __func__, filename); + + g_free (filename); + goto retry; + } + +#ifndef __native_client__ + result = _wapi_lstat (filename, &linkbuf); + if (result != 0) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lstat failed: %s", __func__, filename); + + g_free (filename); + goto retry; + } +#endif + + utf8_filename = mono_utf8_from_external (filename); + if (utf8_filename == NULL) { + /* We couldn't turn this filename into utf8 (eg the + * encoding of the name wasn't convertible), so just + * ignore it. + */ + g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename); + + g_free (filename); + goto retry; + } + g_free (filename); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Found [%s]", __func__, utf8_filename); + + /* fill data block */ + + if (buf.st_mtime < buf.st_ctime) + create_time = buf.st_mtime; + else + create_time = buf.st_ctime; + +#ifdef __native_client__ + find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, NULL); +#else + find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf); +#endif + + time_t_to_filetime (create_time, &find_data->ftCreationTime); + time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime); + time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime); + + if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + find_data->nFileSizeHigh = 0; + find_data->nFileSizeLow = 0; + } else { + find_data->nFileSizeHigh = buf.st_size >> 32; + find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF; + } + + find_data->dwReserved0 = 0; + find_data->dwReserved1 = 0; + + utf8_basename = _wapi_basename (utf8_filename); + utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes, + NULL); + if(utf16_basename==NULL) { + g_free (utf8_basename); + g_free (utf8_filename); + goto retry; + } + ret = TRUE; + + /* utf16 is 2 * utf8 */ + bytes *= 2; + + memset (find_data->cFileName, '\0', (MAX_PATH*2)); + + /* Truncating a utf16 string like this might leave the last + * gchar incomplete + */ + memcpy (find_data->cFileName, utf16_basename, + bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2); + + find_data->cAlternateFileName [0] = 0; /* not used */ + + g_free (utf8_basename); + g_free (utf8_filename); + g_free (utf16_basename); + +cleanup: + mono_w32handle_unlock_handle (handle); + + return(ret); +} + +gboolean +mono_w32file_find_close (gpointer handle) +{ + MonoW32HandleFind *find_handle; + gboolean ok; + + if (handle == NULL) { + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND, + (gpointer *)&find_handle); + if(ok==FALSE) { + g_warning ("%s: error looking up find handle %p", __func__, + handle); + SetLastError (ERROR_INVALID_HANDLE); + return(FALSE); + } + + mono_w32handle_lock_handle (handle); + + g_strfreev (find_handle->namelist); + g_free (find_handle->dir_part); + + mono_w32handle_unlock_handle (handle); + + mono_w32handle_unref (handle); + + return(TRUE); +} + +gboolean +mono_w32file_create_directory (const gunichar2 *name) +{ + gchar *utf8_name; + gint result; + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return FALSE; + } + + result = _wapi_mkdir (utf8_name, 0777); + + if (result == 0) { + g_free (utf8_name); + return TRUE; + } + + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return FALSE; +} + +gboolean +mono_w32file_remove_directory (const gunichar2 *name) +{ + gchar *utf8_name; + gint result; + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return FALSE; + } + + result = _wapi_rmdir (utf8_name); + if (result == -1) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + + return(FALSE); + } + g_free (utf8_name); + + return(TRUE); +} + +guint32 +mono_w32file_get_attributes (const gunichar2 *name) +{ + gchar *utf8_name; + struct stat buf, linkbuf; + gint result; + guint32 ret; + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_PARAMETER); + return (INVALID_FILE_ATTRIBUTES); + } + + result = _wapi_stat (utf8_name, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink... */ + result = _wapi_lstat (utf8_name, &buf); + } + + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return (INVALID_FILE_ATTRIBUTES); + } + +#ifndef __native_client__ + result = _wapi_lstat (utf8_name, &linkbuf); + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return (INVALID_FILE_ATTRIBUTES); + } +#endif + +#ifdef __native_client__ + ret = _wapi_stat_to_file_attributes (utf8_name, &buf, NULL); +#else + ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf); +#endif + + g_free (utf8_name); + + return(ret); +} + +gboolean +mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat) +{ + gchar *utf8_name; + + struct stat buf, linkbuf; + gint result; + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_PARAMETER); + return FALSE; + } + + result = _wapi_stat (utf8_name, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink... */ + result = _wapi_lstat (utf8_name, &buf); + } + + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return FALSE; + } + + result = _wapi_lstat (utf8_name, &linkbuf); + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return(FALSE); + } + + /* fill stat block */ + + stat->attributes = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf); + stat->creation_time = (((guint64) (buf.st_mtime < buf.st_ctime ? buf.st_mtime : buf.st_ctime)) * 10 * 1000 * 1000) + 116444736000000000ULL; + stat->last_access_time = (((guint64) (buf.st_atime)) * 10 * 1000 * 1000) + 116444736000000000ULL; + stat->last_write_time = (((guint64) (buf.st_mtime)) * 10 * 1000 * 1000) + 116444736000000000ULL; + stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size; + + g_free (utf8_name); + return TRUE; +} + +gboolean +mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs) +{ + /* FIXME: think of something clever to do on unix */ + gchar *utf8_name; + struct stat buf; + gint result; + + /* + * Currently we only handle one *internal* case, with a value that is + * not standard: 0x80000000, which means `set executable bit' + */ + + if (name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + + utf8_name = mono_unicode_to_external (name); + if (utf8_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return FALSE; + } + + result = _wapi_stat (utf8_name, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink... */ + result = _wapi_lstat (utf8_name, &buf); + } + + if (result != 0) { + _wapi_set_last_path_error_from_errno (NULL, utf8_name); + g_free (utf8_name); + return FALSE; + } + + /* Contrary to the documentation, ms allows NORMAL to be + * specified along with other attributes, so dont bother to + * catch that case here. + */ + if (attrs & FILE_ATTRIBUTE_READONLY) { + result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP)); + } else { + result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR); + } + + /* Ignore the other attributes for now */ + + if (attrs & 0x80000000){ + mode_t exec_mask = 0; + + if ((buf.st_mode & S_IRUSR) != 0) + exec_mask |= S_IXUSR; + + if ((buf.st_mode & S_IRGRP) != 0) + exec_mask |= S_IXGRP; + + if ((buf.st_mode & S_IROTH) != 0) + exec_mask |= S_IXOTH; + + result = chmod (utf8_name, buf.st_mode | exec_mask); + } + /* Don't bother to reset executable (might need to change this + * policy) + */ + + g_free (utf8_name); + + return(TRUE); +} + +guint32 +mono_w32file_get_cwd (guint32 length, gunichar2 *buffer) +{ + gunichar2 *utf16_path; + glong count; + gsize bytes; + +#ifdef __native_client__ + gchar *path = g_get_current_dir (); + if (length < strlen(path) + 1 || path == NULL) + return 0; + memcpy (buffer, path, strlen(path) + 1); +#else + if (getcwd ((gchar*)buffer, length) == NULL) { + if (errno == ERANGE) { /*buffer length is not big enough */ + gchar *path = g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/ + if (path == NULL) + return 0; + utf16_path = mono_unicode_from_external (path, &bytes); + g_free (utf16_path); + g_free (path); + return (bytes/2)+1; + } + _wapi_set_last_error_from_errno (); + return 0; + } +#endif + + utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes); + count = (bytes/2)+1; + g_assert (count <= length); /*getcwd must have failed before with ERANGE*/ + + /* Add the terminator */ + memset (buffer, '\0', bytes+2); + memcpy (buffer, utf16_path, bytes); + + g_free (utf16_path); + + return count; +} + +gboolean +mono_w32file_set_cwd (const gunichar2 *path) +{ + gchar *utf8_path; + gboolean result; + + if (path == NULL) { + SetLastError (ERROR_INVALID_PARAMETER); + return(FALSE); + } + + utf8_path = mono_unicode_to_external (path); + if (_wapi_chdir (utf8_path) != 0) { + _wapi_set_last_error_from_errno (); + result = FALSE; + } + else + result = TRUE; + + g_free (utf8_path); + return result; +} + +gboolean +mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size) +{ + MonoW32HandleFile pipe_read_handle = {0}; + MonoW32HandleFile pipe_write_handle = {0}; + gpointer read_handle; + gpointer write_handle; + gint filedes[2]; + gint ret; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating pipe", __func__); + + ret=pipe (filedes); + if(ret==-1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error creating pipe: %s", __func__, + strerror (errno)); + + _wapi_set_last_error_from_errno (); + return(FALSE); + } + + if (filedes[0] >= mono_w32handle_fd_reserve || + filedes[1] >= mono_w32handle_fd_reserve) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__); + + SetLastError (ERROR_TOO_MANY_OPEN_FILES); + + close (filedes[0]); + close (filedes[1]); + + return(FALSE); + } + + /* filedes[0] is open for reading, filedes[1] for writing */ + + pipe_read_handle.fd = filedes [0]; + pipe_read_handle.fileaccess = GENERIC_READ; + read_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[0], + &pipe_read_handle); + if (read_handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating pipe read handle", __func__); + close (filedes[0]); + close (filedes[1]); + SetLastError (ERROR_GEN_FAILURE); + + return(FALSE); + } + + pipe_write_handle.fd = filedes [1]; + pipe_write_handle.fileaccess = GENERIC_WRITE; + write_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[1], + &pipe_write_handle); + if (write_handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating pipe write handle", __func__); + mono_w32handle_unref (read_handle); + + close (filedes[0]); + close (filedes[1]); + SetLastError (ERROR_GEN_FAILURE); + + return(FALSE); + } + + *readpipe = read_handle; + *writepipe = write_handle; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning pipe: read handle %p, write handle %p", + __func__, read_handle, write_handle); + + return(TRUE); +} + +#ifdef HAVE_GETFSSTAT +/* Darwin has getfsstat */ +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + struct statfs *stats; + gint size, n, i; + gunichar2 *dir; + glong length, total = 0; + + n = getfsstat (NULL, 0, MNT_NOWAIT); + if (n == -1) + return 0; + size = n * sizeof (struct statfs); + stats = (struct statfs *) g_malloc (size); + if (stats == NULL) + return 0; + if (getfsstat (stats, size, MNT_NOWAIT) == -1){ + g_free (stats); + return 0; + } + for (i = 0; i < n; i++){ + dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL); + if (total + length < len){ + memcpy (buf + total, dir, sizeof (gunichar2) * length); + buf [total+length] = 0; + } + g_free (dir); + total += length + 1; + } + if (total < len) + buf [total] = 0; + total++; + g_free (stats); + return total; +} +#else +/* In-place octal sequence replacement */ +static void +unescape_octal (gchar *str) +{ + gchar *rptr; + gchar *wptr; + + if (str == NULL) + return; + + rptr = wptr = str; + while (*rptr != '\0') { + if (*rptr == '\\') { + gchar c; + rptr++; + c = (*(rptr++) - '0') << 6; + c += (*(rptr++) - '0') << 3; + c += *(rptr++) - '0'; + *wptr++ = c; + } else if (wptr != rptr) { + *wptr++ = *rptr++; + } else { + rptr++; wptr++; + } + } + *wptr = '\0'; +} +static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf); + +#if __linux__ +#define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512 +#define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512 +#define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64 + +typedef struct +{ + glong total; + guint32 buffer_index; + guint32 mountpoint_index; + guint32 field_number; + guint32 allocated_size; + guint32 fsname_index; + guint32 fstype_index; + gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1]; + gchar *mountpoint_allocated; + gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER]; + gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1]; + gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1]; + ssize_t nbytes; + gchar delimiter; + gboolean check_mount_source; +} LinuxMountInfoParseState; + +static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); +static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); +static void append_to_mountpoint (LinuxMountInfoParseState *state); +static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); + +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + gint fd; + gint32 ret = 0; + LinuxMountInfoParseState state; + gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL; + + memset (buf, 0, len * sizeof (gunichar2)); + fd = open ("/proc/self/mountinfo", O_RDONLY); + if (fd != -1) + parser = GetLogicalDriveStrings_MountInfo; + else { + fd = open ("/proc/mounts", O_RDONLY); + if (fd != -1) + parser = GetLogicalDriveStrings_Mounts; + } + + if (!parser) { + ret = GetLogicalDriveStrings_Mtab (len, buf); + goto done_and_out; + } + + memset (&state, 0, sizeof (LinuxMountInfoParseState)); + state.field_number = 1; + state.delimiter = ' '; + + while ((state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER)) > 0) { + state.buffer_index = 0; + + while ((*parser)(len, buf, &state)) { + if (state.buffer [state.buffer_index] == '\n') { + gboolean quit = add_drive_string (len, buf, &state); + state.field_number = 1; + state.buffer_index++; + if (state.mountpoint_allocated) { + g_free (state.mountpoint_allocated); + state.mountpoint_allocated = NULL; + } + if (quit) { + ret = state.total; + goto done_and_out; + } + } + } + }; + ret = state.total; + + done_and_out: + if (fd != -1) + close (fd); + return ret; +} + +static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + gchar *ptr; + + if (state->field_number == 1) + state->check_mount_source = TRUE; + + while (state->buffer_index < (guint32)state->nbytes) { + if (state->buffer [state->buffer_index] == state->delimiter) { + state->field_number++; + switch (state->field_number) { + case 2: + state->mountpoint_index = 0; + break; + + case 3: + if (state->mountpoint_allocated) + state->mountpoint_allocated [state->mountpoint_index] = 0; + else + state->mountpoint [state->mountpoint_index] = 0; + break; + + default: + ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index); + if (ptr) + state->buffer_index = (ptr - (gchar*)state->buffer) - 1; + else + state->buffer_index = state->nbytes; + return TRUE; + } + state->buffer_index++; + continue; + } else if (state->buffer [state->buffer_index] == '\n') + return TRUE; + + switch (state->field_number) { + case 1: + if (state->check_mount_source) { + if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') { + /* We can ignore the rest, it's a device + * path */ + state->check_mount_source = FALSE; + state->fsname [state->fsname_index++] = '/'; + break; + } + if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fsname [state->fsname_index++] = state->buffer [state->buffer_index]; + } + break; + + case 2: + append_to_mountpoint (state); + break; + + case 3: + if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fstype [state->fstype_index++] = state->buffer [state->buffer_index]; + break; + } + + state->buffer_index++; + } + + return FALSE; +} + +static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + while (state->buffer_index < (guint32)state->nbytes) { + if (state->buffer [state->buffer_index] == state->delimiter) { + state->field_number++; + switch (state->field_number) { + case 5: + state->mountpoint_index = 0; + break; + + case 6: + if (state->mountpoint_allocated) + state->mountpoint_allocated [state->mountpoint_index] = 0; + else + state->mountpoint [state->mountpoint_index] = 0; + break; + + case 7: + state->delimiter = '-'; + break; + + case 8: + state->delimiter = ' '; + break; + + case 10: + state->check_mount_source = TRUE; + break; + } + state->buffer_index++; + continue; + } else if (state->buffer [state->buffer_index] == '\n') + return TRUE; + + switch (state->field_number) { + case 5: + append_to_mountpoint (state); + break; + + case 9: + if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fstype [state->fstype_index++] = state->buffer [state->buffer_index]; + break; + + case 10: + if (state->check_mount_source) { + if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') { + /* We can ignore the rest, it's a device + * path */ + state->check_mount_source = FALSE; + state->fsname [state->fsname_index++] = '/'; + break; + } + if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fsname [state->fsname_index++] = state->buffer [state->buffer_index]; + } + break; + } + + state->buffer_index++; + } + + return FALSE; +} + +static void +append_to_mountpoint (LinuxMountInfoParseState *state) +{ + gchar ch = state->buffer [state->buffer_index]; + if (state->mountpoint_allocated) { + if (state->mountpoint_index >= state->allocated_size) { + guint32 newsize = (state->allocated_size << 1) + 1; + gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar)); + + memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index); + g_free (state->mountpoint_allocated); + state->mountpoint_allocated = newbuf; + state->allocated_size = newsize; + } + state->mountpoint_allocated [state->mountpoint_index++] = ch; + } else { + if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) { + state->allocated_size = (state->mountpoint_index << 1) + 1; + state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar)); + memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index); + state->mountpoint_allocated [state->mountpoint_index++] = ch; + } else + state->mountpoint [state->mountpoint_index++] = ch; + } +} + +static gboolean +add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + gboolean quit = FALSE; + gboolean ignore_entry; + + if (state->fsname_index == 1 && state->fsname [0] == '/') + ignore_entry = FALSE; + else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 || + memcmp ("aufs", state->fstype, state->fstype_index) == 0) { + /* Don't ignore overlayfs and aufs - these might be used on Docker + * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */ + ignore_entry = FALSE; + } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) { + ignore_entry = TRUE; + } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) { + /* Ignore GNOME's gvfs */ + if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0) + ignore_entry = TRUE; + else + ignore_entry = FALSE; + } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0) + ignore_entry = FALSE; + else + ignore_entry = TRUE; + + if (!ignore_entry) { + gunichar2 *dir; + glong length; + gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint; + + unescape_octal (mountpoint); + dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL); + if (state->total + length + 1 > len) { + quit = TRUE; + state->total = len * 2; + } else { + length++; + memcpy (buf + state->total, dir, sizeof (gunichar2) * length); + state->total += length; + } + g_free (dir); + } + state->fsname_index = 0; + state->fstype_index = 0; + + return quit; +} +#else +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + return GetLogicalDriveStrings_Mtab (len, buf); +} +#endif +static gint32 +GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf) +{ + FILE *fp; + gunichar2 *ptr, *dir; + glong length, total = 0; + gchar buffer [512]; + gchar **splitted; + + memset (buf, 0, sizeof (gunichar2) * (len + 1)); + buf [0] = '/'; + buf [1] = 0; + buf [2] = 0; + + /* Sigh, mntent and friends don't work well. + * It stops on the first line that doesn't begin with a '/'. + * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */ + fp = fopen ("/etc/mtab", "rt"); + if (fp == NULL) { + fp = fopen ("/etc/mnttab", "rt"); + if (fp == NULL) + return 1; + } + + ptr = buf; + while (fgets (buffer, 512, fp) != NULL) { + if (*buffer != '/') + continue; + + splitted = g_strsplit (buffer, " ", 0); + if (!*splitted || !*(splitted + 1)) { + g_strfreev (splitted); + continue; + } + + unescape_octal (*(splitted + 1)); + dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL); + g_strfreev (splitted); + if (total + length + 1 > len) { + fclose (fp); + g_free (dir); + return len * 2; /* guess */ + } + + memcpy (ptr + total, dir, sizeof (gunichar2) * length); + g_free (dir); + total += length + 1; + } + + fclose (fp); + return total; +/* Commented out, does not work with my mtab!!! - Gonz */ +#ifdef NOTENABLED /* HAVE_MNTENT_H */ +{ + FILE *fp; + struct mntent *mnt; + gunichar2 *ptr, *dir; + glong len, total = 0; + + + fp = setmntent ("/etc/mtab", "rt"); + if (fp == NULL) { + fp = setmntent ("/etc/mnttab", "rt"); + if (fp == NULL) + return; + } + + ptr = buf; + while ((mnt = getmntent (fp)) != NULL) { + g_print ("GOT %s\n", mnt->mnt_dir); + dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL); + if (total + len + 1 > len) { + return len * 2; /* guess */ + } + + memcpy (ptr + total, dir, sizeof (gunichar2) * len); + g_free (dir); + total += len + 1; + } + + endmntent (fp); + return total; +} +#endif +} +#endif + +#if defined(HAVE_STATVFS) || defined(HAVE_STATFS) +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) +{ +#ifdef HAVE_STATVFS + struct statvfs fsstat; +#elif defined(HAVE_STATFS) + struct statfs fsstat; +#endif + gboolean isreadonly; + gchar *utf8_path_name; + gint ret; + unsigned long block_size; + + if (path_name == NULL) { + utf8_path_name = g_strdup (g_get_current_dir()); + if (utf8_path_name == NULL) { + SetLastError (ERROR_DIRECTORY); + return(FALSE); + } + } + else { + utf8_path_name = mono_unicode_to_external (path_name); + if (utf8_path_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + } + + do { +#ifdef HAVE_STATVFS + ret = statvfs (utf8_path_name, &fsstat); + isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY); + block_size = fsstat.f_frsize; +#elif defined(HAVE_STATFS) + ret = statfs (utf8_path_name, &fsstat); +#if defined (MNT_RDONLY) + isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY); +#elif defined (MS_RDONLY) + isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY); +#endif + block_size = fsstat.f_bsize; +#endif + } while(ret == -1 && errno == EINTR); + + g_free(utf8_path_name); + + if (ret == -1) { + _wapi_set_last_error_from_errno (); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: statvfs failed: %s", __func__, strerror (errno)); + return(FALSE); + } + + /* total number of free bytes for non-root */ + if (free_bytes_avail != NULL) { + if (isreadonly) { + *free_bytes_avail = 0; + } + else { + *free_bytes_avail = block_size * (guint64)fsstat.f_bavail; + } + } + + /* total number of bytes available for non-root */ + if (total_number_of_bytes != NULL) { + *total_number_of_bytes = block_size * (guint64)fsstat.f_blocks; + } + + /* total number of bytes available for root */ + if (total_number_of_free_bytes != NULL) { + if (isreadonly) { + *total_number_of_free_bytes = 0; + } + else { + *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree; + } + } + + return(TRUE); +} +#else +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) +{ + if (free_bytes_avail != NULL) { + *free_bytes_avail = (guint64) -1; + } + + if (total_number_of_bytes != NULL) { + *total_number_of_bytes = (guint64) -1; + } + + if (total_number_of_free_bytes != NULL) { + *total_number_of_free_bytes = (guint64) -1; + } + + return(TRUE); +} +#endif + +/* + * General Unix support + */ +typedef struct { + guint32 drive_type; +#if __linux__ + const long fstypeid; +#endif + const gchar* fstype; +} _wapi_drive_type; + +static _wapi_drive_type _wapi_drive_types[] = { +#if PLATFORM_MACOSX + { DRIVE_REMOTE, "afp" }, + { DRIVE_REMOTE, "autofs" }, + { DRIVE_CDROM, "cddafs" }, + { DRIVE_CDROM, "cd9660" }, + { DRIVE_RAMDISK, "devfs" }, + { DRIVE_FIXED, "exfat" }, + { DRIVE_RAMDISK, "fdesc" }, + { DRIVE_REMOTE, "ftp" }, + { DRIVE_FIXED, "hfs" }, + { DRIVE_FIXED, "msdos" }, + { DRIVE_REMOTE, "nfs" }, + { DRIVE_FIXED, "ntfs" }, + { DRIVE_REMOTE, "smbfs" }, + { DRIVE_FIXED, "udf" }, + { DRIVE_REMOTE, "webdav" }, + { DRIVE_UNKNOWN, NULL } +#elif __linux__ + { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"}, + { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"}, + { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"}, + { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"}, + { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"}, + { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" }, + { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"}, + { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"}, + { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"}, + { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"}, + { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"}, + { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"}, + { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"}, + { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"}, + { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"}, + { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"}, + { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"}, + { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"}, + { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"}, + { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"}, + { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"}, + { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"}, + { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"}, + { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"}, + { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"}, + { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"}, + { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"}, + { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"}, + { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"}, + { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"}, + { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"}, + { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"}, + { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"}, + { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"}, + { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"}, + { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"}, + { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"}, + { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"}, + { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"}, + { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"}, + { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"}, + { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"}, + { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"}, + { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"}, + { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"}, + { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"}, + { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"}, + { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"}, + { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"}, + { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"}, + { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"}, + { DRIVE_FIXED, UFS_MAGIC, "ufs"}, + { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"}, + { DRIVE_FIXED, UFS2_MAGIC, "ufs2"}, + { DRIVE_FIXED, UFS_CIGAM, "ufs"}, + { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"}, + { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"}, + { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"}, + { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"}, + { DRIVE_FIXED, V9FS_MAGIC, "9p"}, + { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"}, + { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"}, + { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"}, + { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"}, + { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"}, + { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"}, + { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"}, + { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"}, + { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"}, + { DRIVE_FIXED, OMFS_MAGIC, "omfs"}, + { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"}, + { DRIVE_UNKNOWN, 0, NULL} +#else + { DRIVE_RAMDISK, "ramfs" }, + { DRIVE_RAMDISK, "tmpfs" }, + { DRIVE_RAMDISK, "proc" }, + { DRIVE_RAMDISK, "sysfs" }, + { DRIVE_RAMDISK, "debugfs" }, + { DRIVE_RAMDISK, "devpts" }, + { DRIVE_RAMDISK, "securityfs" }, + { DRIVE_CDROM, "iso9660" }, + { DRIVE_FIXED, "ext2" }, + { DRIVE_FIXED, "ext3" }, + { DRIVE_FIXED, "ext4" }, + { DRIVE_FIXED, "sysv" }, + { DRIVE_FIXED, "reiserfs" }, + { DRIVE_FIXED, "ufs" }, + { DRIVE_FIXED, "vfat" }, + { DRIVE_FIXED, "msdos" }, + { DRIVE_FIXED, "udf" }, + { DRIVE_FIXED, "hfs" }, + { DRIVE_FIXED, "hpfs" }, + { DRIVE_FIXED, "qnx4" }, + { DRIVE_FIXED, "ntfs" }, + { DRIVE_FIXED, "ntfs-3g" }, + { DRIVE_REMOTE, "smbfs" }, + { DRIVE_REMOTE, "fuse" }, + { DRIVE_REMOTE, "nfs" }, + { DRIVE_REMOTE, "nfs4" }, + { DRIVE_REMOTE, "cifs" }, + { DRIVE_REMOTE, "ncpfs" }, + { DRIVE_REMOTE, "coda" }, + { DRIVE_REMOTE, "afs" }, + { DRIVE_UNKNOWN, NULL } +#endif +}; + +#if __linux__ +static guint32 _wapi_get_drive_type(long f_type) +{ + _wapi_drive_type *current; + + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (current->fstypeid == f_type) + return current->drive_type; + current++; + } + + return DRIVE_UNKNOWN; +} +#else +static guint32 _wapi_get_drive_type(const gchar* fstype) +{ + _wapi_drive_type *current; + + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (strcmp (current->fstype, fstype) == 0) + break; + + current++; + } + + return current->drive_type; +} +#endif + +#if defined (PLATFORM_MACOSX) || defined (__linux__) +static guint32 +GetDriveTypeFromPath (const gchar *utf8_root_path_name) +{ + struct statfs buf; + + if (statfs (utf8_root_path_name, &buf) == -1) + return DRIVE_UNKNOWN; +#if PLATFORM_MACOSX + return _wapi_get_drive_type (buf.f_fstypename); +#else + return _wapi_get_drive_type (buf.f_type); +#endif +} +#else +static guint32 +GetDriveTypeFromPath (const gchar *utf8_root_path_name) +{ + guint32 drive_type; + FILE *fp; + gchar buffer [512]; + gchar **splitted; + + fp = fopen ("/etc/mtab", "rt"); + if (fp == NULL) { + fp = fopen ("/etc/mnttab", "rt"); + if (fp == NULL) + return(DRIVE_UNKNOWN); + } + + drive_type = DRIVE_NO_ROOT_DIR; + while (fgets (buffer, 512, fp) != NULL) { + splitted = g_strsplit (buffer, " ", 0); + if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) { + g_strfreev (splitted); + continue; + } + + /* compare given root_path_name with the one from mtab, + if length of utf8_root_path_name is zero it must be the root dir */ + if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 || + (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) { + drive_type = _wapi_get_drive_type (*(splitted + 2)); + /* it is possible this path might be mounted again with + a known type...keep looking */ + if (drive_type != DRIVE_UNKNOWN) { + g_strfreev (splitted); + break; + } + } + + g_strfreev (splitted); + } + + fclose (fp); + return drive_type; +} +#endif + +guint32 +mono_w32file_get_drive_type(const gunichar2 *root_path_name) +{ + gchar *utf8_root_path_name; + guint32 drive_type; + + if (root_path_name == NULL) { + utf8_root_path_name = g_strdup (g_get_current_dir()); + if (utf8_root_path_name == NULL) { + return(DRIVE_NO_ROOT_DIR); + } + } + else { + utf8_root_path_name = mono_unicode_to_external (root_path_name); + if (utf8_root_path_name == NULL) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__); + return(DRIVE_NO_ROOT_DIR); + } + + /* strip trailing slash for compare below */ + if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) { + utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0; + } + } + drive_type = GetDriveTypeFromPath (utf8_root_path_name); + g_free (utf8_root_path_name); + + return (drive_type); +} + +#if defined (PLATFORM_MACOSX) || defined (__linux__) || defined(PLATFORM_BSD) || defined(__native_client__) || defined(__FreeBSD_kernel__) +static gchar* +get_fstypename (gchar *utfpath) +{ +#if defined (PLATFORM_MACOSX) || defined (__linux__) + struct statfs stat; +#if __linux__ + _wapi_drive_type *current; +#endif + if (statfs (utfpath, &stat) == -1) + return NULL; +#if PLATFORM_MACOSX + return g_strdup (stat.f_fstypename); +#else + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (stat.f_type == current->fstypeid) + return g_strdup (current->fstype); + current++; + } + return NULL; +#endif +#else + return NULL; +#endif +} + +/* Linux has struct statfs which has a different layout */ +gboolean +mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize) +{ + gchar *utfpath; + gchar *fstypename; + gboolean status = FALSE; + glong len; + + // We only support getting the file system type + if (fsbuffer == NULL) + return 0; + + utfpath = mono_unicode_to_external (path); + if ((fstypename = get_fstypename (utfpath)) != NULL){ + gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL); + if (ret != NULL && len < fsbuffersize){ + memcpy (fsbuffer, ret, len * sizeof (gunichar2)); + fsbuffer [len] = 0; + status = TRUE; + } + if (ret != NULL) + g_free (ret); + g_free (fstypename); + } + g_free (utfpath); + return status; +} +#endif + +static gboolean +LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high) +{ + MonoW32HandleFile *file_handle; + off_t offset, length; + + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, (gpointer *)&file_handle)) { + g_warning ("%s: error looking up file handle %p", __func__, handle); + SetLastError (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); + SetLastError (ERROR_ACCESS_DENIED); + return FALSE; + } + +#ifdef HAVE_LARGE_FILE_SUPPORT + offset = ((gint64)offset_high << 32) | offset_low; + length = ((gint64)length_high << 32) | length_low; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %lld, length %lld", __func__, handle, offset, length); +#else + if (offset_high > 0 || length_high > 0) { + SetLastError (ERROR_INVALID_PARAMETER); + return FALSE; + } + offset = offset_low; + length = length_low; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %ld, length %ld", __func__, handle, offset, length); +#endif + + return _wapi_lock_file_region (GPOINTER_TO_UINT(handle), offset, length); +} + +static gboolean +UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high) +{ + MonoW32HandleFile *file_handle; + off_t offset, length; + + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, (gpointer *)&file_handle)) { + g_warning ("%s: error looking up file handle %p", __func__, handle); + SetLastError (ERROR_INVALID_HANDLE); + return FALSE; + } + + if (!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); + SetLastError (ERROR_ACCESS_DENIED); + return FALSE; + } + +#ifdef HAVE_LARGE_FILE_SUPPORT + offset = ((gint64)offset_high << 32) | offset_low; + length = ((gint64)length_high << 32) | length_low; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %lld, length %lld", __func__, handle, offset, length); +#else + offset = offset_low; + length = length_low; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %ld, length %ld", __func__, handle, offset, length); +#endif + + return _wapi_unlock_file_region (GPOINTER_TO_UINT(handle), offset, length); +} + +void +mono_w32file_init (void) +{ + mono_os_mutex_init (&stdhandle_mutex); + mono_os_mutex_init (&file_share_mutex); + + mono_w32handle_register_ops (MONO_W32HANDLE_FILE, &_wapi_file_ops); + mono_w32handle_register_ops (MONO_W32HANDLE_CONSOLE, &_wapi_console_ops); + mono_w32handle_register_ops (MONO_W32HANDLE_FIND, &_wapi_find_ops); + mono_w32handle_register_ops (MONO_W32HANDLE_PIPE, &_wapi_pipe_ops); + +/* mono_w32handle_register_capabilities (MONO_W32HANDLE_FILE, */ +/* MONO_W32HANDLE_CAP_WAIT); */ +/* mono_w32handle_register_capabilities (MONO_W32HANDLE_CONSOLE, */ +/* MONO_W32HANDLE_CAP_WAIT); */ + + if (g_getenv ("MONO_STRICT_IO_EMULATION")) + lock_while_writing = TRUE; +} + +void +mono_w32file_cleanup (void) +{ + mono_os_mutex_destroy (&file_share_mutex); + + if (file_share_table) + g_hash_table_destroy (file_share_table); +} + +gboolean +mono_w32file_move (gunichar2 *path, gunichar2 *dest, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = MoveFile (path, dest); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_copy (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = CopyFile (path, dest, !overwrite); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_replace (gunichar2 *destinationFileName, gunichar2 *sourceFileName, gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gint64 +mono_w32file_get_file_size (gpointer handle, gint32 *error) +{ + gint64 length; + guint32 length_hi; + + MONO_ENTER_GC_SAFE; + + length = GetFileSize (handle, &length_hi); + if(length==INVALID_FILE_SIZE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + + return length | ((gint64)length_hi << 32); +} + +gboolean +mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gpointer +mono_w32file_get_console_input (void) +{ + gpointer handle; + + MONO_ENTER_GC_SAFE; + handle = mono_w32file_get_std_handle (STD_INPUT_HANDLE); + MONO_EXIT_GC_SAFE; + + return handle; +} + +gpointer +mono_w32file_get_console_output (void) +{ + gpointer handle; + + MONO_ENTER_GC_SAFE; + handle = mono_w32file_get_std_handle (STD_OUTPUT_HANDLE); + MONO_EXIT_GC_SAFE; + + return handle; +} + +gpointer +mono_w32file_get_console_error (void) +{ + gpointer handle; + + MONO_ENTER_GC_SAFE; + handle = mono_w32file_get_std_handle (STD_ERROR_HANDLE); + MONO_EXIT_GC_SAFE; + + return handle; +} diff --git a/mono/metadata/w32file-win32-internals.h b/mono/metadata/w32file-win32-internals.h new file mode 100644 index 00000000000..0c7c97f93c6 --- /dev/null +++ b/mono/metadata/w32file-win32-internals.h @@ -0,0 +1,15 @@ +/* + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef _MONO_METADATA_W32FILE_WIN32_INTERNALS_H_ +#define _MONO_METADATA_W32FILE_WIN32_INTERNALS_H_ + +#include +#include + +#ifdef HOST_WIN32 +#include "mono/metadata/w32file.h" +#include "mono/metadata/w32file-internals.h" +#endif /* HOST_WIN32 */ +#endif /* _MONO_METADATA_W32FILE_WIN32_INTERNALS_H_ */ diff --git a/mono/metadata/w32file-win32-uwp.c b/mono/metadata/w32file-win32-uwp.c new file mode 100644 index 00000000000..15062f02948 --- /dev/null +++ b/mono/metadata/w32file-win32-uwp.c @@ -0,0 +1,167 @@ +/* + * w32file-win32-uwp.c: UWP w32file support for Mono. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" + +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#include +#include "mono/metadata/w32file-win32-internals.h" + +gboolean +mono_w32file_move (gunichar2 *path, gunichar2 *dest, gint32 *error) +{ + gboolean result = FALSE; + MONO_ENTER_GC_SAFE; + + result = MoveFileEx (path, dest, MOVEFILE_COPY_ALLOWED); + if (result == FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +gboolean +mono_w32file_replace (gunichar2 *destinationFileName, gunichar2 *sourceFileName, + gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error) +{ + gboolean result = FALSE; + MONO_ENTER_GC_SAFE; + + result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL); + if (result == FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +gboolean +mono_w32file_copy (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error) +{ + gboolean result = FALSE; + COPYFILE2_EXTENDED_PARAMETERS copy_param = {0}; + + copy_param.dwSize = sizeof (COPYFILE2_EXTENDED_PARAMETERS); + copy_param.dwCopyFlags = (!overwrite) ? COPY_FILE_FAIL_IF_EXISTS : 0; + + MONO_ENTER_GC_SAFE; + + result = SUCCEEDED (CopyFile2 (path, dest, ©_param)); + if (result == FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +gint64 +mono_w32file_get_file_size (HANDLE handle, gint32 *error) +{ + LARGE_INTEGER length; + + MONO_ENTER_GC_SAFE; + + if (!GetFileSizeEx (handle, &length)) { + *error=GetLastError (); + length.QuadPart = INVALID_FILE_SIZE; + } + + MONO_EXIT_GC_SAFE; + return length.QuadPart; +} + +gboolean +mono_w32file_lock (HANDLE handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result = FALSE; + MONO_ENTER_GC_SAFE; + + result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, + length & 0xFFFFFFFF, length >> 32); + + if (result == FALSE) { + *error = GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +gboolean +mono_w32file_unlock (HANDLE handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result = FALSE; + MONO_ENTER_GC_SAFE; + + result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, + length & 0xFFFFFFFF, length >> 32); + + if (result == FALSE) { + *error = GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +HANDLE +mono_w32file_get_console_output (void) +{ + MonoError mono_error; + mono_error_init (&mono_error); + + g_unsupported_api ("GetStdHandle (STD_OUTPUT_HANDLE)"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_OUTPUT_HANDLE)"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return INVALID_HANDLE_VALUE; +} + +HANDLE +mono_w32file_get_console_input (void) +{ + MonoError mono_error; + mono_error_init (&mono_error); + + g_unsupported_api ("GetStdHandle (STD_INPUT_HANDLE)"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_INPUT_HANDLE)"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return INVALID_HANDLE_VALUE; +} + +HANDLE +mono_w32file_get_console_error (void) +{ + MonoError mono_error; + mono_error_init (&mono_error); + + g_unsupported_api ("GetStdHandle (STD_ERROR_HANDLE)"); + + mono_error_set_not_supported (&mono_error, G_UNSUPPORTED_API, "GetStdHandle (STD_ERROR_HANDLE)"); + mono_error_set_pending_exception (&mono_error); + + SetLastError (ERROR_NOT_SUPPORTED); + + return INVALID_HANDLE_VALUE; +} + +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ + +MONO_EMPTY_SOURCE_FILE (file_io_windows_uwp); +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/mono/metadata/w32file-win32.c b/mono/metadata/w32file-win32.c new file mode 100644 index 00000000000..7a5478cf2e5 --- /dev/null +++ b/mono/metadata/w32file-win32.c @@ -0,0 +1,358 @@ +/* + * w32file-win32.c: Windows File IO internal calls. + * + * Copyright 2016 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include + +#include +#include +#include "mono/metadata/w32file-win32-internals.h" + +void +mono_w32file_init (void) +{ +} + +void +mono_w32file_cleanup (void) +{ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar () +{ + return (gunichar2) ':'; /* colon */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar () +{ + return (gunichar2) '\\'; /* backslash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar () +{ + return (gunichar2) '/'; /* forward slash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_PathSeparator () +{ + return (gunichar2) ';'; /* semicolon */ +} + +void ves_icall_System_IO_MonoIO_DumpHandles (void) +{ + return; +} + +gpointer +mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs) +{ + return CreateFile (name, fileaccess, sharemode, NULL, createmode, attrs, NULL); +} + +gboolean +mono_w32file_delete (const gunichar2 *name) +{ + return DeleteFile (name); +} + +gboolean +mono_w32file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + return ReadFile (handle, buffer, numbytes, bytesread, NULL); +} + +gboolean +mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + return WriteFile (handle, buffer, numbytes, byteswritten, NULL); +} + +gboolean +mono_w32file_flush (gpointer handle) +{ + return FlushFileBuffers (handle); +} + +gboolean +mono_w32file_truncate (gpointer handle) +{ + return SetEndOfFile (handle); +} + +guint32 +mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method) +{ + return SetFilePointer (handle, movedistance, highmovedistance, method); +} + +gint +mono_w32file_get_type (gpointer handle) +{ + return GetFileType (handle); +} + +gboolean +mono_w32file_get_times (gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time) +{ + return GetFileTime (handle, create_time, access_time, write_time); +} + +gboolean +mono_w32file_set_times (gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time) +{ + return SetFileTime (handle, create_time, access_time, write_time); +} + +gboolean +mono_w32file_filetime_to_systemtime (const FILETIME *file_time, SYSTEMTIME *system_time) +{ + return FileTimeToSystemTime (file_time, system_time); +} + +gpointer +mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data) +{ + return FindFirstFile (pattern, find_data); +} + +gboolean +mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data) +{ + return FindNextFile (handle, find_data); +} + +gboolean +mono_w32file_find_close (gpointer handle) +{ + return FindClose (handle); +} + +gboolean +mono_w32file_create_directory (const gunichar2 *name) +{ + return CreateDirectory (name, NULL); +} + +gboolean +mono_w32file_remove_directory (const gunichar2 *name) +{ + return RemoveDirectory (name); +} + +guint32 +mono_w32file_get_attributes (const gunichar2 *name) +{ + return GetFileAttributes (name); +} + +gboolean +mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat) +{ + gboolean result; + WIN32_FILE_ATTRIBUTE_DATA data; + + result = GetFileAttributesEx (name, GetFileExInfoStandard, &data); + 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; + } + + return result; +} + +gboolean +mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs) +{ + return SetFileAttributes (name, attrs); +} + +guint32 +mono_w32file_get_cwd (guint32 length, gunichar2 *buffer) +{ + return GetCurrentDirectory (length, buffer); +} + +gboolean +mono_w32file_set_cwd (const gunichar2 *path) +{ + return SetCurrentDirectory (path); +} + +gboolean +mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size) +{ + SECURITY_ATTRIBUTES attr; + attr.nLength = sizeof(SECURITY_ATTRIBUTES); + attr.bInheritHandle = TRUE; + attr.lpSecurityDescriptor = NULL; + return CreatePipe (readpipe, writepipe, &attr, size); +} + +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; + + result = GetDiskFreeSpaceEx (path_name, wapi_free_bytes_avail, wapi_total_number_of_bytes, wapi_total_number_of_free_bytes); + if (result) { + if (free_bytes_avail) + *free_bytes_avail = wapi_free_bytes_avail->QuadPart; + if (total_number_of_bytes) + *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; + } + + return result; +} + +gboolean +mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize) +{ + return GetVolumeInformation (path, volumename, volumesize, outserial, maxcomp, fsflags, fsbuffer, fsbuffersize); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + +gboolean +mono_w32file_move (gunichar2 *path, gunichar2 *dest, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = MoveFile (path, dest); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_replace (gunichar2 *destinationFileName, gunichar2 *sourceFileName, gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_copy (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = CopyFile (path, dest, !overwrite); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + gboolean result; + + MONO_ENTER_GC_SAFE; + + result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32); + if (!result) + *error = GetLastError (); + + MONO_EXIT_GC_SAFE; + + return result; +} + +HANDLE +mono_w32file_get_console_input (void) +{ + return GetStdHandle (STD_INPUT_HANDLE); +} + +HANDLE +mono_w32file_get_console_output (void) +{ + return GetStdHandle (STD_OUTPUT_HANDLE); +} + +HANDLE +mono_w32file_get_console_error (void) +{ + return GetStdHandle (STD_ERROR_HANDLE); +} + +gint64 +mono_w32file_get_file_size (gpointer handle, gint32 *error) +{ + gint64 length; + guint32 length_hi; + + MONO_ENTER_GC_SAFE; + + length = GetFileSize (handle, &length_hi); + if(length==INVALID_FILE_SIZE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + + return length | ((gint64)length_hi << 32); +} + +guint32 +mono_w32file_get_drive_type (const gunichar2 *root_path_name) +{ + return GetDriveType (root_path_name); +} + +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + return GetLogicalDriveStrings (len, buf); +} + +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ diff --git a/mono/metadata/w32file.c b/mono/metadata/w32file.c new file mode 100644 index 00000000000..8aabbc57537 --- /dev/null +++ b/mono/metadata/w32file.c @@ -0,0 +1,1248 @@ +/* + * w32file.c: File IO internal calls + * + * Author: + * Dick Porter (dick@ximian.com) + * Gonzalo Paniagua Javier (gonzalo@ximian.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) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/* conversion functions */ + +static guint32 convert_mode(MonoFileMode mono_mode) +{ + guint32 mode; + + switch(mono_mode) { + case FileMode_CreateNew: + mode=CREATE_NEW; + break; + case FileMode_Create: + mode=CREATE_ALWAYS; + break; + case FileMode_Open: + mode=OPEN_EXISTING; + break; + case FileMode_OpenOrCreate: + mode=OPEN_ALWAYS; + break; + case FileMode_Truncate: + mode=TRUNCATE_EXISTING; + break; + case FileMode_Append: + mode=OPEN_ALWAYS; + break; + default: + g_warning("System.IO.FileMode has unknown value 0x%x", + mono_mode); + /* Safe fallback */ + mode=OPEN_EXISTING; + } + + return(mode); +} + +static guint32 convert_access(MonoFileAccess mono_access) +{ + guint32 access; + + switch(mono_access) { + case FileAccess_Read: + access=GENERIC_READ; + break; + case FileAccess_Write: + access=GENERIC_WRITE; + break; + case FileAccess_ReadWrite: + access=GENERIC_READ|GENERIC_WRITE; + break; + default: + g_warning("System.IO.FileAccess has unknown value 0x%x", + mono_access); + /* Safe fallback */ + access=GENERIC_READ; + } + + return(access); +} + +static guint32 convert_share(MonoFileShare mono_share) +{ + guint32 share = 0; + + 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); +} + +#if 0 +static guint32 convert_stdhandle(guint32 fd) +{ + guint32 stdhandle; + + switch(fd) { + case 0: + stdhandle=STD_INPUT_HANDLE; + break; + case 1: + stdhandle=STD_OUTPUT_HANDLE; + break; + case 2: + stdhandle=STD_ERROR_HANDLE; + break; + default: + g_warning("unknown standard file descriptor %d", fd); + stdhandle=STD_INPUT_HANDLE; + } + + return(stdhandle); +} +#endif + +static guint32 convert_seekorigin(MonoSeekOrigin origin) +{ + guint32 w32origin; + + switch(origin) { + case SeekOrigin_Begin: + w32origin=FILE_BEGIN; + break; + case SeekOrigin_Current: + w32origin=FILE_CURRENT; + break; + case SeekOrigin_End: + w32origin=FILE_END; + break; + default: + g_warning("System.IO.SeekOrigin has unknown value 0x%x", + origin); + /* Safe fallback */ + w32origin=FILE_CURRENT; + } + + return(w32origin); +} + +static gint64 convert_filetime (const FILETIME *filetime) +{ + return (gint64) ((((guint64) filetime->dwHighDateTime) << 32) + filetime->dwLowDateTime); +} + +/* Managed file attributes have nearly but not quite the same values + * as the w32 equivalents. + */ +static guint32 convert_attrs(MonoFileAttributes attrs) +{ + if(attrs & FileAttributes_Encrypted) { + attrs = (MonoFileAttributes)(attrs | FILE_ATTRIBUTE_ENCRYPTED); + } + + return(attrs); +} + +/* + * On Win32, mono_w32file_get_attributes|_ex () seems to try opening the file, + * which might lead to sharing violation errors, whereas mono_w32file_find_first + * always succeeds. These 2 wrappers resort to mono_w32file_find_first if + * mono_w32file_get_attributes|_ex () has failed. + */ +static guint32 +get_file_attributes (const gunichar2 *path) +{ + guint32 res; + WIN32_FIND_DATA find_data; + HANDLE find_handle; + gint32 error; + + res = mono_w32file_get_attributes (path); + if (res != -1) + return res; + + error = GetLastError (); + + if (error != ERROR_SHARING_VIOLATION) + return res; + + find_handle = mono_w32file_find_first (path, &find_data); + + if (find_handle == INVALID_HANDLE_VALUE) + return res; + + mono_w32file_find_close (find_handle); + + return find_data.dwFileAttributes; +} + +static gboolean +get_file_attributes_ex (const gunichar2 *path, MonoIOStat *stat) +{ + gboolean res; + WIN32_FIND_DATA find_data; + HANDLE find_handle; + gint32 error; + + res = mono_w32file_get_attributes_ex (path, stat); + if (res) + return TRUE; + + error = GetLastError (); + if (error != ERROR_SHARING_VIOLATION) + return FALSE; + + find_handle = mono_w32file_find_first (path, &find_data); + + if (find_handle == INVALID_HANDLE_VALUE) + return FALSE; + + mono_w32file_find_close (find_handle); + + 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; +} + +/* System.IO.MonoIO internal calls */ + +MonoBoolean +ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error) +{ + gboolean ret; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_create_directory (mono_string_chars (path)); + if(ret==FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error) +{ + gboolean ret; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_remove_directory (mono_string_chars (path)); + if(ret==FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +static gchar * +get_search_dir (const gunichar2 *pattern) +{ + gchar *p; + gchar *result; + + p = g_utf16_to_utf8 (pattern, -1, NULL, NULL, NULL); + result = g_path_get_dirname (p); + g_free (p); + return result; +} + +static GPtrArray * +get_filesystem_entries (const gunichar2 *path, + const gunichar2 *path_with_pattern, + gint attrs, gint mask, + gint32 *error) +{ + int i; + WIN32_FIND_DATA data; + HANDLE find_handle; + GPtrArray *names = NULL; + gchar *utf8_path = NULL, *utf8_result, *full_name; + gint32 attributes; + + mask = convert_attrs ((MonoFileAttributes)mask); + attributes = get_file_attributes (path); + if (attributes != -1) { + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + *error = ERROR_INVALID_NAME; + goto fail; + } + } else { + *error = GetLastError (); + goto fail; + } + + find_handle = mono_w32file_find_first (path_with_pattern, &data); + if (find_handle == INVALID_HANDLE_VALUE) { + gint32 find_error = GetLastError (); + + if (find_error == ERROR_FILE_NOT_FOUND || find_error == ERROR_NO_MORE_FILES) { + /* No files, so just return an empty array */ + goto fail; + } + + *error = find_error; + goto fail; + } + + utf8_path = get_search_dir (path_with_pattern); + 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(mono_w32file_find_next (find_handle, &data)); + + if (mono_w32file_find_close (find_handle) == FALSE) { + *error = GetLastError (); + goto fail; + } + + g_free (utf8_path); + return names; +fail: + if (names) { + 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); + return FALSE; +} + + +MonoArray * +ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path, + MonoString *path_with_pattern, + gint attrs, gint mask, + gint32 *ioerror) +{ + MonoError error; + MonoDomain *domain = mono_domain_get (); + MonoArray *result; + int i; + GPtrArray *names; + + *ioerror = ERROR_SUCCESS; + + MONO_ENTER_GC_SAFE; + names = get_filesystem_entries (mono_string_chars (path), mono_string_chars (path_with_pattern), attrs, mask, ioerror); + MONO_EXIT_GC_SAFE; + + if (!names) { + // If there's no array and no error, then return an empty array. + if (*ioerror == ERROR_SUCCESS) { + MonoArray *arr = mono_array_new_checked (domain, mono_defaults.string_class, 0, &error); + mono_error_set_pending_exception (&error); + return arr; + } + return NULL; + } + + result = mono_array_new_checked (domain, mono_defaults.string_class, names->len, &error); + if (mono_error_set_pending_exception (&error)) + goto leave; + for (i = 0; i < names->len; i++) { + mono_array_setref (result, i, mono_string_new (domain, (const char *)g_ptr_array_index (names, i))); + g_free (g_ptr_array_index (names, i)); + } +leave: + g_ptr_array_free (names, TRUE); + 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; +} + +HANDLE +ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, MonoString **file_name, gint32 *file_attr, gint32 *ioerror) +{ + HANDLE hnd; + WIN32_FIND_DATA data; + MonoError error; + + hnd = mono_w32file_find_first (mono_string_chars (path_with_pattern), &data); + + if (hnd == INVALID_HANDLE_VALUE) { + *file_name = NULL; + *file_attr = 0; + *ioerror = GetLastError (); + return hnd; + } + + mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error)); + mono_error_set_pending_exception (&error); + + *file_attr = data.dwFileAttributes; + *ioerror = ERROR_SUCCESS; + + return hnd; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd, MonoString **file_name, gint32 *file_attr, gint32 *ioerror) +{ + MonoBoolean res; + WIN32_FIND_DATA data; + MonoError error; + + res = mono_w32file_find_next (hnd, &data); + + if (res == FALSE) { + *file_name = NULL; + *file_attr = 0; + *ioerror = GetLastError (); + return res; + } + + mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error)); + mono_error_set_pending_exception (&error); + + *file_attr = data.dwFileAttributes; + *ioerror = ERROR_SUCCESS; + + return res; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd) +{ + return mono_w32file_find_close (hnd); +} + +/* FIXME make gc suspendable */ +MonoString * +ves_icall_System_IO_MonoIO_FindFirst (MonoString *path, + MonoString *path_with_pattern, + gint32 *result_attr, gint32 *ioerror, + gpointer *handle) +{ + MonoError error; + WIN32_FIND_DATA data; + HANDLE find_handle; + IncrementalFind *ifh; + MonoString *result; + + *ioerror = ERROR_SUCCESS; + + find_handle = mono_w32file_find_first (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; + + *ioerror = find_error; + return NULL; + } + + 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)) { + MONO_ENTER_GC_SAFE; + mono_w32file_find_close (find_handle); + MONO_EXIT_GC_SAFE; + g_free (ifh); + return NULL; + } + ifh->domain = mono_domain_get (); + *handle = ifh; + + while (incremental_find_check_match (ifh, &data, &result) == 0){ + if (mono_w32file_find_next (find_handle, &data) == FALSE){ + int e = GetLastError (); + if (e != ERROR_NO_MORE_FILES) + *ioerror = e; + return NULL; + } + } + *result_attr = data.dwFileAttributes; + + return result; +} + +/* FIXME make gc suspendable */ +MonoString * +ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *error) +{ + IncrementalFind *ifh = (IncrementalFind *)handle; + WIN32_FIND_DATA data; + MonoString *result; + + *error = ERROR_SUCCESS; + do { + if (mono_w32file_find_next (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 = (IncrementalFind *)handle; + gint32 error; + + MONO_ENTER_GC_SAFE; + if (mono_w32file_find_close (ifh->find_handle) == FALSE){ + error = GetLastError (); + } else + error = ERROR_SUCCESS; + g_free (ifh->utf8_path); + g_free (ifh); + MONO_EXIT_GC_SAFE; + + return error; +} + +MonoString * +ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error) +{ + MonoError error; + MonoString *result; + gunichar2 *buf; + int len, res_len; + + len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/ + buf = g_new (gunichar2, len); + + mono_error_init (&error); + *io_error=ERROR_SUCCESS; + result = NULL; + + res_len = mono_w32file_get_cwd (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 = mono_w32file_get_cwd (res_len, buf) == old_res_len; + } + + if (res_len) { + len = 0; + while (buf [len]) + ++ len; + + result = mono_string_new_utf16_checked (mono_domain_get (), buf, len, &error); + } else { + *io_error=GetLastError (); + } + + g_free (buf); + mono_error_set_pending_exception (&error); + return result; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path, + gint32 *error) +{ + gboolean ret; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_set_cwd (mono_string_chars (path)); + if(ret==FALSE) { + *error=GetLastError (); + } + + return(ret); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, gint32 *error) +{ + *error=ERROR_SUCCESS; + return mono_w32file_move (mono_string_chars (path), mono_string_chars (dest), error); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName, + MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors, + gint32 *error) +{ + gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL; + guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH; + + 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; + + /* FIXME: source and destination file names must not be NULL, but apparently they might be! */ + return mono_w32file_replace (utf16_destinationFileName, utf16_sourceFileName, + utf16_destinationBackupFileName, replaceFlags, error); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest, + MonoBoolean overwrite, gint32 *error) +{ + *error=ERROR_SUCCESS; + return mono_w32file_copy (mono_string_chars (path), mono_string_chars (dest), overwrite, error); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error) +{ + gboolean ret; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_delete (mono_string_chars (path)); + if(ret==FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +gint32 +ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error) +{ + gint32 ret; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + ret=get_file_attributes (mono_string_chars (path)); + + /* + * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32 + * headers is wrong, hence this temporary workaround. + * See + * http://cygwin.com/ml/cygwin/2003-09/msg01771.html + */ + if (ret==-1) { + /* if(ret==INVALID_FILE_ATTRIBUTES) { */ + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs, + gint32 *error) +{ + gboolean ret; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_set_attributes (mono_string_chars (path), + convert_attrs ((MonoFileAttributes)attrs)); + if(ret==FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +gint32 +ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error) +{ + gboolean ret; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_get_type (handle); + if(ret==FILE_TYPE_UNKNOWN) { + /* Not necessarily an error, but the caller will have + * to decide based on the error value. + */ + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, gint32 *error) +{ + gboolean result; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + result = get_file_attributes_ex (mono_string_chars (path), stat); + + if (!result) { + *error=GetLastError (); + memset (stat, 0, sizeof (MonoIOStat)); + } + + MONO_EXIT_GC_SAFE; + return result; +} + +HANDLE +ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, + gint32 access_mode, gint32 share, gint32 options, + gint32 *error) +{ + HANDLE ret; + int attributes, attrs; + gunichar2 *chars; + MONO_ENTER_GC_SAFE; + + chars = mono_string_chars (filename); + *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; + + 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=mono_w32file_create (chars, convert_access ((MonoFileAccess)access_mode), convert_share ((MonoFileShare)share), convert_mode ((MonoFileMode)mode), attributes); + if(ret==INVALID_HANDLE_VALUE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error) +{ + gboolean ret; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + ret=CloseHandle (handle); + if(ret==FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +gint32 +ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest, + gint32 dest_offset, gint32 count, + gint32 *error) +{ + guchar *buffer; + gboolean result; + guint32 n; + + *error=ERROR_SUCCESS; + + MONO_CHECK_ARG_NULL (dest, 0); + + if (dest_offset > mono_array_length (dest) - count) { + mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong.")); + return 0; + } + + buffer = mono_array_addr (dest, guchar, dest_offset); + + MONO_ENTER_GC_SAFE; + result = mono_w32file_read (handle, buffer, count, &n); + MONO_EXIT_GC_SAFE; + + if (!result) { + *error=GetLastError (); + return -1; + } + + return (gint32)n; +} + +gint32 +ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src, + gint32 src_offset, gint32 count, + gint32 *error) +{ + guchar *buffer; + gboolean result; + guint32 n; + + *error=ERROR_SUCCESS; + + MONO_CHECK_ARG_NULL (src, 0); + + if (src_offset > mono_array_length (src) - count) { + mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong.")); + return 0; + } + + buffer = mono_array_addr (src, guchar, src_offset); + MONO_ENTER_GC_SAFE; + result = mono_w32file_write (handle, buffer, count, &n); + MONO_EXIT_GC_SAFE; + + if (!result) { + *error=GetLastError (); + return -1; + } + + return (gint32)n; +} + +gint64 +ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin, + gint32 *error) +{ + gint32 offset_hi; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + offset_hi = offset >> 32; + offset = mono_w32file_seek (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi, + convert_seekorigin ((MonoSeekOrigin)origin)); + + if(offset==INVALID_SET_FILE_POINTER) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return offset | ((gint64)offset_hi << 32); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error) +{ + gboolean ret; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + ret=mono_w32file_flush (handle); + if(ret==FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +gint64 +ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error) +{ + *error=ERROR_SUCCESS; + return mono_w32file_get_file_size (handle, error); +} + +/* FIXME make gc suspendable */ +MonoBoolean +ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length, + gint32 *error) +{ + gint64 offset, offset_set; + gint32 offset_hi; + gint32 length_hi; + gboolean result; + + *error=ERROR_SUCCESS; + + /* save file pointer */ + + offset_hi = 0; + offset = mono_w32file_seek (handle, 0, &offset_hi, FILE_CURRENT); + if(offset==INVALID_SET_FILE_POINTER) { + *error=GetLastError (); + return(FALSE); + } + + /* extend or truncate */ + + length_hi = length >> 32; + offset_set=mono_w32file_seek (handle, length & 0xFFFFFFFF, &length_hi, + FILE_BEGIN); + if(offset_set==INVALID_SET_FILE_POINTER) { + *error=GetLastError (); + return(FALSE); + } + + result = mono_w32file_truncate (handle); + if(result==FALSE) { + *error=GetLastError (); + return(FALSE); + } + + /* restore file pointer */ + + offset_set=mono_w32file_seek (handle, offset & 0xFFFFFFFF, &offset_hi, + FILE_BEGIN); + if(offset_set==INVALID_SET_FILE_POINTER) { + *error=GetLastError (); + return(FALSE); + } + + return result; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time, + gint64 last_access_time, + gint64 last_write_time, gint32 *error) +{ + gboolean ret; + const FILETIME *creation_filetime; + const FILETIME *access_filetime; + const FILETIME *write_filetime; + MONO_ENTER_GC_SAFE; + + *error=ERROR_SUCCESS; + + if (creation_time < 0) + creation_filetime = NULL; + else + creation_filetime = (FILETIME *)&creation_time; + + if (last_access_time < 0) + access_filetime = NULL; + else + access_filetime = (FILETIME *)&last_access_time; + + if (last_write_time < 0) + write_filetime = NULL; + else + write_filetime = (FILETIME *)&last_write_time; + + ret=mono_w32file_set_times (handle, creation_filetime, access_filetime, write_filetime); + if(ret==FALSE) { + *error=GetLastError (); + } + + MONO_EXIT_GC_SAFE; + return(ret); +} + +HANDLE +ves_icall_System_IO_MonoIO_get_ConsoleOutput () +{ + return mono_w32file_get_console_output (); +} + +HANDLE +ves_icall_System_IO_MonoIO_get_ConsoleInput () +{ + return mono_w32file_get_console_input (); +} + +HANDLE +ves_icall_System_IO_MonoIO_get_ConsoleError () +{ + return mono_w32file_get_console_error (); +} + +MonoBoolean +ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, HANDLE *write_handle, gint32 *error) +{ + gboolean ret; + + MONO_ENTER_GC_SAFE; + ret=mono_w32file_create_pipe (read_handle, write_handle, 0); + MONO_EXIT_GC_SAFE; + + if(ret==FALSE) { + *error = GetLastError (); + /* FIXME: throw an exception? */ + return(FALSE); + } + + 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, gint32 *error) +{ + /* This is only used on Windows */ + gboolean ret; + + MONO_ENTER_GC_SAFE; +#ifdef HOST_WIN32 + ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options); +#else + mono_w32handle_ref (source_handle); + *target_handle = source_handle; + ret = TRUE; +#endif + MONO_EXIT_GC_SAFE; + + if(ret==FALSE) { + *error = GetLastError (); + /* FIXME: throw an exception? */ + return(FALSE); + } + + return(TRUE); +} + +#ifndef HOST_WIN32 +gunichar2 +ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar () +{ + return (gunichar2) '/'; /* forward slash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar () +{ + return (gunichar2) '/'; /* forward slash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar () +{ + if (IS_PORTABILITY_SET) + return (gunichar2) '\\'; /* backslash */ + else + return (gunichar2) '/'; /* forward slash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_PathSeparator () +{ + return (gunichar2) ':'; /* colon */ +} +#endif /* !HOST_WIN32 */ + +static const gunichar2 +invalid_path_chars [] = { +#if defined (TARGET_WIN32) + 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */ + 0x003c, /* less than */ + 0x003e, /* greater than */ + 0x007c, /* pipe */ + 0x0008, + 0x0010, + 0x0011, + 0x0012, + 0x0014, + 0x0015, + 0x0016, + 0x0017, + 0x0018, + 0x0019, +#endif + 0x0000 /* null */ +}; + +MonoArray * +ves_icall_System_IO_MonoIO_get_InvalidPathChars () +{ + MonoError error; + MonoArray *chars; + MonoDomain *domain; + int i, n; + + domain = mono_domain_get (); + n = sizeof (invalid_path_chars) / sizeof (gunichar2); + chars = mono_array_new_checked (domain, mono_defaults.char_class, n, &error); + if (mono_error_set_pending_exception (&error)) + return NULL; + + for (i = 0; i < n; ++ i) + mono_array_set (chars, gunichar2, i, invalid_path_chars [i]); + + return chars; +} + +void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position, + gint64 length, gint32 *error) +{ + *error=ERROR_SUCCESS; + mono_w32file_lock (handle, position, length, error); +} + +void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position, + gint64 length, gint32 *error) +{ + *error=ERROR_SUCCESS; + mono_w32file_unlock (handle, position, length, error); +} + +//Support for io-layer free mmap'd files. + +#if defined (TARGET_IOS) || defined (TARGET_ANDROID) + +gint64 +mono_filesize_from_path (MonoString *string) +{ + MonoError error; + struct stat buf; + gint64 res; + char *path = mono_string_to_utf8_checked (string, &error); + mono_error_raise_exception (&error); /* OK to throw, external only without a good alternative */ + + MONO_ENTER_GC_SAFE; + if (stat (path, &buf) == -1) + res = -1; + else + res = (gint64)buf.st_size; + + g_free (path); + + MONO_EXIT_GC_SAFE; + return res; +} + +gint64 +mono_filesize_from_fd (int fd) +{ + struct stat buf; + int res; + + MONO_ENTER_GC_SAFE; + res = fstat (fd, &buf); + MONO_EXIT_GC_SAFE; + + if (res == -1) + return (gint64)-1; + + return (gint64)buf.st_size; +} + +#endif + +#ifndef HOST_WIN32 +void mono_w32handle_dump (void); + +void ves_icall_System_IO_MonoIO_DumpHandles (void) +{ + mono_w32handle_dump (); +} +#endif /* !HOST_WIN32 */ diff --git a/mono/metadata/w32file.h b/mono/metadata/w32file.h new file mode 100644 index 00000000000..05bfe9fc929 --- /dev/null +++ b/mono/metadata/w32file.h @@ -0,0 +1,497 @@ +/* + * w32file.h: File IO internal calls + * + * Authors: + * Dick Porter (dick@ximian.com) + * Dan Lewis (dihlewis@yahoo.co.uk) + * + * (C) 2001 Ximian, Inc. + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#ifndef _MONO_METADATA_W32FILE_H_ +#define _MONO_METADATA_W32FILE_H_ + +#include +#include + +#include "io-layer/io-layer.h" +#include +#include + +G_BEGIN_DECLS + +/* This is a copy of System.IO.FileAccess */ +typedef enum { + FileAccess_Read=0x01, + FileAccess_Write=0x02, + FileAccess_ReadWrite=FileAccess_Read|FileAccess_Write +} MonoFileAccess; + +/* This is a copy of System.IO.FileMode */ +typedef enum { + FileMode_CreateNew=1, + FileMode_Create=2, + FileMode_Open=3, + FileMode_OpenOrCreate=4, + FileMode_Truncate=5, + FileMode_Append=6 +} MonoFileMode; + +/* This is a copy of System.IO.FileShare */ +typedef enum { + FileShare_None=0x0, + FileShare_Read=0x01, + FileShare_Write=0x02, + FileShare_ReadWrite=FileShare_Read|FileShare_Write, + FileShare_Delete=0x04 +} MonoFileShare; + +/* This is a copy of System.IO.FileOptions */ +typedef enum { + FileOptions_None = 0, + FileOptions_Temporary = 1, // Internal. See note in System.IO.FileOptions + FileOptions_Encrypted = 0x4000, + FileOptions_DeleteOnClose = 0x4000000, + FileOptions_SequentialScan = 0x8000000, + FileOptions_RandomAccess = 0x10000000, + FileOptions_Asynchronous = 0x40000000, + FileOptions_WriteThrough = 0x80000000 +} MonoFileOptions; + +/* This is a copy of System.IO.SeekOrigin */ +typedef enum { + SeekOrigin_Begin=0, + SeekOrigin_Current=1, + SeekOrigin_End=2 +} MonoSeekOrigin; + +/* This is a copy of System.IO.MonoIOStat */ +typedef struct _MonoIOStat { + gint32 attributes; + gint64 length; + gint64 creation_time; + gint64 last_access_time; + gint64 last_write_time; +} MonoIOStat; + +/* This is a copy of System.IO.FileAttributes */ +typedef enum { + FileAttributes_ReadOnly=0x00001, + FileAttributes_Hidden=0x00002, + FileAttributes_System=0x00004, + FileAttributes_Directory=0x00010, + FileAttributes_Archive=0x00020, + FileAttributes_Device=0x00040, + FileAttributes_Normal=0x00080, + FileAttributes_Temporary=0x00100, + FileAttributes_SparseFile=0x00200, + FileAttributes_ReparsePoint=0x00400, + FileAttributes_Compressed=0x00800, + FileAttributes_Offline=0x01000, + FileAttributes_NotContentIndexed=0x02000, + FileAttributes_Encrypted=0x04000, + FileAttributes_MonoExecutable= (int) 0x80000000 +} MonoFileAttributes; +/* This is not used anymore +typedef struct _MonoFSAsyncResult { + MonoObject obj; + MonoObject *state; + MonoBoolean completed; + MonoBoolean done; + MonoException *exc; + MonoWaitHandle *wait_handle; + MonoDelegate *async_callback; + MonoBoolean completed_synch; + MonoArray *buffer; + gint offset; + gint count; + gint original_count; + gint bytes_read; + MonoDelegate *real_cb; +} MonoFSAsyncResult; +*/ +/* System.IO.MonoIO */ + +extern MonoBoolean +ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error); + +MonoArray * +ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path, + MonoString *path_with_pattern, + gint mask, gint attrs, + gint32 *error); + +extern gpointer +ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, + MonoString **file_name, + gint32 *file_attr, + gint32 *ioerror); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_FindNextFile (gpointer hnd, + MonoString **file_name, + gint32 *file_attr, + gint32 *ioerror); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_FindCloseFile (gpointer hnd); + +extern MonoString * +ves_icall_System_IO_MonoIO_FindFirst (MonoString *path, + MonoString *path_with_pattern, + gint32 *result_mask, + gint32 *error, + gpointer *handle); +extern MonoString * +ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_mask, gint32 *error); + +extern int +ves_icall_System_IO_MonoIO_FindClose (gpointer handle); + +extern MonoString * +ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest, + MonoBoolean overwrite, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error); + +extern gint32 +ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs, + gint32 *error); + +extern gint32 +ves_icall_System_IO_MonoIO_GetFileType (gpointer handle, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, + gint32 *error); + +extern gpointer +ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, + gint32 access_mode, gint32 share, gint32 options, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_Close (gpointer handle, gint32 *error); + +extern gint32 +ves_icall_System_IO_MonoIO_Read (gpointer handle, MonoArray *dest, + gint32 dest_offset, gint32 count, + gint32 *error); + +extern gint32 +ves_icall_System_IO_MonoIO_Write (gpointer handle, MonoArray *src, + gint32 src_offset, gint32 count, + gint32 *error); + +extern gint64 +ves_icall_System_IO_MonoIO_Seek (gpointer handle, gint64 offset, gint32 origin, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_Flush (gpointer handle, gint32 *error); + +extern gint64 +ves_icall_System_IO_MonoIO_GetLength (gpointer handle, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_SetLength (gpointer handle, gint64 length, + gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_SetFileTime (gpointer handle, gint64 creation_time, + gint64 last_access_time, + gint64 last_write_time, gint32 *error); + +extern gpointer +ves_icall_System_IO_MonoIO_get_ConsoleOutput (void); + +extern gpointer +ves_icall_System_IO_MonoIO_get_ConsoleInput (void); + +extern gpointer +ves_icall_System_IO_MonoIO_get_ConsoleError (void); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_CreatePipe (gpointer *read_handle, gpointer *write_handle, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_DuplicateHandle (gpointer source_process_handle, gpointer source_handle, + gpointer target_process_handle, gpointer *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error); + +extern gunichar2 +ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar (void); + +extern gunichar2 +ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar (void); + +extern gunichar2 +ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar (void); + +extern gunichar2 +ves_icall_System_IO_MonoIO_get_PathSeparator (void); + +extern MonoArray * +ves_icall_System_IO_MonoIO_get_InvalidPathChars (void); + +extern void ves_icall_System_IO_MonoIO_Lock (gpointer handle, gint64 position, + gint64 length, gint32 *error); +extern void ves_icall_System_IO_MonoIO_Unlock (gpointer handle, gint64 position, + gint64 length, gint32 *error); + +extern MonoBoolean +ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName, + MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors, + gint32 *error); + +#if defined (TARGET_IOS) || defined (TARGET_ANDROID) + +MONO_RT_EXTERNAL_ONLY +extern gint64 +mono_filesize_from_path (MonoString *path); + +extern gint64 +mono_filesize_from_fd (int fd); + +#endif + +void +ves_icall_System_IO_MonoIO_DumpHandles (void); + +#if !defined(HOST_WIN32) + +#define GENERIC_READ 0x80000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_ALL 0x10000000 + +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 + +#define CREATE_NEW 1 +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 +#define OPEN_ALWAYS 4 +#define TRUNCATE_EXISTING 5 + +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_ENCRYPTED 0x00000040 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 +#define FILE_ATTRIBUTE_OFFLINE 0x00001000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 +#define FILE_FLAG_OPEN_NO_RECALL 0x00100000 +#define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 +#define FILE_FLAG_POSIX_SEMANTICS 0x01000000 +#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 +#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000 +#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 +#define FILE_FLAG_RANDOM_ACCESS 0x10000000 +#define FILE_FLAG_NO_BUFFERING 0x20000000 +#define FILE_FLAG_OVERLAPPED 0x40000000 +#define FILE_FLAG_WRITE_THROUGH 0x80000000 + +#define REPLACEFILE_WRITE_THROUGH 0x00000001 +#define REPLACEFILE_IGNORE_MERGE_ERRORS 0x00000002 + +#define MAX_PATH 260 + +#define INVALID_SET_FILE_POINTER ((guint32) 0xFFFFFFFF) +#define INVALID_FILE_SIZE ((guint32) 0xFFFFFFFF) +#define INVALID_FILE_ATTRIBUTES ((guint32) 0xFFFFFFFF) + +#define FILE_TYPE_UNKNOWN 0x0000 +#define FILE_TYPE_DISK 0x0001 +#define FILE_TYPE_CHAR 0x0002 +#define FILE_TYPE_PIPE 0x0003 +#define FILE_TYPE_REMOTE 0x8000 + +#define FILE_BEGIN 0 +#define FILE_CURRENT 1 +#define FILE_END 2 + +#define DRIVE_UNKNOWN 0 +#define DRIVE_NO_ROOT_DIR 1 +#define DRIVE_REMOVABLE 2 +#define DRIVE_FIXED 3 +#define DRIVE_REMOTE 4 +#define DRIVE_CDROM 5 +#define DRIVE_RAMDISK 6 + +typedef struct { + guint16 wYear; + guint16 wMonth; + guint16 wDayOfWeek; + guint16 wDay; + guint16 wHour; + guint16 wMinute; + guint16 wSecond; + guint16 wMilliseconds; +} SYSTEMTIME; + +typedef struct { +#if G_BYTE_ORDER == G_BIG_ENDIAN + guint32 dwHighDateTime; + guint32 dwLowDateTime; +#else + guint32 dwLowDateTime; + guint32 dwHighDateTime; +#endif +} FILETIME; + +typedef struct { + guint32 dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + guint32 nFileSizeHigh; + guint32 nFileSizeLow; + guint32 dwReserved0; + guint32 dwReserved1; + gunichar2 cFileName [MAX_PATH]; + gunichar2 cAlternateFileName [14]; +} WIN32_FIND_DATA; + +#endif /* !defined(HOST_WIN32) */ + +void +mono_w32file_init (void); + +void +mono_w32file_cleanup (void); + +gpointer +mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs); + +gboolean +mono_w32file_delete (const gunichar2 *name); + +gboolean +mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread); + +gboolean +mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten); + +gboolean +mono_w32file_flush (gpointer handle); + +gboolean +mono_w32file_truncate (gpointer handle); + +guint32 +mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method); + +gboolean +mono_w32file_move (gunichar2 *path, gunichar2 *dest, gint32 *error); + +gboolean +mono_w32file_copy (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error); + +gboolean +mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error); + +gboolean +mono_w32file_replace (gunichar2 *destinationFileName, gunichar2 *sourceFileName, gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error); + +gboolean +mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error); + +gpointer +mono_w32file_get_console_output (void); + +gpointer +mono_w32file_get_console_error (void); + +gpointer +mono_w32file_get_console_input (void); + +gint64 +mono_w32file_get_file_size (gpointer handle, gint32 *error); + +gint +mono_w32file_get_type (gpointer handle); + +gboolean +mono_w32file_get_times (gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time); + +gboolean +mono_w32file_set_times (gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time); + +gboolean +mono_w32file_filetime_to_systemtime (const FILETIME *file_time, SYSTEMTIME *system_time); + +gpointer +mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data); + +gboolean +mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data); + +gboolean +mono_w32file_find_close (gpointer handle); + +gboolean +mono_w32file_create_directory (const gunichar2 *name); + +gboolean +mono_w32file_remove_directory (const gunichar2 *name); + +guint32 +mono_w32file_get_attributes (const gunichar2 *name); + +gboolean +mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat); + +gboolean +mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs); + +guint32 +mono_w32file_get_cwd (guint32 length, gunichar2 *buffer); + +gboolean +mono_w32file_set_cwd (const gunichar2 *path); + +gboolean +mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size); + +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf); + +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); + +guint32 +mono_w32file_get_drive_type (const gunichar2 *root_path_name); + +gboolean +mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize); + +G_END_DECLS + +#endif /* _MONO_METADATA_W32FILE_H_ */ diff --git a/mono/metadata/w32handle.c b/mono/metadata/w32handle.c index d4fae9dd09e..248bf1b316f 100644 --- a/mono/metadata/w32handle.c +++ b/mono/metadata/w32handle.c @@ -250,7 +250,7 @@ mono_w32handle_unlock_handle (gpointer handle) } /* - * wapi_init: + * mono_w32handle_init: * * Initialize the io-layer. */ diff --git a/mono/metadata/w32mutex-unix.c b/mono/metadata/w32mutex-unix.c index a39f5ca2761..faf8ac6eea9 100644 --- a/mono/metadata/w32mutex-unix.c +++ b/mono/metadata/w32mutex-unix.c @@ -18,6 +18,8 @@ #include "mono/utils/mono-threads.h" #include "mono/metadata/w32handle.h" +#define MAX_PATH 260 + typedef struct { MonoNativeThreadId tid; guint32 recursion; diff --git a/mono/metadata/w32process-unix.c b/mono/metadata/w32process-unix.c index 8be10017ab6..e81b50a1acf 100644 --- a/mono/metadata/w32process-unix.c +++ b/mono/metadata/w32process-unix.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,7 @@ #include #include #include +#include #ifndef MAXPATHLEN #define MAXPATHLEN 242 @@ -1886,9 +1888,9 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, out_fd = GPOINTER_TO_UINT (startup_handles->output); err_fd = GPOINTER_TO_UINT (startup_handles->error); } else { - in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE)); - out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE)); - err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE)); + in_fd = GPOINTER_TO_UINT (mono_w32file_get_console_input ()); + out_fd = GPOINTER_TO_UINT (mono_w32file_get_console_output ()); + err_fd = GPOINTER_TO_UINT (mono_w32file_get_console_error ()); } /* @@ -2961,14 +2963,37 @@ map_pe_file (gunichar2 *filename, gint32 *map_size, void **handle) return(NULL); } - fd = _wapi_open (filename_ext, O_RDONLY, 0); - if (fd == -1) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s: %s", __func__, filename_ext, strerror (errno)); + fd = open (filename_ext, O_RDONLY, 0); + if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) { + gint saved_errno; + gchar *located_filename; - SetLastError (_wapi_get_win32_file_error (errno)); - g_free (filename_ext); + saved_errno = errno; - return(NULL); + located_filename = mono_portability_find_file (filename_ext, TRUE); + if (!located_filename) { + errno = saved_errno; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s (1): %s", __func__, filename_ext, strerror (errno)); + + g_free (filename_ext); + + SetLastError (_wapi_get_win32_file_error (errno)); + return NULL; + } + + fd = open (located_filename, O_RDONLY, 0); + if (fd == -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s (2): %s", __func__, filename_ext, strerror (errno)); + + g_free (filename_ext); + g_free (located_filename); + + SetLastError (_wapi_get_win32_file_error (errno)); + return NULL; + } + + g_free (located_filename); } if (fstat (fd, &statbuf) == -1) { diff --git a/mono/metadata/w32process.c b/mono/metadata/w32process.c index e4a8a755ff2..78a622af653 100644 --- a/mono/metadata/w32process.c +++ b/mono/metadata/w32process.c @@ -4,6 +4,7 @@ #include "w32process.h" #include "w32process-internals.h" #include "w32process-win32-internals.h" +#include "w32file.h" #include "object.h" #include "object-internals.h" #include "class.h" diff --git a/mono/metadata/w32semaphore-unix.c b/mono/metadata/w32semaphore-unix.c index bcecd8cb558..0e88d94abe7 100644 --- a/mono/metadata/w32semaphore-unix.c +++ b/mono/metadata/w32semaphore-unix.c @@ -14,6 +14,8 @@ #include "mono/utils/mono-logger-internals.h" #include "mono/metadata/w32handle.h" +#define MAX_PATH 260 + typedef struct { guint32 val; gint32 max; diff --git a/mono/metadata/w32socket-internals.h b/mono/metadata/w32socket-internals.h index dd4c61129d0..02f5fe10844 100644 --- a/mono/metadata/w32socket-internals.h +++ b/mono/metadata/w32socket-internals.h @@ -33,16 +33,6 @@ typedef struct { gpointer buf; } WSABUF; -typedef struct { - guint32 Internal; - guint32 InternalHigh; - guint32 Offset; - guint32 OffsetHigh; - gpointer hEvent; - gpointer handle1; - gpointer handle2; -} OVERLAPPED; - typedef struct { gpointer Head; guint32 HeadLength; @@ -57,6 +47,16 @@ typedef struct { guint8 Data4[8]; } GUID; +typedef struct { + guint32 Internal; + guint32 InternalHigh; + guint32 Offset; + guint32 OffsetHigh; + gpointer hEvent; + gpointer handle1; + gpointer handle2; +} OVERLAPPED; + typedef BOOL (WINAPI *LPFN_DISCONNECTEX)(SOCKET, OVERLAPPED*, guint32, guint32); typedef BOOL (WINAPI *LPFN_TRANSMITFILE)(SOCKET, HANDLE, guint32, guint32, OVERLAPPED*, TRANSMIT_FILE_BUFFERS*, guint32); diff --git a/mono/metadata/w32socket.c b/mono/metadata/w32socket.c index 9d45ddac921..ba9156eb48b 100644 --- a/mono/metadata/w32socket.c +++ b/mono/metadata/w32socket.c @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/msvc/libmonoruntime.vcxproj b/msvc/libmonoruntime.vcxproj index 6c94d1ff21e..0f4d0703b7b 100644 --- a/msvc/libmonoruntime.vcxproj +++ b/msvc/libmonoruntime.vcxproj @@ -29,7 +29,7 @@ - + @@ -49,7 +49,7 @@ - + @@ -134,9 +134,9 @@ - - - + + + diff --git a/msvc/libmonoruntime.vcxproj.filters b/msvc/libmonoruntime.vcxproj.filters index 8de9920a9ea..bc9a0c35fb6 100644 --- a/msvc/libmonoruntime.vcxproj.filters +++ b/msvc/libmonoruntime.vcxproj.filters @@ -46,7 +46,7 @@ Source Files - + Source Files @@ -250,7 +250,7 @@ Source Files - + Source Files @@ -321,7 +321,7 @@ Header Files - + Header Files @@ -534,10 +534,10 @@ Header Files - + Header Files - + Header Files diff --git a/msvc/pedump.vcxproj b/msvc/pedump.vcxproj index e997a22ae4b..57b84fab14f 100644 --- a/msvc/pedump.vcxproj +++ b/msvc/pedump.vcxproj @@ -212,8 +212,8 @@ - - + + diff --git a/msvc/pedump.vcxproj.filters b/msvc/pedump.vcxproj.filters index c7f55c9b0d1..9f25b8211b5 100644 --- a/msvc/pedump.vcxproj.filters +++ b/msvc/pedump.vcxproj.filters @@ -22,7 +22,7 @@ Source Files - + Source Files @@ -100,7 +100,7 @@ Source Files - + Source Files