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