630278f1b26841232678a578f038006f8d96ff96
[mono.git] / mono / metadata / w32file.c
1 /**
2  * \file
3  * File IO internal calls
4  *
5  * Author:
6  *      Dick Porter (dick@ximian.com)
7  *      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8  *
9  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11  * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
12  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13  */
14
15 #include <config.h>
16
17 #include <glib.h>
18 #include <string.h>
19 #include <errno.h>
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23 #ifdef HAVE_SYS_STAT_H
24 #include <sys/stat.h>
25 #endif
26 #ifdef HAVE_SYS_TYPES_H
27 #include <sys/types.h>
28 #endif
29
30 #include <mono/metadata/object.h>
31 #include <mono/metadata/w32file.h>
32 #include <mono/metadata/w32error.h>
33 #include <mono/metadata/w32file-internals.h>
34 #include <mono/metadata/exception.h>
35 #include <mono/metadata/appdomain.h>
36 #include <mono/metadata/marshal.h>
37 #include <mono/utils/strenc.h>
38 #include <utils/mono-io-portability.h>
39 #include <mono/metadata/w32handle.h>
40 #include <mono/utils/w32api.h>
41
42 #undef DEBUG
43
44 /* conversion functions */
45
46 static guint32 convert_mode(MonoFileMode mono_mode)
47 {
48         guint32 mode;
49
50         switch(mono_mode) {
51         case FileMode_CreateNew:
52                 mode=CREATE_NEW;
53                 break;
54         case FileMode_Create:
55                 mode=CREATE_ALWAYS;
56                 break;
57         case FileMode_Open:
58                 mode=OPEN_EXISTING;
59                 break;
60         case FileMode_OpenOrCreate:
61                 mode=OPEN_ALWAYS;
62                 break;
63         case FileMode_Truncate:
64                 mode=TRUNCATE_EXISTING;
65                 break;
66         case FileMode_Append:
67                 mode=OPEN_ALWAYS;
68                 break;
69         default:
70                 g_warning("System.IO.FileMode has unknown value 0x%x",
71                           mono_mode);
72                 /* Safe fallback */
73                 mode=OPEN_EXISTING;
74         }
75         
76         return(mode);
77 }
78
79 static guint32 convert_access(MonoFileAccess mono_access)
80 {
81         guint32 access;
82         
83         switch(mono_access) {
84         case FileAccess_Read:
85                 access=GENERIC_READ;
86                 break;
87         case FileAccess_Write:
88                 access=GENERIC_WRITE;
89                 break;
90         case FileAccess_ReadWrite:
91                 access=GENERIC_READ|GENERIC_WRITE;
92                 break;
93         default:
94                 g_warning("System.IO.FileAccess has unknown value 0x%x",
95                           mono_access);
96                 /* Safe fallback */
97                 access=GENERIC_READ;
98         }
99         
100         return(access);
101 }
102
103 static guint32 convert_share(MonoFileShare mono_share)
104 {
105         guint32 share = 0;
106         
107         if (mono_share & FileShare_Read) {
108                 share |= FILE_SHARE_READ;
109         }
110         if (mono_share & FileShare_Write) {
111                 share |= FILE_SHARE_WRITE;
112         }
113         if (mono_share & FileShare_Delete) {
114                 share |= FILE_SHARE_DELETE;
115         }
116         
117         if (mono_share & ~(FileShare_Read|FileShare_Write|FileShare_Delete)) {
118                 g_warning("System.IO.FileShare has unknown value 0x%x",
119                           mono_share);
120                 /* Safe fallback */
121                 share=0;
122         }
123
124         return(share);
125 }
126
127 #if 0
128 static guint32 convert_stdhandle(guint32 fd)
129 {
130         guint32 stdhandle;
131         
132         switch(fd) {
133         case 0:
134                 stdhandle=STD_INPUT_HANDLE;
135                 break;
136         case 1:
137                 stdhandle=STD_OUTPUT_HANDLE;
138                 break;
139         case 2:
140                 stdhandle=STD_ERROR_HANDLE;
141                 break;
142         default:
143                 g_warning("unknown standard file descriptor %d", fd);
144                 stdhandle=STD_INPUT_HANDLE;
145         }
146         
147         return(stdhandle);
148 }
149 #endif
150
151 static guint32 convert_seekorigin(MonoSeekOrigin origin)
152 {
153         guint32 w32origin;
154         
155         switch(origin) {
156         case SeekOrigin_Begin:
157                 w32origin=FILE_BEGIN;
158                 break;
159         case SeekOrigin_Current:
160                 w32origin=FILE_CURRENT;
161                 break;
162         case SeekOrigin_End:
163                 w32origin=FILE_END;
164                 break;
165         default:
166                 g_warning("System.IO.SeekOrigin has unknown value 0x%x",
167                           origin);
168                 /* Safe fallback */
169                 w32origin=FILE_CURRENT;
170         }
171         
172         return(w32origin);
173 }
174
175 static gint64 convert_filetime (const FILETIME *filetime)
176 {
177         return (gint64) ((((guint64) filetime->dwHighDateTime) << 32) + filetime->dwLowDateTime);
178 }
179
180 /* Managed file attributes have nearly but not quite the same values
181  * as the w32 equivalents.
182  */
183 static guint32 convert_attrs(MonoFileAttributes attrs)
184 {
185         if(attrs & FileAttributes_Encrypted) {
186                 attrs = (MonoFileAttributes)(attrs | FILE_ATTRIBUTE_ENCRYPTED);
187         }
188         
189         return(attrs);
190 }
191
192 /*
193  * On Win32, mono_w32file_get_attributes|_ex () seems to try opening the file,
194  * which might lead to sharing violation errors, whereas mono_w32file_find_first
195  * always succeeds. These 2 wrappers resort to mono_w32file_find_first if
196  * mono_w32file_get_attributes|_ex () has failed.
197  */
198 static guint32
199 get_file_attributes (const gunichar2 *path)
200 {
201         guint32 res;
202         WIN32_FIND_DATA find_data;
203         HANDLE find_handle;
204         gint32 error;
205
206         res = mono_w32file_get_attributes (path);
207         if (res != -1)
208                 return res;
209
210         error = mono_w32error_get_last ();
211
212         if (error != ERROR_SHARING_VIOLATION)
213                 return res;
214
215         find_handle = mono_w32file_find_first (path, &find_data);
216
217         if (find_handle == INVALID_HANDLE_VALUE)
218                 return res;
219
220         mono_w32file_find_close (find_handle);
221
222         return find_data.dwFileAttributes;
223 }
224
225 static gboolean
226 get_file_attributes_ex (const gunichar2 *path, MonoIOStat *stat)
227 {
228         gboolean res;
229         WIN32_FIND_DATA find_data;
230         HANDLE find_handle;
231         gint32 error;
232
233         res = mono_w32file_get_attributes_ex (path, stat);
234         if (res)
235                 return TRUE;
236
237         error = mono_w32error_get_last ();
238         if (error != ERROR_SHARING_VIOLATION)
239                 return FALSE;
240
241         find_handle = mono_w32file_find_first (path, &find_data);
242
243         if (find_handle == INVALID_HANDLE_VALUE)
244                 return FALSE;
245
246         mono_w32file_find_close (find_handle);
247         
248         stat->attributes = find_data.dwFileAttributes;
249         stat->creation_time = convert_filetime (&find_data.ftCreationTime);
250         stat->last_access_time = convert_filetime (&find_data.ftLastAccessTime);
251         stat->last_write_time = convert_filetime (&find_data.ftLastWriteTime);
252         stat->length = ((gint64)find_data.nFileSizeHigh << 32) | find_data.nFileSizeLow;
253         return TRUE;
254 }
255
256 /* System.IO.MonoIO internal calls */
257
258 MonoBoolean
259 ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error)
260 {
261         gboolean ret;
262         
263         *error=ERROR_SUCCESS;
264         
265         ret=mono_w32file_create_directory (mono_string_chars (path));
266         if(ret==FALSE) {
267                 *error=mono_w32error_get_last ();
268         }
269
270         return(ret);
271 }
272
273 MonoBoolean
274 ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error)
275 {
276         gboolean ret;
277         
278         *error=ERROR_SUCCESS;
279         
280         ret=mono_w32file_remove_directory (mono_string_chars (path));
281         if(ret==FALSE) {
282                 *error=mono_w32error_get_last ();
283         }
284
285         return(ret);
286 }
287
288 typedef struct {
289         MonoDomain *domain;
290         gchar *utf8_path;
291         HANDLE find_handle;
292 } IncrementalFind;
293         
294 static gboolean
295 incremental_find_check_match (IncrementalFind *handle, WIN32_FIND_DATA *data, MonoString **result, MonoError *error)
296 {
297         error_init (error);
298         gchar *utf8_result;
299         gchar *full_name;
300         
301         if ((data->cFileName[0] == '.' && data->cFileName[1] == 0) || (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0))
302                 return FALSE;
303
304         utf8_result = g_utf16_to_utf8 (data->cFileName, -1, NULL, NULL, NULL);
305         if (utf8_result == NULL) 
306                 return FALSE;
307         
308         full_name = g_build_filename (handle->utf8_path, utf8_result, NULL);
309         g_free (utf8_result);
310         *result = mono_string_new_checked (mono_domain_get (), full_name, error);
311         g_free (full_name);
312         if (!is_ok (error))
313                 return FALSE;
314         
315         return TRUE;
316 }
317
318 HANDLE
319 ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
320 {
321         HANDLE hnd;
322         WIN32_FIND_DATA data;
323         MonoError error;
324
325         hnd = mono_w32file_find_first (mono_string_chars (path_with_pattern), &data);
326
327         if (hnd == INVALID_HANDLE_VALUE) {
328                 *file_name = NULL;
329                 *file_attr = 0;
330                 *ioerror = mono_w32error_get_last ();
331                 return hnd;
332         }
333
334         mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
335         mono_error_set_pending_exception (&error);
336
337         *file_attr = data.dwFileAttributes;
338         *ioerror = ERROR_SUCCESS;
339
340         return hnd;
341 }
342
343 MonoBoolean
344 ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
345 {
346         MonoBoolean res;
347         WIN32_FIND_DATA data;
348         MonoError error;
349
350         res = mono_w32file_find_next (hnd, &data);
351
352         if (res == FALSE) {
353                 *file_name = NULL;
354                 *file_attr = 0;
355                 *ioerror = mono_w32error_get_last ();
356                 return res;
357         }
358
359         mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
360         mono_error_set_pending_exception (&error);
361
362         *file_attr = data.dwFileAttributes;
363         *ioerror = ERROR_SUCCESS;
364
365         return res;
366 }
367
368 MonoBoolean
369 ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd)
370 {
371         return mono_w32file_find_close (hnd);
372 }
373
374 /* FIXME make gc suspendable */
375 MonoString *
376 ves_icall_System_IO_MonoIO_FindFirst (MonoString *path,
377                                       MonoString *path_with_pattern,
378                                       gint32 *result_attr, gint32 *ioerror,
379                                       gpointer *handle)
380 {
381         MonoError error;
382         WIN32_FIND_DATA data;
383         HANDLE find_handle;
384         IncrementalFind *ifh;
385         MonoString *result;
386         
387         *ioerror = ERROR_SUCCESS;
388         
389         find_handle = mono_w32file_find_first (mono_string_chars (path_with_pattern), &data);
390         
391         if (find_handle == INVALID_HANDLE_VALUE) {
392                 gint32 find_error = mono_w32error_get_last ();
393                 *handle = NULL;
394                 
395                 if (find_error == ERROR_FILE_NOT_FOUND) 
396                         return NULL;
397                 
398                 *ioerror = find_error;
399                 return NULL;
400         }
401
402         ifh = g_new (IncrementalFind, 1);
403         ifh->find_handle = find_handle;
404         ifh->utf8_path = mono_string_to_utf8_checked (path, &error);
405         if (mono_error_set_pending_exception (&error)) {
406                 mono_w32file_find_close (find_handle);
407                 g_free (ifh);
408                 return NULL;
409         }
410         ifh->domain = mono_domain_get ();
411         *handle = ifh;
412
413         while (incremental_find_check_match (ifh, &data, &result, &error) == 0){
414                 if (!is_ok (&error)) {
415                         mono_error_set_pending_exception (&error);
416                         return NULL;
417                 }
418                 if (mono_w32file_find_next (find_handle, &data) == FALSE){
419                         int e = mono_w32error_get_last ();
420                         if (e != ERROR_NO_MORE_FILES)
421                                 *ioerror = e;
422                         return NULL;
423                 }
424         }
425         *result_attr = data.dwFileAttributes;
426
427         return result;
428 }
429
430 /* FIXME make gc suspendable */
431 MonoString *
432 ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *ioerror)
433 {
434         MonoError error;
435         IncrementalFind *ifh = (IncrementalFind *)handle;
436         WIN32_FIND_DATA data;
437         MonoString *result;
438
439         error_init (&error);
440         *ioerror = ERROR_SUCCESS;
441         do {
442                 if (!is_ok (&error)) {
443                         mono_error_set_pending_exception (&error);
444                         return NULL;
445                 }
446                 if (mono_w32file_find_next (ifh->find_handle, &data) == FALSE){
447                         int e = mono_w32error_get_last ();
448                         if (e != ERROR_NO_MORE_FILES)
449                                 *ioerror = e;
450                         return NULL;
451                 }
452         } while (incremental_find_check_match (ifh, &data, &result, &error) == 0);
453
454         *result_attr = data.dwFileAttributes;
455         return result;
456 }
457
458 int
459 ves_icall_System_IO_MonoIO_FindClose (gpointer handle)
460 {
461         IncrementalFind *ifh = (IncrementalFind *)handle;
462         gint32 error;
463
464         if (mono_w32file_find_close (ifh->find_handle) == FALSE){
465                 error = mono_w32error_get_last ();
466         } else
467                 error = ERROR_SUCCESS;
468         g_free (ifh->utf8_path);
469         g_free (ifh);
470
471         return error;
472 }
473
474 MonoString *
475 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error)
476 {
477         MonoError error;
478         MonoString *result;
479         gunichar2 *buf;
480         int len, res_len;
481
482         len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/
483         buf = g_new (gunichar2, len);
484         
485         error_init (&error);
486         *io_error=ERROR_SUCCESS;
487         result = NULL;
488
489         res_len = mono_w32file_get_cwd (len, buf);
490         if (res_len > len) { /*buf is too small.*/
491                 int old_res_len = res_len;
492                 g_free (buf);
493                 buf = g_new (gunichar2, res_len);
494                 res_len = mono_w32file_get_cwd (res_len, buf) == old_res_len;
495         }
496         
497         if (res_len) {
498                 len = 0;
499                 while (buf [len])
500                         ++ len;
501
502                 result = mono_string_new_utf16_checked (mono_domain_get (), buf, len, &error);
503         } else {
504                 *io_error=mono_w32error_get_last ();
505         }
506
507         g_free (buf);
508         mono_error_set_pending_exception (&error);
509         return result;
510 }
511
512 MonoBoolean
513 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
514                                                 gint32 *error)
515 {
516         gboolean ret;
517         
518         *error=ERROR_SUCCESS;
519         
520         ret=mono_w32file_set_cwd (mono_string_chars (path));
521         if(ret==FALSE) {
522                 *error=mono_w32error_get_last ();
523         }
524         
525         return(ret);
526 }
527
528 MonoBoolean
529 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, gint32 *error)
530 {
531         *error=ERROR_SUCCESS;
532         return mono_w32file_move (mono_string_chars (path), mono_string_chars (dest), error);
533 }
534
535 MonoBoolean
536 ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName,
537                                         MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors,
538                                         gint32 *error)
539 {
540         gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL;
541         guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH;
542
543         if (sourceFileName)
544                 utf16_sourceFileName = mono_string_chars (sourceFileName);
545         if (destinationFileName)
546                 utf16_destinationFileName = mono_string_chars (destinationFileName);
547         if (destinationBackupFileName)
548                 utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName);
549
550         *error = ERROR_SUCCESS;
551         if (ignoreMetadataErrors)
552                 replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS;
553
554         /* FIXME: source and destination file names must not be NULL, but apparently they might be! */
555         return mono_w32file_replace (utf16_destinationFileName, utf16_sourceFileName,
556                                           utf16_destinationBackupFileName, replaceFlags, error);
557 }
558
559 MonoBoolean
560 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
561                                      MonoBoolean overwrite, gint32 *error)
562 {
563         *error=ERROR_SUCCESS;
564         return mono_w32file_copy (mono_string_chars (path), mono_string_chars (dest), overwrite, error);
565 }
566
567 MonoBoolean
568 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
569 {
570         gboolean ret;
571         
572         *error=ERROR_SUCCESS;
573         
574         ret=mono_w32file_delete (mono_string_chars (path));
575         if(ret==FALSE) {
576                 *error=mono_w32error_get_last ();
577         }
578         
579         return(ret);
580 }
581
582 gint32 
583 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
584 {
585         gint32 ret;
586         *error=ERROR_SUCCESS;
587         
588         ret=get_file_attributes (mono_string_chars (path));
589
590         /* 
591          * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
592          * headers is wrong, hence this temporary workaround.
593          * See
594          * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
595          */
596         if (ret==-1) {
597           /* if(ret==INVALID_FILE_ATTRIBUTES) { */
598                 *error=mono_w32error_get_last ();
599         }
600         return(ret);
601 }
602
603 MonoBoolean
604 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
605                                               gint32 *error)
606 {
607         gboolean ret;
608         *error=ERROR_SUCCESS;
609         
610         ret=mono_w32file_set_attributes (mono_string_chars (path),
611                 convert_attrs ((MonoFileAttributes)attrs));
612         if(ret==FALSE) {
613                 *error=mono_w32error_get_last ();
614         }
615         return(ret);
616 }
617
618 gint32
619 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
620 {
621         gboolean ret;
622
623         *error=ERROR_SUCCESS;
624         
625         ret=mono_w32file_get_type (handle);
626         if(ret==FILE_TYPE_UNKNOWN) {
627                 /* Not necessarily an error, but the caller will have
628                  * to decide based on the error value.
629                  */
630                 *error=mono_w32error_get_last ();
631         }
632         
633         return(ret);
634 }
635
636 MonoBoolean
637 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, gint32 *error)
638 {
639         gboolean result;
640
641         *error=ERROR_SUCCESS;
642         
643         result = get_file_attributes_ex (mono_string_chars (path), stat);
644
645         if (!result) {
646                 *error=mono_w32error_get_last ();
647                 memset (stat, 0, sizeof (MonoIOStat));
648         }
649
650         return result;
651 }
652
653 HANDLE 
654 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
655                                  gint32 access_mode, gint32 share, gint32 options,
656                                  gint32 *error)
657 {
658         HANDLE ret;
659         int attributes, attrs;
660         gunichar2 *chars;
661
662         chars = mono_string_chars (filename);   
663         *error=ERROR_SUCCESS;
664
665         if (options != 0){
666                 if (options & FileOptions_Encrypted)
667                         attributes = FILE_ATTRIBUTE_ENCRYPTED;
668                 else
669                         attributes = FILE_ATTRIBUTE_NORMAL;
670                 if (options & FileOptions_DeleteOnClose)
671                         attributes |= FILE_FLAG_DELETE_ON_CLOSE;
672                 if (options & FileOptions_SequentialScan)
673                         attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
674                 if (options & FileOptions_RandomAccess)
675                         attributes |= FILE_FLAG_RANDOM_ACCESS;
676
677                 if (options & FileOptions_Temporary)
678                         attributes |= FILE_ATTRIBUTE_TEMPORARY;
679                 
680                 if (options & FileOptions_WriteThrough)
681                         attributes |= FILE_FLAG_WRITE_THROUGH;
682         } else
683                 attributes = FILE_ATTRIBUTE_NORMAL;
684
685         /* If we're opening a directory we need to set the extra flag
686          */
687         attrs = get_file_attributes (chars);
688         if (attrs != INVALID_FILE_ATTRIBUTES) {
689                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
690                         attributes |= FILE_FLAG_BACKUP_SEMANTICS;
691                 }
692         }
693         
694         ret=mono_w32file_create (chars, convert_access ((MonoFileAccess)access_mode), convert_share ((MonoFileShare)share), convert_mode ((MonoFileMode)mode), attributes);
695         if(ret==INVALID_HANDLE_VALUE) {
696                 *error=mono_w32error_get_last ();
697         } 
698         
699         return(ret);
700 }
701
702 MonoBoolean
703 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
704 {
705         gboolean ret;
706         *error=ERROR_SUCCESS;
707         
708         ret=mono_w32file_close (handle);
709         if(ret==FALSE) {
710                 *error=mono_w32error_get_last ();
711         }
712         return(ret);
713 }
714
715 gint32 
716 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
717                                  gint32 dest_offset, gint32 count,
718                                  gint32 *error)
719 {
720         guchar *buffer;
721         gboolean result;
722         guint32 n;
723
724         *error=ERROR_SUCCESS;
725
726         MONO_CHECK_ARG_NULL (dest, 0);
727
728         if (dest_offset > mono_array_length (dest) - count) {
729                 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
730                 return 0;
731         }
732
733         buffer = mono_array_addr (dest, guchar, dest_offset);
734
735         result = mono_w32file_read (handle, buffer, count, &n);
736
737         if (!result) {
738                 *error=mono_w32error_get_last ();
739                 return -1;
740         }
741
742         return (gint32)n;
743 }
744
745 gint32 
746 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
747                                   gint32 src_offset, gint32 count,
748                                   gint32 *error)
749 {
750         guchar *buffer;
751         gboolean result;
752         guint32 n;
753
754         *error=ERROR_SUCCESS;
755
756         MONO_CHECK_ARG_NULL (src, 0);
757         
758         if (src_offset > mono_array_length (src) - count) {
759                 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
760                 return 0;
761         }
762         
763         buffer = mono_array_addr (src, guchar, src_offset);
764         result = mono_w32file_write (handle, buffer, count, &n);
765
766         if (!result) {
767                 *error=mono_w32error_get_last ();
768                 return -1;
769         }
770
771         return (gint32)n;
772 }
773
774 gint64 
775 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
776                                  gint32 *error)
777 {
778         gint32 offset_hi;
779
780         *error=ERROR_SUCCESS;
781         
782         offset_hi = offset >> 32;
783         offset = mono_w32file_seek (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi,
784                                  convert_seekorigin ((MonoSeekOrigin)origin));
785
786         if(offset==INVALID_SET_FILE_POINTER) {
787                 *error=mono_w32error_get_last ();
788         }
789
790         return offset | ((gint64)offset_hi << 32);
791 }
792
793 MonoBoolean
794 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
795 {
796         gboolean ret;
797
798         *error=ERROR_SUCCESS;
799         
800         ret=mono_w32file_flush (handle);
801         if(ret==FALSE) {
802                 *error=mono_w32error_get_last ();
803         }
804         
805         return(ret);
806 }
807
808 gint64
809 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
810 {
811         *error=ERROR_SUCCESS;
812         return mono_w32file_get_file_size (handle, error);
813 }
814
815 /* FIXME make gc suspendable */
816 MonoBoolean
817 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
818                                       gint32 *error)
819 {
820         gint64 offset, offset_set;
821         gint32 offset_hi;
822         gint32 length_hi;
823         gboolean result;
824
825         *error=ERROR_SUCCESS;
826         
827         /* save file pointer */
828
829         offset_hi = 0;
830         offset = mono_w32file_seek (handle, 0, &offset_hi, FILE_CURRENT);
831         if(offset==INVALID_SET_FILE_POINTER) {
832                 *error=mono_w32error_get_last ();
833                 return(FALSE);
834         }
835
836         /* extend or truncate */
837
838         length_hi = length >> 32;
839         offset_set=mono_w32file_seek (handle, length & 0xFFFFFFFF, &length_hi,
840                                    FILE_BEGIN);
841         if(offset_set==INVALID_SET_FILE_POINTER) {
842                 *error=mono_w32error_get_last ();
843                 return(FALSE);
844         }
845
846         result = mono_w32file_truncate (handle);
847         if(result==FALSE) {
848                 *error=mono_w32error_get_last ();
849                 return(FALSE);
850         }
851
852         /* restore file pointer */
853
854         offset_set=mono_w32file_seek (handle, offset & 0xFFFFFFFF, &offset_hi,
855                                    FILE_BEGIN);
856         if(offset_set==INVALID_SET_FILE_POINTER) {
857                 *error=mono_w32error_get_last ();
858                 return(FALSE);
859         }
860
861         return result;
862 }
863
864 MonoBoolean
865 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
866                                         gint64 last_access_time,
867                                         gint64 last_write_time, gint32 *error)
868 {
869         gboolean ret;
870         const FILETIME *creation_filetime;
871         const FILETIME *access_filetime;
872         const FILETIME *write_filetime;
873
874         *error=ERROR_SUCCESS;
875         
876         if (creation_time < 0)
877                 creation_filetime = NULL;
878         else
879                 creation_filetime = (FILETIME *)&creation_time;
880
881         if (last_access_time < 0)
882                 access_filetime = NULL;
883         else
884                 access_filetime = (FILETIME *)&last_access_time;
885
886         if (last_write_time < 0)
887                 write_filetime = NULL;
888         else
889                 write_filetime = (FILETIME *)&last_write_time;
890
891         ret=mono_w32file_set_times (handle, creation_filetime, access_filetime, write_filetime);
892         if(ret==FALSE) {
893                 *error=mono_w32error_get_last ();
894         }
895
896         return(ret);
897 }
898
899 HANDLE 
900 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
901 {
902         return mono_w32file_get_console_output ();
903 }
904
905 HANDLE 
906 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
907 {
908         return mono_w32file_get_console_input ();
909 }
910
911 HANDLE 
912 ves_icall_System_IO_MonoIO_get_ConsoleError ()
913 {
914         return mono_w32file_get_console_error ();
915 }
916
917 MonoBoolean
918 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, HANDLE *write_handle, gint32 *error)
919 {
920         gboolean ret;
921
922         ret=mono_w32file_create_pipe (read_handle, write_handle, 0);
923
924         if(ret==FALSE) {
925                 *error = mono_w32error_get_last ();
926                 /* FIXME: throw an exception? */
927                 return(FALSE);
928         }
929         
930         return(TRUE);
931 }
932
933 MonoBoolean
934 ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle, HANDLE source_handle,
935                 HANDLE target_process_handle, HANDLE *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error)
936 {
937 #ifndef HOST_WIN32
938         *target_handle = mono_w32handle_duplicate (source_handle);
939 #else
940         gboolean ret;
941
942         MONO_ENTER_GC_SAFE;
943         ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options);
944         MONO_EXIT_GC_SAFE;
945
946         if (!ret) {
947                 *error = mono_w32error_get_last ();
948                 /* FIXME: throw an exception? */
949                 return(FALSE);
950         }
951 #endif
952
953         return(TRUE);
954 }
955
956 #ifndef HOST_WIN32
957 gunichar2 
958 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
959 {
960         return (gunichar2) '/'; /* forward slash */
961 }
962
963 gunichar2 
964 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
965 {
966         return (gunichar2) '/'; /* forward slash */
967 }
968
969 gunichar2 
970 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
971 {
972         if (IS_PORTABILITY_SET)
973                 return (gunichar2) '\\';        /* backslash */
974         else
975                 return (gunichar2) '/'; /* forward slash */
976 }
977
978 gunichar2 
979 ves_icall_System_IO_MonoIO_get_PathSeparator ()
980 {
981         return (gunichar2) ':'; /* colon */
982 }
983 #endif /* !HOST_WIN32 */
984
985 static const gunichar2
986 invalid_path_chars [] = {
987 #if defined (TARGET_WIN32)
988         0x0022,                         /* double quote, which seems allowed in MS.NET but should be rejected */
989         0x003c,                         /* less than */
990         0x003e,                         /* greater than */
991         0x007c,                         /* pipe */
992         0x0008,
993         0x0010,
994         0x0011,
995         0x0012,
996         0x0014,
997         0x0015,
998         0x0016,
999         0x0017,
1000         0x0018,
1001         0x0019,
1002 #endif
1003         0x0000                          /* null */
1004 };
1005
1006 MonoArray *
1007 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
1008 {
1009         MonoError error;
1010         MonoArray *chars;
1011         MonoDomain *domain;
1012         int i, n;
1013
1014         domain = mono_domain_get ();
1015         n = sizeof (invalid_path_chars) / sizeof (gunichar2);
1016         chars = mono_array_new_checked (domain, mono_defaults.char_class, n, &error);
1017         if (mono_error_set_pending_exception (&error))
1018                 return NULL;
1019
1020         for (i = 0; i < n; ++ i)
1021                 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
1022         
1023         return chars;
1024 }
1025
1026 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
1027                                       gint64 length, gint32 *error)
1028 {
1029         *error=ERROR_SUCCESS;
1030         mono_w32file_lock (handle, position, length, error);
1031 }
1032
1033 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
1034                                         gint64 length, gint32 *error)
1035 {
1036         *error=ERROR_SUCCESS;
1037         mono_w32file_unlock (handle, position, length, error);
1038 }
1039
1040 //Support for io-layer free mmap'd files.
1041
1042 #if defined (TARGET_IOS) || defined (TARGET_ANDROID)
1043
1044 gint64
1045 mono_filesize_from_path (MonoString *string)
1046 {
1047         MonoError error;
1048         struct stat buf;
1049         gint64 res;
1050         char *path = mono_string_to_utf8_checked (string, &error);
1051         mono_error_raise_exception (&error); /* OK to throw, external only without a good alternative */
1052
1053         gint stat_res;
1054         MONO_ENTER_GC_SAFE;
1055         stat_res = stat (path, &buf);
1056         MONO_EXIT_GC_SAFE;
1057         if (stat_res == -1)
1058                 res = -1;
1059         else
1060                 res = (gint64)buf.st_size;
1061
1062         g_free (path);
1063
1064         return res;
1065 }
1066
1067 gint64
1068 mono_filesize_from_fd (int fd)
1069 {
1070         struct stat buf;
1071         int res;
1072
1073         MONO_ENTER_GC_SAFE;
1074         res = fstat (fd, &buf);
1075         MONO_EXIT_GC_SAFE;
1076         
1077         if (res == -1)
1078                 return (gint64)-1;
1079
1080         return (gint64)buf.st_size;
1081 }
1082
1083 #endif
1084
1085 #ifndef HOST_WIN32
1086 void mono_w32handle_dump (void);
1087
1088 void ves_icall_System_IO_MonoIO_DumpHandles (void)
1089 {
1090         mono_w32handle_dump ();
1091 }
1092 #endif /* !HOST_WIN32 */