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