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