[reflection] Use coop handles for MonoMethod icalls (#4272)
[mono.git] / mono / metadata / file-io.c
1 /*
2  * file-io.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/file-io.h>
32 #include <mono/metadata/file-io-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         guint64 ticks = filetime->dwHighDateTime;
176         ticks <<= 32;
177         ticks += filetime->dwLowDateTime;
178         return (gint64)ticks;
179 }
180
181 static void convert_win32_file_attribute_data (const WIN32_FILE_ATTRIBUTE_DATA *data, MonoIOStat *stat)
182 {
183         stat->attributes = data->dwFileAttributes;
184         stat->creation_time = convert_filetime (&data->ftCreationTime);
185         stat->last_access_time = convert_filetime (&data->ftLastAccessTime);
186         stat->last_write_time = convert_filetime (&data->ftLastWriteTime);
187         stat->length = ((gint64)data->nFileSizeHigh << 32) | data->nFileSizeLow;
188 }
189
190 /* Managed file attributes have nearly but not quite the same values
191  * as the w32 equivalents.
192  */
193 static guint32 convert_attrs(MonoFileAttributes attrs)
194 {
195         if(attrs & FileAttributes_Encrypted) {
196                 attrs = (MonoFileAttributes)(attrs | FILE_ATTRIBUTE_ENCRYPTED);
197         }
198         
199         return(attrs);
200 }
201
202 /*
203  * On Win32, GetFileAttributes|Ex () seems to try opening the file,
204  * which might lead to sharing violation errors, whereas FindFirstFile
205  * always succeeds. These 2 wrappers resort to FindFirstFile if
206  * GetFileAttributes|Ex () has failed.
207  */
208 static guint32
209 get_file_attributes (const gunichar2 *path)
210 {
211         guint32 res;
212         WIN32_FIND_DATA find_data;
213         HANDLE find_handle;
214         gint32 error;
215
216         res = GetFileAttributes (path);
217         if (res != -1)
218                 return res;
219
220         error = GetLastError ();
221
222         if (error != ERROR_SHARING_VIOLATION)
223                 return res;
224
225         find_handle = FindFirstFile (path, &find_data);
226
227         if (find_handle == INVALID_HANDLE_VALUE)
228                 return res;
229
230         FindClose (find_handle);
231
232         return find_data.dwFileAttributes;
233 }
234
235 static gboolean
236 get_file_attributes_ex (const gunichar2 *path, WIN32_FILE_ATTRIBUTE_DATA *data)
237 {
238         gboolean res;
239         WIN32_FIND_DATA find_data;
240         HANDLE find_handle;
241         gint32 error;
242
243         res = GetFileAttributesEx (path, GetFileExInfoStandard, data);
244         if (res)
245                 return TRUE;
246
247         error = GetLastError ();
248
249         if (error != ERROR_SHARING_VIOLATION)
250                 return FALSE;
251
252         find_handle = FindFirstFile (path, &find_data);
253
254         if (find_handle == INVALID_HANDLE_VALUE)
255                 return FALSE;
256
257         FindClose (find_handle);
258
259         data->dwFileAttributes = find_data.dwFileAttributes;
260         data->ftCreationTime = find_data.ftCreationTime;
261         data->ftLastAccessTime = find_data.ftLastAccessTime;
262         data->ftLastWriteTime = find_data.ftLastWriteTime;
263         data->nFileSizeHigh = find_data.nFileSizeHigh;
264         data->nFileSizeLow = find_data.nFileSizeLow;
265         
266         return TRUE;
267 }
268
269 /* System.IO.MonoIO internal calls */
270
271 MonoBoolean
272 ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error)
273 {
274         gboolean ret;
275         MONO_ENTER_GC_SAFE;
276         
277         *error=ERROR_SUCCESS;
278         
279         ret=CreateDirectory (mono_string_chars (path), NULL);
280         if(ret==FALSE) {
281                 *error=GetLastError ();
282         }
283
284         MONO_EXIT_GC_SAFE;
285         return(ret);
286 }
287
288 MonoBoolean
289 ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error)
290 {
291         gboolean ret;
292         MONO_ENTER_GC_SAFE;
293         
294         *error=ERROR_SUCCESS;
295         
296         ret=RemoveDirectory (mono_string_chars (path));
297         if(ret==FALSE) {
298                 *error=GetLastError ();
299         }
300
301         MONO_EXIT_GC_SAFE;
302         return(ret);
303 }
304
305 static gchar *
306 get_search_dir (const gunichar2 *pattern)
307 {
308         gchar *p;
309         gchar *result;
310
311         p = g_utf16_to_utf8 (pattern, -1, NULL, NULL, NULL);
312         result = g_path_get_dirname (p);
313         g_free (p);
314         return result;
315 }
316
317 static GPtrArray *
318 get_filesystem_entries (const gunichar2 *path,
319                                                  const gunichar2 *path_with_pattern,
320                                                  gint attrs, gint mask,
321                                                  gint32 *error)
322 {
323         int i;
324         WIN32_FIND_DATA data;
325         HANDLE find_handle;
326         GPtrArray *names = NULL;
327         gchar *utf8_path = NULL, *utf8_result, *full_name;
328         gint32 attributes;
329
330         mask = convert_attrs ((MonoFileAttributes)mask);
331         attributes = get_file_attributes (path);
332         if (attributes != -1) {
333                 if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
334                         *error = ERROR_INVALID_NAME;
335                         goto fail;
336                 }
337         } else {
338                 *error = GetLastError ();
339                 goto fail;
340         }
341         
342         find_handle = FindFirstFile (path_with_pattern, &data);
343         if (find_handle == INVALID_HANDLE_VALUE) {
344                 gint32 find_error = GetLastError ();
345                 
346                 if (find_error == ERROR_FILE_NOT_FOUND || find_error == ERROR_NO_MORE_FILES) {
347                         /* No files, so just return an empty array */
348                         goto fail;
349                 }
350                 
351                 *error = find_error;
352                 goto fail;
353         }
354
355         utf8_path = get_search_dir (path_with_pattern);
356         names = g_ptr_array_new ();
357
358         do {
359                 if ((data.cFileName[0] == '.' && data.cFileName[1] == 0) ||
360                     (data.cFileName[0] == '.' && data.cFileName[1] == '.' && data.cFileName[2] == 0)) {
361                         continue;
362                 }
363                 
364                 if ((data.dwFileAttributes & mask) == attrs) {
365                         utf8_result = g_utf16_to_utf8 (data.cFileName, -1, NULL, NULL, NULL);
366                         if (utf8_result == NULL) {
367                                 continue;
368                         }
369                         
370                         full_name = g_build_filename (utf8_path, utf8_result, NULL);
371                         g_ptr_array_add (names, full_name);
372
373                         g_free (utf8_result);
374                 }
375         } while(FindNextFile (find_handle, &data));
376
377         if (FindClose (find_handle) == FALSE) {
378                 *error = GetLastError ();
379                 goto fail;
380         }
381
382         g_free (utf8_path);
383         return names;
384 fail:
385         if (names) {
386                 for (i = 0; i < names->len; i++)
387                         g_free (g_ptr_array_index (names, i));
388                 g_ptr_array_free (names, TRUE);
389         }
390         g_free (utf8_path);
391         return FALSE;
392 }
393
394
395 MonoArray *
396 ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path,
397                                                  MonoString *path_with_pattern,
398                                                  gint attrs, gint mask,
399                                                  gint32 *ioerror)
400 {
401         MonoError error;
402         MonoDomain *domain = mono_domain_get ();
403         MonoArray *result;
404         int i;
405         GPtrArray *names;
406         
407         *ioerror = ERROR_SUCCESS;
408
409         MONO_ENTER_GC_SAFE;
410         names = get_filesystem_entries (mono_string_chars (path), mono_string_chars (path_with_pattern), attrs, mask, ioerror);
411         MONO_EXIT_GC_SAFE;
412
413         if (!names) {
414                 // If there's no array and no error, then return an empty array.
415                 if (*ioerror == ERROR_SUCCESS) {
416                         MonoArray *arr = mono_array_new_checked (domain, mono_defaults.string_class, 0, &error);
417                         mono_error_set_pending_exception (&error);
418                         return arr;
419                 }
420                 return NULL;
421         }
422
423         result = mono_array_new_checked (domain, mono_defaults.string_class, names->len, &error);
424         if (mono_error_set_pending_exception (&error))
425                 goto leave;
426         for (i = 0; i < names->len; i++) {
427                 mono_array_setref (result, i, mono_string_new (domain, (const char *)g_ptr_array_index (names, i)));
428                 g_free (g_ptr_array_index (names, i));
429         }
430 leave:
431         g_ptr_array_free (names, TRUE);
432         return result;
433 }
434
435 typedef struct {
436         MonoDomain *domain;
437         gchar *utf8_path;
438         HANDLE find_handle;
439 } IncrementalFind;
440         
441 static gboolean
442 incremental_find_check_match (IncrementalFind *handle, WIN32_FIND_DATA *data, MonoString **result)
443 {
444         gchar *utf8_result;
445         gchar *full_name;
446         
447         if ((data->cFileName[0] == '.' && data->cFileName[1] == 0) || (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0))
448                 return FALSE;
449
450         utf8_result = g_utf16_to_utf8 (data->cFileName, -1, NULL, NULL, NULL);
451         if (utf8_result == NULL) 
452                 return FALSE;
453         
454         full_name = g_build_filename (handle->utf8_path, utf8_result, NULL);
455         g_free (utf8_result);
456         *result = mono_string_new (mono_domain_get (), full_name);
457         g_free (full_name);
458         
459         return TRUE;
460 }
461
462 HANDLE
463 ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
464 {
465         HANDLE hnd;
466         WIN32_FIND_DATA data;
467         MonoError error;
468
469         hnd = FindFirstFile (mono_string_chars (path_with_pattern), &data);
470
471         if (hnd == INVALID_HANDLE_VALUE) {
472                 *file_name = NULL;
473                 *file_attr = 0;
474                 *ioerror = GetLastError ();
475                 return hnd;
476         }
477
478         mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
479         mono_error_set_pending_exception (&error);
480
481         *file_attr = data.dwFileAttributes;
482         *ioerror = ERROR_SUCCESS;
483
484         return hnd;
485 }
486
487 MonoBoolean
488 ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
489 {
490         MonoBoolean res;
491         WIN32_FIND_DATA data;
492         MonoError error;
493
494         res = FindNextFile (hnd, &data);
495
496         if (res == FALSE) {
497                 *file_name = NULL;
498                 *file_attr = 0;
499                 *ioerror = GetLastError ();
500                 return res;
501         }
502
503         mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
504         mono_error_set_pending_exception (&error);
505
506         *file_attr = data.dwFileAttributes;
507         *ioerror = ERROR_SUCCESS;
508
509         return res;
510 }
511
512 MonoBoolean
513 ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd)
514 {
515         return FindClose (hnd);
516 }
517
518 /* FIXME make gc suspendable */
519 MonoString *
520 ves_icall_System_IO_MonoIO_FindFirst (MonoString *path,
521                                       MonoString *path_with_pattern,
522                                       gint32 *result_attr, gint32 *ioerror,
523                                       gpointer *handle)
524 {
525         MonoError error;
526         WIN32_FIND_DATA data;
527         HANDLE find_handle;
528         IncrementalFind *ifh;
529         MonoString *result;
530         
531         *ioerror = ERROR_SUCCESS;
532         
533         find_handle = FindFirstFile (mono_string_chars (path_with_pattern), &data);
534         
535         if (find_handle == INVALID_HANDLE_VALUE) {
536                 gint32 find_error = GetLastError ();
537                 *handle = NULL;
538                 
539                 if (find_error == ERROR_FILE_NOT_FOUND) 
540                         return NULL;
541                 
542                 *ioerror = find_error;
543                 return NULL;
544         }
545
546         ifh = g_new (IncrementalFind, 1);
547         ifh->find_handle = find_handle;
548         ifh->utf8_path = mono_string_to_utf8_checked (path, &error);
549         if (mono_error_set_pending_exception (&error)) {
550                 MONO_ENTER_GC_SAFE;
551                 FindClose (find_handle);
552                 MONO_EXIT_GC_SAFE;
553                 g_free (ifh);
554                 return NULL;
555         }
556         ifh->domain = mono_domain_get ();
557         *handle = ifh;
558
559         while (incremental_find_check_match (ifh, &data, &result) == 0){
560                 if (FindNextFile (find_handle, &data) == FALSE){
561                         int e = GetLastError ();
562                         if (e != ERROR_NO_MORE_FILES)
563                                 *ioerror = e;
564                         return NULL;
565                 }
566         }
567         *result_attr = data.dwFileAttributes;
568         
569         return result;
570 }
571
572 /* FIXME make gc suspendable */
573 MonoString *
574 ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *error)
575 {
576         IncrementalFind *ifh = (IncrementalFind *)handle;
577         WIN32_FIND_DATA data;
578         MonoString *result;
579
580         *error = ERROR_SUCCESS;
581         do {
582                 if (FindNextFile (ifh->find_handle, &data) == FALSE){
583                         int e = GetLastError ();
584                         if (e != ERROR_NO_MORE_FILES)
585                                 *error = e;
586                         return NULL;
587                 }
588         } while (incremental_find_check_match (ifh, &data, &result) == 0);
589
590         *result_attr = data.dwFileAttributes;
591         return result;
592 }
593
594 int
595 ves_icall_System_IO_MonoIO_FindClose (gpointer handle)
596 {
597         IncrementalFind *ifh = (IncrementalFind *)handle;
598         gint32 error;
599
600         MONO_ENTER_GC_SAFE;
601         if (FindClose (ifh->find_handle) == FALSE){
602                 error = GetLastError ();
603         } else
604                 error = ERROR_SUCCESS;
605         g_free (ifh->utf8_path);
606         g_free (ifh);
607         MONO_EXIT_GC_SAFE;
608
609         return error;
610 }
611
612 MonoString *
613 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error)
614 {
615         MonoError error;
616         MonoString *result;
617         gunichar2 *buf;
618         int len, res_len;
619
620         len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/
621         buf = g_new (gunichar2, len);
622         
623         mono_error_init (&error);
624         *io_error=ERROR_SUCCESS;
625         result = NULL;
626
627         res_len = GetCurrentDirectory (len, buf);
628         if (res_len > len) { /*buf is too small.*/
629                 int old_res_len = res_len;
630                 g_free (buf);
631                 buf = g_new (gunichar2, res_len);
632                 res_len = GetCurrentDirectory (res_len, buf) == old_res_len;
633         }
634         
635         if (res_len) {
636                 len = 0;
637                 while (buf [len])
638                         ++ len;
639
640                 result = mono_string_new_utf16_checked (mono_domain_get (), buf, len, &error);
641         } else {
642                 *io_error=GetLastError ();
643         }
644
645         g_free (buf);
646         mono_error_set_pending_exception (&error);
647         return result;
648 }
649
650 MonoBoolean
651 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
652                                                 gint32 *error)
653 {
654         gboolean ret;
655         
656         *error=ERROR_SUCCESS;
657         
658         ret=SetCurrentDirectory (mono_string_chars (path));
659         if(ret==FALSE) {
660                 *error=GetLastError ();
661         }
662         
663         return(ret);
664 }
665
666 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
667 gboolean
668 mono_file_io_move_file (gunichar2 *path, gunichar2 *dest, gint32 *error)
669 {
670         gboolean result = FALSE;
671         MONO_ENTER_GC_SAFE;
672
673         result = MoveFile (path, dest);
674         if (result == FALSE) {
675                 *error=GetLastError ();
676         }
677
678         MONO_EXIT_GC_SAFE;
679         return result;
680 }
681 #endif /* HAVE_CLASSIC_WINAPI_SUPPORT */
682
683 MonoBoolean
684 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, gint32 *error)
685 {
686         *error=ERROR_SUCCESS;
687         return mono_file_io_move_file (mono_string_chars (path), mono_string_chars (dest), error);
688 }
689
690 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
691 gboolean
692 mono_file_io_replace_file (gunichar2 *destinationFileName, gunichar2 *sourceFileName,
693                            gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error)
694 {
695         gboolean result = FALSE;
696         MONO_ENTER_GC_SAFE;
697
698         result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL);
699         if (result == FALSE) {
700                 *error=GetLastError ();
701         }
702
703         MONO_EXIT_GC_SAFE;
704         return result;
705 }
706 #endif /* HAVE_CLASSIC_WINAPI_SUPPORT */
707
708 MonoBoolean
709 ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName,
710                                         MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors,
711                                         gint32 *error)
712 {
713         gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL;
714         guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH;
715
716         if (sourceFileName)
717                 utf16_sourceFileName = mono_string_chars (sourceFileName);
718         if (destinationFileName)
719                 utf16_destinationFileName = mono_string_chars (destinationFileName);
720         if (destinationBackupFileName)
721                 utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName);
722
723         *error = ERROR_SUCCESS;
724         if (ignoreMetadataErrors)
725                 replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS;
726
727         /* FIXME: source and destination file names must not be NULL, but apparently they might be! */
728         return mono_file_io_replace_file (utf16_destinationFileName, utf16_sourceFileName,
729                                           utf16_destinationBackupFileName, replaceFlags, error);
730 }
731
732 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
733 gboolean
734 mono_file_io_copy_file (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error)
735 {
736         gboolean result = FALSE;
737         MONO_ENTER_GC_SAFE;
738
739         result = CopyFile (path, dest, !overwrite);
740         if (result == FALSE) {
741                 *error=GetLastError ();
742         }
743
744         MONO_EXIT_GC_SAFE;
745         return result;
746 }
747 #endif /* HAVE_CLASSIC_WINAPI_SUPPORT */
748
749 MonoBoolean
750 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
751                                      MonoBoolean overwrite, gint32 *error)
752 {
753         *error=ERROR_SUCCESS;
754         return mono_file_io_copy_file (mono_string_chars (path), mono_string_chars (dest), overwrite, error);
755 }
756
757 MonoBoolean
758 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
759 {
760         gboolean ret;
761         MONO_ENTER_GC_SAFE;
762         
763         *error=ERROR_SUCCESS;
764         
765         ret=DeleteFile (mono_string_chars (path));
766         if(ret==FALSE) {
767                 *error=GetLastError ();
768         }
769         
770         MONO_EXIT_GC_SAFE;
771         return(ret);
772 }
773
774 gint32 
775 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
776 {
777         gint32 ret;
778         MONO_ENTER_GC_SAFE;
779
780         *error=ERROR_SUCCESS;
781         
782         ret=get_file_attributes (mono_string_chars (path));
783
784         /* 
785          * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
786          * headers is wrong, hence this temporary workaround.
787          * See
788          * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
789          */
790         if (ret==-1) {
791           /* if(ret==INVALID_FILE_ATTRIBUTES) { */
792                 *error=GetLastError ();
793         }
794         
795         MONO_EXIT_GC_SAFE;
796         return(ret);
797 }
798
799 MonoBoolean
800 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
801                                               gint32 *error)
802 {
803         gboolean ret;
804         MONO_ENTER_GC_SAFE;
805         
806         *error=ERROR_SUCCESS;
807         
808         ret=SetFileAttributes (mono_string_chars (path),
809                 convert_attrs ((MonoFileAttributes)attrs));
810         if(ret==FALSE) {
811                 *error=GetLastError ();
812         }
813
814         MONO_EXIT_GC_SAFE;
815         return(ret);
816 }
817
818 gint32
819 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
820 {
821         gboolean ret;
822         MONO_ENTER_GC_SAFE;
823
824         *error=ERROR_SUCCESS;
825         
826         ret=GetFileType (handle);
827         if(ret==FILE_TYPE_UNKNOWN) {
828                 /* Not necessarily an error, but the caller will have
829                  * to decide based on the error value.
830                  */
831                 *error=GetLastError ();
832         }
833         
834         MONO_EXIT_GC_SAFE;
835         return(ret);
836 }
837
838 MonoBoolean
839 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
840                                         gint32 *error)
841 {
842         gboolean result;
843         WIN32_FILE_ATTRIBUTE_DATA data;
844         MONO_ENTER_GC_SAFE;
845
846         *error=ERROR_SUCCESS;
847         
848         result = get_file_attributes_ex (mono_string_chars (path), &data);
849
850         if (result) {
851                 convert_win32_file_attribute_data (&data, stat);
852         } else {
853                 *error=GetLastError ();
854                 memset (stat, 0, sizeof (MonoIOStat));
855         }
856
857         MONO_EXIT_GC_SAFE;
858         return result;
859 }
860
861 HANDLE 
862 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
863                                  gint32 access_mode, gint32 share, gint32 options,
864                                  gint32 *error)
865 {
866         HANDLE ret;
867         int attributes, attrs;
868         gunichar2 *chars;
869         MONO_ENTER_GC_SAFE;
870
871         chars = mono_string_chars (filename);   
872         *error=ERROR_SUCCESS;
873
874         if (options != 0){
875                 if (options & FileOptions_Encrypted)
876                         attributes = FILE_ATTRIBUTE_ENCRYPTED;
877                 else
878                         attributes = FILE_ATTRIBUTE_NORMAL;
879                 if (options & FileOptions_DeleteOnClose)
880                         attributes |= FILE_FLAG_DELETE_ON_CLOSE;
881                 if (options & FileOptions_SequentialScan)
882                         attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
883                 if (options & FileOptions_RandomAccess)
884                         attributes |= FILE_FLAG_RANDOM_ACCESS;
885
886                 if (options & FileOptions_Temporary)
887                         attributes |= FILE_ATTRIBUTE_TEMPORARY;
888                 
889                 if (options & FileOptions_WriteThrough)
890                         attributes |= FILE_FLAG_WRITE_THROUGH;
891         } else
892                 attributes = FILE_ATTRIBUTE_NORMAL;
893
894         /* If we're opening a directory we need to set the extra flag
895          */
896         attrs = get_file_attributes (chars);
897         if (attrs != INVALID_FILE_ATTRIBUTES) {
898                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
899                         attributes |= FILE_FLAG_BACKUP_SEMANTICS;
900                 }
901         }
902         
903         ret=CreateFile (chars, convert_access ((MonoFileAccess)access_mode),
904                         convert_share ((MonoFileShare)share), NULL, convert_mode ((MonoFileMode)mode),
905                         attributes, NULL);
906         if(ret==INVALID_HANDLE_VALUE) {
907                 *error=GetLastError ();
908         } 
909         
910         MONO_EXIT_GC_SAFE;
911         return(ret);
912 }
913
914 MonoBoolean
915 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
916 {
917         gboolean ret;
918         MONO_ENTER_GC_SAFE;
919
920         *error=ERROR_SUCCESS;
921         
922         ret=CloseHandle (handle);
923         if(ret==FALSE) {
924                 *error=GetLastError ();
925         }
926         
927         MONO_EXIT_GC_SAFE;
928         return(ret);
929 }
930
931 gint32 
932 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
933                                  gint32 dest_offset, gint32 count,
934                                  gint32 *error)
935 {
936         guchar *buffer;
937         gboolean result;
938         guint32 n;
939
940         *error=ERROR_SUCCESS;
941
942         MONO_CHECK_ARG_NULL (dest, 0);
943
944         if (dest_offset > mono_array_length (dest) - count) {
945                 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
946                 return 0;
947         }
948
949         buffer = mono_array_addr (dest, guchar, dest_offset);
950
951         MONO_ENTER_GC_SAFE;
952         result = ReadFile (handle, buffer, count, &n, NULL);
953         MONO_EXIT_GC_SAFE;
954
955         if (!result) {
956                 *error=GetLastError ();
957                 return -1;
958         }
959
960         return (gint32)n;
961 }
962
963 gint32 
964 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
965                                   gint32 src_offset, gint32 count,
966                                   gint32 *error)
967 {
968         guchar *buffer;
969         gboolean result;
970         guint32 n;
971
972         *error=ERROR_SUCCESS;
973
974         MONO_CHECK_ARG_NULL (src, 0);
975         
976         if (src_offset > mono_array_length (src) - count) {
977                 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
978                 return 0;
979         }
980         
981         buffer = mono_array_addr (src, guchar, src_offset);
982         MONO_ENTER_GC_SAFE;
983         result = WriteFile (handle, buffer, count, &n, NULL);
984         MONO_EXIT_GC_SAFE;
985
986         if (!result) {
987                 *error=GetLastError ();
988                 return -1;
989         }
990
991         return (gint32)n;
992 }
993
994 gint64 
995 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
996                                  gint32 *error)
997 {
998         gint32 offset_hi;
999         MONO_ENTER_GC_SAFE;
1000
1001         *error=ERROR_SUCCESS;
1002         
1003         offset_hi = offset >> 32;
1004         offset = SetFilePointer (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi,
1005                                  convert_seekorigin ((MonoSeekOrigin)origin));
1006
1007         if(offset==INVALID_SET_FILE_POINTER) {
1008                 *error=GetLastError ();
1009         }
1010
1011         MONO_EXIT_GC_SAFE;
1012         return offset | ((gint64)offset_hi << 32);
1013 }
1014
1015 MonoBoolean
1016 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
1017 {
1018         gboolean ret;
1019         MONO_ENTER_GC_SAFE;
1020
1021         *error=ERROR_SUCCESS;
1022         
1023         ret=FlushFileBuffers (handle);
1024         if(ret==FALSE) {
1025                 *error=GetLastError ();
1026         }
1027         
1028         MONO_EXIT_GC_SAFE;
1029         return(ret);
1030 }
1031
1032 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
1033 gint64
1034 mono_file_io_get_file_size (HANDLE handle, gint32 *error)
1035 {
1036         gint64 length;
1037         guint32 length_hi;
1038
1039         MONO_ENTER_GC_SAFE;
1040
1041         length = GetFileSize (handle, &length_hi);
1042         if(length==INVALID_FILE_SIZE) {
1043                 *error=GetLastError ();
1044         }
1045
1046         MONO_EXIT_GC_SAFE;
1047         return length | ((gint64)length_hi << 32);
1048 }
1049 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
1050
1051 gint64
1052 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
1053 {
1054         *error=ERROR_SUCCESS;
1055         return mono_file_io_get_file_size (handle, error);
1056 }
1057
1058 /* FIXME make gc suspendable */
1059 MonoBoolean
1060 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
1061                                       gint32 *error)
1062 {
1063         gint64 offset, offset_set;
1064         gint32 offset_hi;
1065         gint32 length_hi;
1066         gboolean result;
1067
1068         *error=ERROR_SUCCESS;
1069         
1070         /* save file pointer */
1071
1072         offset_hi = 0;
1073         offset = SetFilePointer (handle, 0, &offset_hi, FILE_CURRENT);
1074         if(offset==INVALID_SET_FILE_POINTER) {
1075                 *error=GetLastError ();
1076                 return(FALSE);
1077         }
1078
1079         /* extend or truncate */
1080
1081         length_hi = length >> 32;
1082         offset_set=SetFilePointer (handle, length & 0xFFFFFFFF, &length_hi,
1083                                    FILE_BEGIN);
1084         if(offset_set==INVALID_SET_FILE_POINTER) {
1085                 *error=GetLastError ();
1086                 return(FALSE);
1087         }
1088
1089         result = SetEndOfFile (handle);
1090         if(result==FALSE) {
1091                 *error=GetLastError ();
1092                 return(FALSE);
1093         }
1094
1095         /* restore file pointer */
1096
1097         offset_set=SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
1098                                    FILE_BEGIN);
1099         if(offset_set==INVALID_SET_FILE_POINTER) {
1100                 *error=GetLastError ();
1101                 return(FALSE);
1102         }
1103
1104         return result;
1105 }
1106
1107 MonoBoolean
1108 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
1109                                         gint64 last_access_time,
1110                                         gint64 last_write_time, gint32 *error)
1111 {
1112         gboolean ret;
1113         const FILETIME *creation_filetime;
1114         const FILETIME *last_access_filetime;
1115         const FILETIME *last_write_filetime;
1116         MONO_ENTER_GC_SAFE;
1117
1118         *error=ERROR_SUCCESS;
1119         
1120         if (creation_time < 0)
1121                 creation_filetime = NULL;
1122         else
1123                 creation_filetime = (FILETIME *)&creation_time;
1124
1125         if (last_access_time < 0)
1126                 last_access_filetime = NULL;
1127         else
1128                 last_access_filetime = (FILETIME *)&last_access_time;
1129
1130         if (last_write_time < 0)
1131                 last_write_filetime = NULL;
1132         else
1133                 last_write_filetime = (FILETIME *)&last_write_time;
1134
1135         ret=SetFileTime (handle, creation_filetime, last_access_filetime, last_write_filetime);
1136         if(ret==FALSE) {
1137                 *error=GetLastError ();
1138         }
1139
1140         MONO_EXIT_GC_SAFE;
1141         return(ret);
1142 }
1143
1144 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
1145 HANDLE
1146 mono_file_io_get_console_output (void)
1147 {
1148         return GetStdHandle (STD_OUTPUT_HANDLE);
1149 }
1150 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
1151
1152 HANDLE 
1153 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
1154 {
1155         return mono_file_io_get_console_output ();
1156 }
1157
1158 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
1159 HANDLE
1160 mono_file_io_get_console_input (void)
1161 {
1162         return GetStdHandle (STD_INPUT_HANDLE);
1163 }
1164 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
1165
1166 HANDLE 
1167 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
1168 {
1169         return mono_file_io_get_console_input ();
1170 }
1171
1172 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
1173 HANDLE
1174 mono_file_io_get_console_error (void)
1175 {
1176         return GetStdHandle (STD_ERROR_HANDLE);
1177 }
1178 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
1179
1180 HANDLE 
1181 ves_icall_System_IO_MonoIO_get_ConsoleError ()
1182 {
1183         return mono_file_io_get_console_error ();
1184 }
1185
1186 MonoBoolean
1187 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, HANDLE *write_handle, gint32 *error)
1188 {
1189         SECURITY_ATTRIBUTES attr;
1190         gboolean ret;
1191         
1192         attr.nLength=sizeof(SECURITY_ATTRIBUTES);
1193         attr.bInheritHandle=TRUE;
1194         attr.lpSecurityDescriptor=NULL;
1195
1196         MONO_ENTER_GC_SAFE;
1197         ret=CreatePipe (read_handle, write_handle, &attr, 0);
1198         MONO_EXIT_GC_SAFE;
1199
1200         if(ret==FALSE) {
1201                 *error = GetLastError ();
1202                 /* FIXME: throw an exception? */
1203                 return(FALSE);
1204         }
1205         
1206         return(TRUE);
1207 }
1208
1209 MonoBoolean
1210 ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle, HANDLE source_handle,
1211                 HANDLE target_process_handle, HANDLE *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error)
1212 {
1213         /* This is only used on Windows */
1214         gboolean ret;
1215         
1216         MONO_ENTER_GC_SAFE;
1217 #ifdef HOST_WIN32
1218         ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options);
1219 #else
1220         mono_w32handle_ref (source_handle);
1221         *target_handle = source_handle;
1222         ret = TRUE;
1223 #endif
1224         MONO_EXIT_GC_SAFE;
1225
1226         if(ret==FALSE) {
1227                 *error = GetLastError ();
1228                 /* FIXME: throw an exception? */
1229                 return(FALSE);
1230         }
1231         
1232         return(TRUE);
1233 }
1234
1235 #ifndef HOST_WIN32
1236 gunichar2 
1237 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
1238 {
1239         return (gunichar2) '/'; /* forward slash */
1240 }
1241
1242 gunichar2 
1243 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
1244 {
1245         return (gunichar2) '/'; /* forward slash */
1246 }
1247
1248 gunichar2 
1249 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
1250 {
1251         if (IS_PORTABILITY_SET)
1252                 return (gunichar2) '\\';        /* backslash */
1253         else
1254                 return (gunichar2) '/'; /* forward slash */
1255 }
1256
1257 gunichar2 
1258 ves_icall_System_IO_MonoIO_get_PathSeparator ()
1259 {
1260         return (gunichar2) ':'; /* colon */
1261 }
1262 #endif /* !HOST_WIN32 */
1263
1264 static const gunichar2
1265 invalid_path_chars [] = {
1266 #if defined (TARGET_WIN32)
1267         0x0022,                         /* double quote, which seems allowed in MS.NET but should be rejected */
1268         0x003c,                         /* less than */
1269         0x003e,                         /* greater than */
1270         0x007c,                         /* pipe */
1271         0x0008,
1272         0x0010,
1273         0x0011,
1274         0x0012,
1275         0x0014,
1276         0x0015,
1277         0x0016,
1278         0x0017,
1279         0x0018,
1280         0x0019,
1281 #endif
1282         0x0000                          /* null */
1283 };
1284
1285 MonoArray *
1286 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
1287 {
1288         MonoError error;
1289         MonoArray *chars;
1290         MonoDomain *domain;
1291         int i, n;
1292
1293         domain = mono_domain_get ();
1294         n = sizeof (invalid_path_chars) / sizeof (gunichar2);
1295         chars = mono_array_new_checked (domain, mono_defaults.char_class, n, &error);
1296         if (mono_error_set_pending_exception (&error))
1297                 return NULL;
1298
1299         for (i = 0; i < n; ++ i)
1300                 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
1301         
1302         return chars;
1303 }
1304
1305 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
1306 gboolean
1307 mono_file_io_lock_file (HANDLE handle, gint64 position, gint64 length, gint32 *error)
1308 {
1309         gboolean result = FALSE;
1310         MONO_ENTER_GC_SAFE;
1311
1312         result = LockFile (handle, position & 0xFFFFFFFF, position >> 32,
1313                            length & 0xFFFFFFFF, length >> 32);
1314
1315         if (result == FALSE) {
1316                 *error = GetLastError ();
1317         }
1318
1319         MONO_EXIT_GC_SAFE;
1320         return result;
1321 }
1322 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
1323
1324 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
1325                                       gint64 length, gint32 *error)
1326 {
1327         *error=ERROR_SUCCESS;
1328         mono_file_io_lock_file (handle, position, length, error);
1329 }
1330
1331 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
1332 gboolean
1333 mono_file_io_unlock_file (HANDLE handle, gint64 position, gint64 length, gint32 *error)
1334 {
1335         gboolean result = FALSE;
1336         MONO_ENTER_GC_SAFE;
1337         
1338         result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32,
1339                              length & 0xFFFFFFFF, length >> 32);
1340
1341         if (result == FALSE) {
1342                 *error = GetLastError ();
1343         }
1344
1345         MONO_EXIT_GC_SAFE;
1346         return result;
1347 }
1348 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
1349
1350 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
1351                                         gint64 length, gint32 *error)
1352 {
1353         *error=ERROR_SUCCESS;
1354         mono_file_io_unlock_file (handle, position, length, error);
1355 }
1356
1357 //Support for io-layer free mmap'd files.
1358
1359 #if defined (TARGET_IOS) || defined (TARGET_ANDROID)
1360
1361 gint64
1362 mono_filesize_from_path (MonoString *string)
1363 {
1364         MonoError error;
1365         struct stat buf;
1366         gint64 res;
1367         char *path = mono_string_to_utf8_checked (string, &error);
1368         mono_error_raise_exception (&error); /* OK to throw, external only without a good alternative */
1369
1370         MONO_ENTER_GC_SAFE;
1371         if (stat (path, &buf) == -1)
1372                 res = -1;
1373         else
1374                 res = (gint64)buf.st_size;
1375
1376         g_free (path);
1377
1378         MONO_EXIT_GC_SAFE;
1379         return res;
1380 }
1381
1382 gint64
1383 mono_filesize_from_fd (int fd)
1384 {
1385         struct stat buf;
1386         int res;
1387
1388         MONO_ENTER_GC_SAFE;
1389         res = fstat (fd, &buf);
1390         MONO_EXIT_GC_SAFE;
1391         
1392         if (res == -1)
1393                 return (gint64)-1;
1394
1395         return (gint64)buf.st_size;
1396 }
1397
1398 #endif
1399
1400 #ifndef HOST_WIN32
1401 void mono_w32handle_dump (void);
1402
1403 void ves_icall_System_IO_MonoIO_DumpHandles (void)
1404 {
1405
1406         mono_w32handle_dump ();
1407 }
1408 #endif /* !HOST_WIN32 */