2005-12-23 Dick Porter <dick@ximian.com>
[mono.git] / mono / metadata / file-io.c
index 230265ad4b42cd5a5c209e671cf9fdfeb652ccc5..8556e9f5f3f018ddbfc8948f7a485cb1deb3051d 100644 (file)
@@ -3,20 +3,36 @@
  *
  * Author:
  *     Dick Porter (dick@ximian.com)
+ *     Gonzalo Paniagua Javier (gonzalo@ximian.com)
  *
- * (C) 2001 Ximian, Inc.
+ * (C) 2001,2002,2003 Ximian, Inc.
+ * Copyright (c) 2004,2005 Novell, Inc. (http://www.novell.com)
  */
 
 #include <config.h>
 #include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
 
 #include <mono/metadata/object.h>
 #include <mono/io-layer/io-layer.h>
 #include <mono/metadata/file-io.h>
 #include <mono/metadata/exception.h>
 #include <mono/metadata/appdomain.h>
+#include <mono/metadata/marshal.h>
+#include <mono/utils/strenc.h>
 
-#define DEBUG
+#undef DEBUG
+
+/* conversion functions */
 
 static guint32 convert_mode(MonoFileMode mono_mode)
 {
@@ -102,6 +118,7 @@ static guint32 convert_share(MonoFileShare mono_share)
        return(share);
 }
 
+#if 0
 static guint32 convert_stdhandle(guint32 fd)
 {
        guint32 stdhandle;
@@ -123,6 +140,7 @@ static guint32 convert_stdhandle(guint32 fd)
        
        return(stdhandle);
 }
+#endif
 
 static guint32 convert_seekorigin(MonoSeekOrigin origin)
 {
@@ -148,234 +166,947 @@ static guint32 convert_seekorigin(MonoSeekOrigin origin)
        return(w32origin);
 }
 
-/* fd must be one of stdin (value 0), stdout (1) or stderr (2).  These
- * values must be hardcoded in corlib.
- */
-HANDLE ves_icall_System_PAL_OpSys_GetStdHandle(MonoObject *this,
-                                              gint32 fd)
+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, const gunichar2 *name, MonoIOStat *stat)
 {
-       HANDLE handle;
+       int len;
+       
+       stat->attributes = data->dwFileAttributes;
+       stat->creation_time = convert_filetime (&data->ftCreationTime);
+       stat->last_access_time = convert_filetime (&data->ftLastAccessTime);
+       stat->last_write_time = convert_filetime (&data->ftLastWriteTime);
+       stat->length = ((gint64)data->nFileSizeHigh << 32) | data->nFileSizeLow;
+
+       len = 0;
+       while (name [len])
+               ++ len;
+
+       stat->name = mono_string_new_utf16 (mono_domain_get (), name, len);
+}
 
-       if(fd!=0 && fd!=1 && fd!=2) {
-               mono_raise_exception(mono_get_exception_io("Invalid file descriptor"));
+/* 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 |= FILE_ATTRIBUTE_ENCRYPTED;
        }
        
-       handle=GetStdHandle(convert_stdhandle(fd));
+       return(attrs);
+}
+
+/* System.IO.MonoIO internal calls */
+
+MonoBoolean
+ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error)
+{
+       gboolean ret;
+       
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
        
-       return(handle);
+       ret=CreateDirectory (mono_string_chars (path), NULL);
+       if(ret==FALSE) {
+               *error=GetLastError ();
+       }
+       
+       return(ret);
 }
 
-void ves_icall_System_PAL_OpSys_DeleteFile(MonoObject *this, MonoString *path)
+MonoBoolean
+ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error)
 {
-       char *filename;
+       gboolean ret;
        
-       filename=mono_string_to_utf16(path);
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
+       
+       ret=RemoveDirectory (mono_string_chars (path));
+       if(ret==FALSE) {
+               *error=GetLastError ();
+       }
+       
+       return(ret);
+}
+
+static gint
+get_error_from_g_file_error (gint error)
+{
+       switch (error) {
+       case G_FILE_ERROR_ACCES:
+               error = ERROR_ACCESS_DENIED;
+               break;
+       case G_FILE_ERROR_NAMETOOLONG:
+               error = ERROR_FILENAME_EXCED_RANGE;
+               break;
+       case G_FILE_ERROR_NOENT:
+               error = ERROR_FILE_NOT_FOUND;
+               break;
+       case G_FILE_ERROR_NOTDIR:
+               error = ERROR_FILE_NOT_FOUND;
+               break;
+       case G_FILE_ERROR_ROFS:
+               error = ERROR_ACCESS_DENIED;
+               break;
+       case G_FILE_ERROR_TXTBSY:
+               error = ERROR_SHARING_VIOLATION;
+               break;
+       case G_FILE_ERROR_NOSPC:
+               error = ERROR_HANDLE_DISK_FULL;
+               break;
+       case G_FILE_ERROR_NFILE:
+       case G_FILE_ERROR_MFILE:
+               error = ERROR_TOO_MANY_OPEN_FILES;
+               break;
+       case G_FILE_ERROR_BADF:
+               error = ERROR_INVALID_HANDLE;
+               break;
+       case G_FILE_ERROR_INVAL:
+               error = ERROR_INVALID_PARAMETER;
+               break;
+       case G_FILE_ERROR_AGAIN:
+               error = ERROR_SHARING_VIOLATION;
+               break;
+       case G_FILE_ERROR_INTR:
+               error = ERROR_IO_PENDING;
+               break;
+       case G_FILE_ERROR_PERM:
+               error = ERROR_ACCESS_DENIED;
+               break;
+       case G_FILE_ERROR_FAILED:
+               error = ERROR_INVALID_PARAMETER;
+               break;
+       case G_FILE_ERROR_NXIO:
+       case G_FILE_ERROR_NOMEM:
+       case G_FILE_ERROR_NODEV:
+       case G_FILE_ERROR_FAULT:
+       case G_FILE_ERROR_LOOP:
+       case G_FILE_ERROR_PIPE:
+       case G_FILE_ERROR_IO:
+       default:
+               error = ERROR_GEN_FAILURE;
+               break;
+
+       }
+
+       return error;
+}
+
+static gint
+file_compare (gconstpointer a, gconstpointer b)
+{
+       gchar *astr = *(gchar **) a;
+       gchar *bstr = *(gchar **) b;
+
+       return strcmp (astr, bstr);
+}
+
+static gint
+get_file_attributes (const char *filename)
+{
+#ifdef PLATFORM_WIN32
+       gunichar2 *full16;
+       gint result;
+
+       full16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+       if (full16 == NULL) {
+               g_message ("Bad encoding for '%s'\n", filename);
+               return FALSE;
+       }
+
+       result = GetFileAttributes (full16);
+       g_free (full16);
+       return result;
+#else
+       struct stat buf;
+       struct stat linkbuf;
+       int result;
+       int file_attrs;
+
+       result = lstat (filename, &buf);
+       if (result == -1)
+               return FALSE;
+
+       if ((buf.st_mode & S_IFLNK) != 0) {
+               result = stat (filename, &linkbuf);
+               if (result != -1) {
+                       buf = linkbuf;
+               } else {
+                       buf.st_mode |= ~S_IFDIR; /* force it to be returned as regular file */
+               }
+       }
+
+       file_attrs = 0;
+       if ((buf.st_mode & S_IFDIR) != 0)
+               file_attrs |= FILE_ATTRIBUTE_DIRECTORY;
+       else
+               file_attrs |= FILE_ATTRIBUTE_ARCHIVE;
+
+       if ((buf.st_mode & S_IWUSR) == 0)
+               file_attrs |= FILE_ATTRIBUTE_READONLY;
+
+       if (*filename == '.')
+               file_attrs |= FILE_ATTRIBUTE_HIDDEN;
+
+       return file_attrs;
+#endif
+}
+
+static gboolean
+test_file (const char *filename, int attrs, int mask)
+{
+       int file_attr;
+
+       file_attr = get_file_attributes (filename);
+       if (file_attr == FALSE)
+               return FALSE;
+
+       return ((file_attr & mask) == attrs);
+}
+
+/* scandir using glib */
+static gint
+mono_io_scandir (const gchar *dirname, const gchar *pattern, int attrs,
+               int mask, gchar ***namelist)
+{
+       GError *error = NULL;
+       GDir *dir;
+       GPtrArray *names;
+       const gchar *name;
+       gint result;
+       GPatternSpec *patspec;
+       gchar *full_name;
+
+       mask = convert_attrs (mask);
+       *namelist = NULL;
+       dir = 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_error_from_g_file_error (error->code);
+               g_error_free (error);
+               if (errnum == ERROR_FILE_NOT_FOUND && g_file_test (dirname, G_FILE_TEST_IS_DIR))
+                       errnum = ERROR_ACCESS_DENIED;
+
+               SetLastError (errnum);
+               return -1;
+       }
+
+       patspec = g_pattern_spec_new (pattern);
+       names = g_ptr_array_new ();
+       while ((name = g_dir_read_name (dir)) != NULL) {
+               if (!g_pattern_match_string (patspec, name))
+                       continue;
+
+               full_name = g_build_filename (dirname, name, NULL);
+               if (FALSE == test_file (full_name, attrs, mask)) {
+                       g_free (full_name);
+                       continue;
+               }
+
+               g_ptr_array_add (names, full_name);
+       }
        
-       DeleteFile(filename);
+       g_pattern_spec_free (patspec);
+       g_dir_close (dir);
+       result = names->len;
+       if (result > 0) {
+               g_ptr_array_sort (names, file_compare);
+               g_ptr_array_set_size (names, result + 1);
 
-       g_free(filename);
+               *namelist = (gchar **) g_ptr_array_free (names, FALSE);
+       } else {
+               g_ptr_array_free (names, TRUE);
+       }
+
+       return result;
 }
 
-gboolean ves_icall_System_PAL_OpSys_ExistsFile(MonoObject *this, MonoString *path)
+MonoArray *
+ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *_path, MonoString *_pattern,
+                                       gint attrs, gint mask, gint32 *error)
 {
-       return(FALSE);
+       MonoDomain *domain;
+       MonoArray *result;
+       gchar **namelist;
+       gchar *path;
+       gchar *pattern;
+       int i, nnames;
+       int removed;
+       MonoString *str_name;
+#ifndef PLATFORM_WIN32
+       gunichar2 *utf16;
+       gsize nbytes;
+#endif
+
+       MONO_ARCH_SAVE_REGS;
+
+       *error = ERROR_SUCCESS;
+
+       path = mono_string_to_utf8 (_path);
+       pattern = mono_string_to_utf8 (_pattern);
+       nnames = mono_io_scandir (path, pattern, attrs, mask, &namelist);
+       if (nnames < 0) {
+               *error = GetLastError ();
+               g_free (pattern);
+               g_free (path);
+               return NULL;
+       }
+
+       domain = mono_domain_get ();
+       result = mono_array_new (domain, mono_defaults.string_class, nnames);
+       removed = 0;
+       for (i = 0; i < nnames; i++) {
+#if PLATFORM_WIN32
+               str_name = mono_string_new (domain, namelist [i]);
+#else
+               utf16 = mono_unicode_from_external (namelist [i], &nbytes);
+               if (utf16 == NULL) {
+                       g_message ("Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODING\n",
+                               namelist [i]);
+                       removed++;
+                       continue;
+               }
+               str_name = mono_string_from_utf16 (utf16);
+               g_free (utf16);
+#endif
+               mono_array_set (result, MonoString *, i - removed, str_name);
+       }
+
+       if (removed > 0) {
+               MonoArray *shrinked;
+               shrinked = mono_array_new (domain, mono_defaults.string_class, nnames - removed);
+               for (i = 0; i < (nnames - removed); i++) {
+                       MonoString *str;
+                       str = mono_array_get (result, MonoString *, i);
+                       mono_array_set (shrinked, MonoString *,i, str);
+               }
+               result = shrinked;
+       }
+
+       g_strfreev (namelist);
+       g_free (pattern);
+       g_free (path);
+       return result;
 }
 
-gboolean ves_icall_System_PAL_OpSys_GetFileTime(HANDLE handle, gint64 *createtime, gint64 *lastaccess, gint64 *lastwrite)
+MonoString *
+ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error)
+{
+       MonoString *result;
+       gunichar2 *buf;
+       int len;
+
+       MONO_ARCH_SAVE_REGS;
+
+       len = MAX_PATH + 1;
+       buf = g_new (gunichar2, len);
+       
+       *error=ERROR_SUCCESS;
+       result = NULL;
+
+       if (GetCurrentDirectory (len, buf) > 0) {
+               len = 0;
+               while (buf [len])
+                       ++ len;
+
+               result = mono_string_new_utf16 (mono_domain_get (), buf, len);
+       } else {
+               *error=GetLastError ();
+       }
+
+       g_free (buf);
+       return result;
+}
+
+MonoBoolean
+ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
+                                               gint32 *error)
 {
        gboolean ret;
-       FILETIME cr, ac, wr;
        
-       if(handle == INVALID_HANDLE_VALUE) 
-               mono_raise_exception(mono_get_exception_io("Invalid handle"));
+       MONO_ARCH_SAVE_REGS;
 
-       ret=GetFileTime(handle, &cr, &ac, &wr);
-       if(ret==TRUE) {
-               /* The FILETIME struct holds two unsigned 32 bit
-                * values for the low and high bytes, but the .net
-                * file time insists on being signed :(
-                */
-               *createtime=((gint64)cr.dwHighDateTime << 32) +
-                       cr.dwLowDateTime;
-               *lastaccess=((gint64)ac.dwHighDateTime << 32) +
-                       ac.dwLowDateTime;
-               *lastwrite=((gint64)wr.dwHighDateTime << 32) +
-                       wr.dwLowDateTime;
+       *error=ERROR_SUCCESS;
+       
+       ret=SetCurrentDirectory (mono_string_chars (path));
+       if(ret==FALSE) {
+               *error=GetLastError ();
        }
        
        return(ret);
 }
 
-gboolean ves_icall_System_PAL_OpSys_SetFileTime(HANDLE handle, gint64 createtime, gint64 lastaccess, gint64 lastwrite)
+MonoBoolean
+ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest,
+                                    gint32 *error)
 {
        gboolean ret;
-       FILETIME cr, ac, wr;
        
-       if(handle == INVALID_HANDLE_VALUE)
-               mono_raise_exception(mono_get_exception_io("Invalid handle"));
+       MONO_ARCH_SAVE_REGS;
 
-       cr.dwLowDateTime= createtime & 0xFFFFFFFF;
-       cr.dwHighDateTime= createtime >> 32;
+       *error=ERROR_SUCCESS;
+       
+       ret=MoveFile (mono_string_chars (path), mono_string_chars (dest));
+       if(ret==FALSE) {
+               *error=GetLastError ();
+       }
        
-       ac.dwLowDateTime= lastaccess & 0xFFFFFFFF;
-       ac.dwHighDateTime= lastaccess >> 32;
+       return(ret);
+}
+
+MonoBoolean
+ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
+                                    MonoBoolean overwrite, gint32 *error)
+{
+       gboolean ret;
        
-       wr.dwLowDateTime= lastwrite & 0xFFFFFFFF;
-       wr.dwHighDateTime= lastwrite >> 32;
+       MONO_ARCH_SAVE_REGS;
 
-       ret=SetFileTime(handle, &cr, &ac, &wr);
+       *error=ERROR_SUCCESS;
+       
+       ret=CopyFile (mono_string_chars (path), mono_string_chars (dest), !overwrite);
+       if(ret==FALSE) {
+               *error=GetLastError ();
+       }
        
        return(ret);
 }
 
-/* System.IO.FileStream */
+MonoBoolean
+ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
+{
+       gboolean ret;
+       
+       MONO_ARCH_SAVE_REGS;
 
-HANDLE
-ves_icall_System_IO_FileStream_FileOpen (MonoString *path, gint32 mode, gint32 access, gint32 share) {
-       HANDLE handle;
-       char *filename;
+       *error=ERROR_SUCCESS;
        
-       filename=mono_string_to_utf16(path);
+       ret=DeleteFile (mono_string_chars (path));
+       if(ret==FALSE) {
+               *error=GetLastError ();
+       }
        
-       handle=CreateFile(filename, convert_access(access),
-                         convert_share(share), NULL, convert_mode(mode),
-                         FILE_ATTRIBUTE_NORMAL, NULL);
+       return(ret);
+}
 
-       g_free(filename);
+gint32 
+ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
+{
+       gint32 ret;
        
-       /* fixme: raise mor appropriate exceptions (errno) */
-       if(handle == INVALID_HANDLE_VALUE) {
-               mono_raise_exception(mono_get_exception_io("Invalid handle"));
-       }
+       MONO_ARCH_SAVE_REGS;
 
-       return(handle);
-}
+       *error=ERROR_SUCCESS;
+       
+       ret=GetFileAttributes (mono_string_chars (path));
 
-void 
-ves_icall_System_IO_FileStream_FileClose (HANDLE handle) {
-       if(handle == INVALID_HANDLE_VALUE) {
-               mono_raise_exception(mono_get_exception_io("Invalid handle"));
+       /* 
+        * 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 ();
        }
+       
+       return(ret);
+}
 
-       CloseHandle(handle);
+MonoBoolean
+ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
+                                             gint32 *error)
+{
+       gboolean ret;
+       
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
+       
+       ret=SetFileAttributes (mono_string_chars (path),
+                              convert_attrs (attrs));
+       if(ret==FALSE) {
+               *error=GetLastError ();
+       }
+       
+       return(ret);
 }
 
-gint32 
-ves_icall_System_IO_FileStream_FileRead (HANDLE handle, MonoArray *dest, gint32 dest_offset, gint32 count) {
+gint32
+ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
+{
        gboolean ret;
-       guint32 bytesread;
-       guchar *buf;
-       gint32 alen;
+       
+       MONO_ARCH_SAVE_REGS;
 
-       if(handle == INVALID_HANDLE_VALUE) {
-               mono_raise_exception(mono_get_exception_io("Invalid handle"));
+       *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 ();
        }
+       
+       return(ret);
+}
+
+MonoBoolean
+ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
+                                       gint32 *error)
+{
+       gboolean result;
+       WIN32_FILE_ATTRIBUTE_DATA data;
+
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
+       
+       result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data);
 
-       alen=mono_array_length(dest);
-       if(dest_offset+count>alen) {
-               return(0);
+       if (result) {
+               convert_win32_file_attribute_data (&data,
+                                                  mono_string_chars (path),
+                                                  stat);
+       } else {
+               *error=GetLastError ();
        }
+
+       return result;
+}
+
+HANDLE 
+ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
+                                gint32 access_mode, gint32 share,
+                                MonoBoolean async, gint32 *error)
+{
+       HANDLE ret;
        
-       buf=mono_array_addr(dest, guchar, dest_offset);
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
        
-       ret=ReadFile(handle, buf, count, &bytesread, NULL);
+       ret=CreateFile (mono_string_chars (filename),
+                       convert_access (access_mode), convert_share (share),
+                       NULL, convert_mode (mode),
+                       FILE_ATTRIBUTE_NORMAL | ((async) ? FILE_FLAG_OVERLAPPED : 0),
+                       NULL);
+       if(ret==INVALID_HANDLE_VALUE) {
+               *error=GetLastError ();
+       }
        
-       return(bytesread);
+       return(ret);
 }
 
-gint32 
-ves_icall_System_IO_FileStream_FileWrite (HANDLE handle, MonoArray *src, gint32 src_offset, gint32 count) {
+MonoBoolean
+ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
+{
        gboolean ret;
-       guint32 byteswritten;
-       guchar *buf;
-       gint32 alen;
        
-       if(handle == INVALID_HANDLE_VALUE) {
-               mono_raise_exception(mono_get_exception_io("Invalid handle"));
-       }
+       MONO_ARCH_SAVE_REGS;
 
-       alen=mono_array_length(src);
-       if(src_offset+count>alen) {
-               return(0);
+       *error=ERROR_SUCCESS;
+       
+       ret=CloseHandle (handle);
+       if(ret==FALSE) {
+               *error=GetLastError ();
        }
        
-       buf=mono_array_addr(src, guchar, src_offset);
+       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;
+
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
+       
+       if (dest_offset + count > mono_array_length (dest))
+               return 0;
+
+       buffer = mono_array_addr (dest, guchar, dest_offset);
+       result = ReadFile (handle, buffer, count, &n, NULL);
+
+       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;
+
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
        
-       ret=WriteFile(handle, buf, count, &byteswritten, NULL);
+       if (src_offset + count > mono_array_length (src))
+               return 0;
        
-       return(byteswritten);
+       buffer = mono_array_addr (src, guchar, src_offset);
+       result = WriteFile (handle, buffer, count, &n, NULL);
+
+       if (!result) {
+               *error=GetLastError ();
+               return -1;
+       }
+
+       return (gint32)n;
 }
 
 gint64 
-ves_icall_System_IO_FileStream_FileSeek (HANDLE handle, gint64 offset, gint32 origin) {
-       gint64 ret;
-       gint32 offsetlo, offsethi, retlo;
+ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
+                                gint32 *error)
+{
+       guint32 offset_hi;
+
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
        
-       if(handle == INVALID_HANDLE_VALUE) {
-               mono_raise_exception(mono_get_exception_io("Invalid handle"));
-       }
+       offset_hi = offset >> 32;
+       offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
+                                convert_seekorigin (origin));
 
-       offsetlo=offset & 0xFFFFFFFF;
-       offsethi=offset >> 32;
+       if(offset==INVALID_SET_FILE_POINTER) {
+               *error=GetLastError ();
+       }
+       
+       return offset | ((gint64)offset_hi << 32);
+}
 
-       retlo=SetFilePointer(handle, offset, &offsethi,
-                            convert_seekorigin(origin));
+MonoBoolean
+ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
+{
+       gboolean ret;
        
-       ret=((gint64)offsethi << 32) + retlo;
+       MONO_ARCH_SAVE_REGS;
 
+       *error=ERROR_SUCCESS;
+       
+       ret=FlushFileBuffers (handle);
+       if(ret==FALSE) {
+               *error=GetLastError ();
+       }
+       
        return(ret);
 }
 
 gint64 
-ves_icall_System_IO_FileStream_FileGetLength (HANDLE handle) {
-       gint32 length_lo, length_hi;
+ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
+{
+       gint64 length;
+       guint32 length_hi;
 
-       if (handle == INVALID_HANDLE_VALUE)
-               mono_raise_exception (mono_get_exception_io ("Invalid handle"));
+       MONO_ARCH_SAVE_REGS;
+
+       *error=ERROR_SUCCESS;
        
-       length_lo = GetFileSize (handle, &length_hi);
+       length = GetFileSize (handle, &length_hi);
+       if(length==INVALID_FILE_SIZE) {
+               *error=GetLastError ();
+       }
        
-       return ((gint64)length_hi << 32) | length_lo;
+       return length | ((gint64)length_hi << 32);
 }
 
-void 
-ves_icall_System_IO_FileStream_FileSetLength (HANDLE handle, gint64 length)
+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;
-       gint32 position_lo, position_hi;
-       gint32 length_lo, length_hi;
 
-       if (handle == INVALID_HANDLE_VALUE)
-               mono_raise_exception (mono_get_exception_io ("Invalid handle"));
+       MONO_ARCH_SAVE_REGS;
 
+       *error=ERROR_SUCCESS;
+       
        /* save file pointer */
 
-       position_hi = 0;
-       position_lo = SetFilePointer (handle, 0, &position_hi, FILE_CURRENT);
+       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;
-       length_lo = length & 0xFFFFFFFF;
+       offset_set=SetFilePointer (handle, length & 0xFFFFFFFF, &length_hi,
+                                  FILE_BEGIN);
+       if(offset_set==INVALID_SET_FILE_POINTER) {
+               *error=GetLastError ();
+               return(FALSE);
+       }
 
-       SetFilePointer (handle, length_lo, &length_hi, FILE_BEGIN);
        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;
 
-       /* restore */
+       MONO_ARCH_SAVE_REGS;
 
-       SetFilePointer (handle, position_lo, &position_hi, FILE_BEGIN);
+       *error=ERROR_SUCCESS;
        
-       if (result == FALSE)
-               mono_raise_exception (mono_get_exception_io ("IO Exception"));
+       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 ();
+       }
+       
+       return(ret);
 }
 
-void 
-ves_icall_System_IO_FileStream_FileFlush (HANDLE handle)
+HANDLE 
+ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
 {
-       gboolean result;
+       MONO_ARCH_SAVE_REGS;
+
+       return GetStdHandle (STD_OUTPUT_HANDLE);
+}
+
+HANDLE 
+ves_icall_System_IO_MonoIO_get_ConsoleInput ()
+{
+       MONO_ARCH_SAVE_REGS;
+
+       return GetStdHandle (STD_INPUT_HANDLE);
+}
+
+HANDLE 
+ves_icall_System_IO_MonoIO_get_ConsoleError ()
+{
+       MONO_ARCH_SAVE_REGS;
+
+       return GetStdHandle (STD_ERROR_HANDLE);
+}
+
+MonoBoolean
+ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle,
+                                      HANDLE *write_handle)
+{
+       SECURITY_ATTRIBUTES attr;
+       gboolean ret;
+       
+       MONO_ARCH_SAVE_REGS;
+
+       attr.nLength=sizeof(SECURITY_ATTRIBUTES);
+       attr.bInheritHandle=TRUE;
+       attr.lpSecurityDescriptor=NULL;
+       
+       ret=CreatePipe (read_handle, write_handle, &attr, 0);
+       if(ret==FALSE) {
+               /* FIXME: throw an exception? */
+               return(FALSE);
+       }
+       
+       return(TRUE);
+}
+
+gunichar2 
+ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
+{
+#if defined (PLATFORM_WIN32)
+       return (gunichar2) ':'; /* colon */
+#else
+       return (gunichar2) '/'; /* forward slash */
+#endif
+}
+
+gunichar2 
+ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
+{
+#if defined (PLATFORM_WIN32)
+       return (gunichar2) '\\';        /* backslash */
+#else
+       return (gunichar2) '/'; /* forward slash */
+#endif
+}
+
+gunichar2 
+ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
+{
+#if defined (PLATFORM_WIN32)
+       return (gunichar2) '/'; /* forward slash */
+#else
+       return (gunichar2) '/'; /* slash, same as DirectorySeparatorChar */
+#endif
+}
 
-       if (handle == INVALID_HANDLE_VALUE)
-               mono_raise_exception (mono_get_exception_io ("Invalid handle"));
+gunichar2 
+ves_icall_System_IO_MonoIO_get_PathSeparator ()
+{
+#if defined (PLATFORM_WIN32)
+       return (gunichar2) ';'; /* semicolon */
+#else
+       return (gunichar2) ':'; /* colon */
+#endif
+}
+
+static const gunichar2
+invalid_path_chars [] = {
+#if defined (PLATFORM_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 ()
+{
+       MonoArray *chars;
+       MonoDomain *domain;
+       int i, n;
 
-       FlushFileBuffers (handle);      /* discard result */
+       MONO_ARCH_SAVE_REGS;
+
+       domain = mono_domain_get ();
+       n = sizeof (invalid_path_chars) / sizeof (gunichar2);
+       chars = mono_array_new (domain, mono_defaults.char_class, n);
+
+       for (i = 0; i < n; ++ i)
+               mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
+       
+       return chars;
 }
+
+gint32
+ves_icall_System_IO_MonoIO_GetTempPath (MonoString **mono_name)
+{
+       gunichar2 *name;
+       int ret;
+
+       name=g_new0 (gunichar2, 256);
+       
+       ret=GetTempPath (256, name);
+       if(ret>255) {
+               /* Buffer was too short. Try again... */
+               g_free (name);
+               name=g_new0 (gunichar2, ret+2); /* include the terminator */
+               ret=GetTempPath (ret, name);
+       }
+       
+       if(ret>0) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION
+                          ": Temp path is [%s] (len %d)", name, ret);
+#endif
+
+               *mono_name=mono_string_new_utf16 (mono_domain_get (), name,
+                                                 ret);
+       }
+
+       g_free (name);
+       
+       return(ret);
+}
+
+void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
+                                     gint64 length, gint32 *error)
+{
+       gboolean ret;
+       
+       *error=ERROR_SUCCESS;
+       
+       ret=LockFile (handle, position & 0xFFFFFFFF, position >> 32,
+                     length & 0xFFFFFFFF, length >> 32);
+       if (ret == FALSE) {
+               *error = GetLastError ();
+       }
+}
+
+void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
+                                       gint64 length, gint32 *error)
+{
+       gboolean ret;
+       
+       *error=ERROR_SUCCESS;
+       
+       ret=UnlockFile (handle, position & 0xFFFFFFFF, position >> 32,
+                       length & 0xFFFFFFFF, length >> 32);
+       if (ret == FALSE) {
+               *error = GetLastError ();
+       }
+}
+