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