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