325767f52434e9fbcb91aa60963c18d518354651
[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                 MonoString *name = mono_string_new_checked (domain, (const char *)g_ptr_array_index (names, i), &error);
409                 if (mono_error_set_pending_exception (&error))
410                         goto leave;
411                 mono_array_setref (result, i, name);
412                 g_free (g_ptr_array_index (names, i));
413         }
414 leave:
415         g_ptr_array_free (names, TRUE);
416         return result;
417 }
418
419 typedef struct {
420         MonoDomain *domain;
421         gchar *utf8_path;
422         HANDLE find_handle;
423 } IncrementalFind;
424         
425 static gboolean
426 incremental_find_check_match (IncrementalFind *handle, WIN32_FIND_DATA *data, MonoString **result, MonoError *error)
427 {
428         error_init (error);
429         gchar *utf8_result;
430         gchar *full_name;
431         
432         if ((data->cFileName[0] == '.' && data->cFileName[1] == 0) || (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0))
433                 return FALSE;
434
435         utf8_result = g_utf16_to_utf8 (data->cFileName, -1, NULL, NULL, NULL);
436         if (utf8_result == NULL) 
437                 return FALSE;
438         
439         full_name = g_build_filename (handle->utf8_path, utf8_result, NULL);
440         g_free (utf8_result);
441         *result = mono_string_new_checked (mono_domain_get (), full_name, error);
442         g_free (full_name);
443         if (!is_ok (error))
444                 return FALSE;
445         
446         return TRUE;
447 }
448
449 HANDLE
450 ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
451 {
452         HANDLE hnd;
453         WIN32_FIND_DATA data;
454         MonoError error;
455
456         hnd = mono_w32file_find_first (mono_string_chars (path_with_pattern), &data);
457
458         if (hnd == INVALID_HANDLE_VALUE) {
459                 *file_name = NULL;
460                 *file_attr = 0;
461                 *ioerror = mono_w32error_get_last ();
462                 return hnd;
463         }
464
465         mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
466         mono_error_set_pending_exception (&error);
467
468         *file_attr = data.dwFileAttributes;
469         *ioerror = ERROR_SUCCESS;
470
471         return hnd;
472 }
473
474 MonoBoolean
475 ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
476 {
477         MonoBoolean res;
478         WIN32_FIND_DATA data;
479         MonoError error;
480
481         res = mono_w32file_find_next (hnd, &data);
482
483         if (res == FALSE) {
484                 *file_name = NULL;
485                 *file_attr = 0;
486                 *ioerror = mono_w32error_get_last ();
487                 return res;
488         }
489
490         mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
491         mono_error_set_pending_exception (&error);
492
493         *file_attr = data.dwFileAttributes;
494         *ioerror = ERROR_SUCCESS;
495
496         return res;
497 }
498
499 MonoBoolean
500 ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd)
501 {
502         return mono_w32file_find_close (hnd);
503 }
504
505 /* FIXME make gc suspendable */
506 MonoString *
507 ves_icall_System_IO_MonoIO_FindFirst (MonoString *path,
508                                       MonoString *path_with_pattern,
509                                       gint32 *result_attr, gint32 *ioerror,
510                                       gpointer *handle)
511 {
512         MonoError error;
513         WIN32_FIND_DATA data;
514         HANDLE find_handle;
515         IncrementalFind *ifh;
516         MonoString *result;
517         
518         *ioerror = ERROR_SUCCESS;
519         
520         find_handle = mono_w32file_find_first (mono_string_chars (path_with_pattern), &data);
521         
522         if (find_handle == INVALID_HANDLE_VALUE) {
523                 gint32 find_error = mono_w32error_get_last ();
524                 *handle = NULL;
525                 
526                 if (find_error == ERROR_FILE_NOT_FOUND) 
527                         return NULL;
528                 
529                 *ioerror = find_error;
530                 return NULL;
531         }
532
533         ifh = g_new (IncrementalFind, 1);
534         ifh->find_handle = find_handle;
535         ifh->utf8_path = mono_string_to_utf8_checked (path, &error);
536         if (mono_error_set_pending_exception (&error)) {
537                 mono_w32file_find_close (find_handle);
538                 g_free (ifh);
539                 return NULL;
540         }
541         ifh->domain = mono_domain_get ();
542         *handle = ifh;
543
544         while (incremental_find_check_match (ifh, &data, &result, &error) == 0){
545                 if (!is_ok (&error)) {
546                         mono_error_set_pending_exception (&error);
547                         return NULL;
548                 }
549                 if (mono_w32file_find_next (find_handle, &data) == FALSE){
550                         int e = mono_w32error_get_last ();
551                         if (e != ERROR_NO_MORE_FILES)
552                                 *ioerror = e;
553                         return NULL;
554                 }
555         }
556         *result_attr = data.dwFileAttributes;
557
558         return result;
559 }
560
561 /* FIXME make gc suspendable */
562 MonoString *
563 ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *ioerror)
564 {
565         MonoError error;
566         IncrementalFind *ifh = (IncrementalFind *)handle;
567         WIN32_FIND_DATA data;
568         MonoString *result;
569
570         error_init (&error);
571         *ioerror = ERROR_SUCCESS;
572         do {
573                 if (!is_ok (&error)) {
574                         mono_error_set_pending_exception (&error);
575                         return NULL;
576                 }
577                 if (mono_w32file_find_next (ifh->find_handle, &data) == FALSE){
578                         int e = mono_w32error_get_last ();
579                         if (e != ERROR_NO_MORE_FILES)
580                                 *ioerror = e;
581                         return NULL;
582                 }
583         } while (incremental_find_check_match (ifh, &data, &result, &error) == 0);
584
585         *result_attr = data.dwFileAttributes;
586         return result;
587 }
588
589 int
590 ves_icall_System_IO_MonoIO_FindClose (gpointer handle)
591 {
592         IncrementalFind *ifh = (IncrementalFind *)handle;
593         gint32 error;
594
595         if (mono_w32file_find_close (ifh->find_handle) == FALSE){
596                 error = mono_w32error_get_last ();
597         } else
598                 error = ERROR_SUCCESS;
599         g_free (ifh->utf8_path);
600         g_free (ifh);
601
602         return error;
603 }
604
605 MonoString *
606 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error)
607 {
608         MonoError error;
609         MonoString *result;
610         gunichar2 *buf;
611         int len, res_len;
612
613         len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/
614         buf = g_new (gunichar2, len);
615         
616         error_init (&error);
617         *io_error=ERROR_SUCCESS;
618         result = NULL;
619
620         res_len = mono_w32file_get_cwd (len, buf);
621         if (res_len > len) { /*buf is too small.*/
622                 int old_res_len = res_len;
623                 g_free (buf);
624                 buf = g_new (gunichar2, res_len);
625                 res_len = mono_w32file_get_cwd (res_len, buf) == old_res_len;
626         }
627         
628         if (res_len) {
629                 len = 0;
630                 while (buf [len])
631                         ++ len;
632
633                 result = mono_string_new_utf16_checked (mono_domain_get (), buf, len, &error);
634         } else {
635                 *io_error=mono_w32error_get_last ();
636         }
637
638         g_free (buf);
639         mono_error_set_pending_exception (&error);
640         return result;
641 }
642
643 MonoBoolean
644 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
645                                                 gint32 *error)
646 {
647         gboolean ret;
648         
649         *error=ERROR_SUCCESS;
650         
651         ret=mono_w32file_set_cwd (mono_string_chars (path));
652         if(ret==FALSE) {
653                 *error=mono_w32error_get_last ();
654         }
655         
656         return(ret);
657 }
658
659 MonoBoolean
660 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, gint32 *error)
661 {
662         *error=ERROR_SUCCESS;
663         return mono_w32file_move (mono_string_chars (path), mono_string_chars (dest), error);
664 }
665
666 MonoBoolean
667 ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName,
668                                         MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors,
669                                         gint32 *error)
670 {
671         gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL;
672         guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH;
673
674         if (sourceFileName)
675                 utf16_sourceFileName = mono_string_chars (sourceFileName);
676         if (destinationFileName)
677                 utf16_destinationFileName = mono_string_chars (destinationFileName);
678         if (destinationBackupFileName)
679                 utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName);
680
681         *error = ERROR_SUCCESS;
682         if (ignoreMetadataErrors)
683                 replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS;
684
685         /* FIXME: source and destination file names must not be NULL, but apparently they might be! */
686         return mono_w32file_replace (utf16_destinationFileName, utf16_sourceFileName,
687                                           utf16_destinationBackupFileName, replaceFlags, error);
688 }
689
690 MonoBoolean
691 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
692                                      MonoBoolean overwrite, gint32 *error)
693 {
694         *error=ERROR_SUCCESS;
695         return mono_w32file_copy (mono_string_chars (path), mono_string_chars (dest), overwrite, error);
696 }
697
698 MonoBoolean
699 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
700 {
701         gboolean ret;
702         
703         *error=ERROR_SUCCESS;
704         
705         ret=mono_w32file_delete (mono_string_chars (path));
706         if(ret==FALSE) {
707                 *error=mono_w32error_get_last ();
708         }
709         
710         return(ret);
711 }
712
713 gint32 
714 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
715 {
716         gint32 ret;
717         *error=ERROR_SUCCESS;
718         
719         ret=get_file_attributes (mono_string_chars (path));
720
721         /* 
722          * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
723          * headers is wrong, hence this temporary workaround.
724          * See
725          * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
726          */
727         if (ret==-1) {
728           /* if(ret==INVALID_FILE_ATTRIBUTES) { */
729                 *error=mono_w32error_get_last ();
730         }
731         return(ret);
732 }
733
734 MonoBoolean
735 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
736                                               gint32 *error)
737 {
738         gboolean ret;
739         *error=ERROR_SUCCESS;
740         
741         ret=mono_w32file_set_attributes (mono_string_chars (path),
742                 convert_attrs ((MonoFileAttributes)attrs));
743         if(ret==FALSE) {
744                 *error=mono_w32error_get_last ();
745         }
746         return(ret);
747 }
748
749 gint32
750 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
751 {
752         gboolean ret;
753
754         *error=ERROR_SUCCESS;
755         
756         ret=mono_w32file_get_type (handle);
757         if(ret==FILE_TYPE_UNKNOWN) {
758                 /* Not necessarily an error, but the caller will have
759                  * to decide based on the error value.
760                  */
761                 *error=mono_w32error_get_last ();
762         }
763         
764         return(ret);
765 }
766
767 MonoBoolean
768 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, gint32 *error)
769 {
770         gboolean result;
771
772         *error=ERROR_SUCCESS;
773         
774         result = get_file_attributes_ex (mono_string_chars (path), stat);
775
776         if (!result) {
777                 *error=mono_w32error_get_last ();
778                 memset (stat, 0, sizeof (MonoIOStat));
779         }
780
781         return result;
782 }
783
784 HANDLE 
785 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
786                                  gint32 access_mode, gint32 share, gint32 options,
787                                  gint32 *error)
788 {
789         HANDLE ret;
790         int attributes, attrs;
791         gunichar2 *chars;
792
793         chars = mono_string_chars (filename);   
794         *error=ERROR_SUCCESS;
795
796         if (options != 0){
797                 if (options & FileOptions_Encrypted)
798                         attributes = FILE_ATTRIBUTE_ENCRYPTED;
799                 else
800                         attributes = FILE_ATTRIBUTE_NORMAL;
801                 if (options & FileOptions_DeleteOnClose)
802                         attributes |= FILE_FLAG_DELETE_ON_CLOSE;
803                 if (options & FileOptions_SequentialScan)
804                         attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
805                 if (options & FileOptions_RandomAccess)
806                         attributes |= FILE_FLAG_RANDOM_ACCESS;
807
808                 if (options & FileOptions_Temporary)
809                         attributes |= FILE_ATTRIBUTE_TEMPORARY;
810                 
811                 if (options & FileOptions_WriteThrough)
812                         attributes |= FILE_FLAG_WRITE_THROUGH;
813         } else
814                 attributes = FILE_ATTRIBUTE_NORMAL;
815
816         /* If we're opening a directory we need to set the extra flag
817          */
818         attrs = get_file_attributes (chars);
819         if (attrs != INVALID_FILE_ATTRIBUTES) {
820                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
821                         attributes |= FILE_FLAG_BACKUP_SEMANTICS;
822                 }
823         }
824         
825         ret=mono_w32file_create (chars, convert_access ((MonoFileAccess)access_mode), convert_share ((MonoFileShare)share), convert_mode ((MonoFileMode)mode), attributes);
826         if(ret==INVALID_HANDLE_VALUE) {
827                 *error=mono_w32error_get_last ();
828         } 
829         
830         return(ret);
831 }
832
833 MonoBoolean
834 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
835 {
836         gboolean ret;
837         *error=ERROR_SUCCESS;
838         
839         ret=mono_w32file_close (handle);
840         if(ret==FALSE) {
841                 *error=mono_w32error_get_last ();
842         }
843         return(ret);
844 }
845
846 gint32 
847 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
848                                  gint32 dest_offset, gint32 count,
849                                  gint32 *error)
850 {
851         guchar *buffer;
852         gboolean result;
853         guint32 n;
854
855         *error=ERROR_SUCCESS;
856
857         MONO_CHECK_ARG_NULL (dest, 0);
858
859         if (dest_offset > mono_array_length (dest) - count) {
860                 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
861                 return 0;
862         }
863
864         buffer = mono_array_addr (dest, guchar, dest_offset);
865
866         result = mono_w32file_read (handle, buffer, count, &n);
867
868         if (!result) {
869                 *error=mono_w32error_get_last ();
870                 return -1;
871         }
872
873         return (gint32)n;
874 }
875
876 gint32 
877 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
878                                   gint32 src_offset, gint32 count,
879                                   gint32 *error)
880 {
881         guchar *buffer;
882         gboolean result;
883         guint32 n;
884
885         *error=ERROR_SUCCESS;
886
887         MONO_CHECK_ARG_NULL (src, 0);
888         
889         if (src_offset > mono_array_length (src) - count) {
890                 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
891                 return 0;
892         }
893         
894         buffer = mono_array_addr (src, guchar, src_offset);
895         result = mono_w32file_write (handle, buffer, count, &n);
896
897         if (!result) {
898                 *error=mono_w32error_get_last ();
899                 return -1;
900         }
901
902         return (gint32)n;
903 }
904
905 gint64 
906 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
907                                  gint32 *error)
908 {
909         gint32 offset_hi;
910
911         *error=ERROR_SUCCESS;
912         
913         offset_hi = offset >> 32;
914         offset = mono_w32file_seek (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi,
915                                  convert_seekorigin ((MonoSeekOrigin)origin));
916
917         if(offset==INVALID_SET_FILE_POINTER) {
918                 *error=mono_w32error_get_last ();
919         }
920
921         return offset | ((gint64)offset_hi << 32);
922 }
923
924 MonoBoolean
925 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
926 {
927         gboolean ret;
928
929         *error=ERROR_SUCCESS;
930         
931         ret=mono_w32file_flush (handle);
932         if(ret==FALSE) {
933                 *error=mono_w32error_get_last ();
934         }
935         
936         return(ret);
937 }
938
939 gint64
940 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
941 {
942         *error=ERROR_SUCCESS;
943         return mono_w32file_get_file_size (handle, error);
944 }
945
946 /* FIXME make gc suspendable */
947 MonoBoolean
948 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
949                                       gint32 *error)
950 {
951         gint64 offset, offset_set;
952         gint32 offset_hi;
953         gint32 length_hi;
954         gboolean result;
955
956         *error=ERROR_SUCCESS;
957         
958         /* save file pointer */
959
960         offset_hi = 0;
961         offset = mono_w32file_seek (handle, 0, &offset_hi, FILE_CURRENT);
962         if(offset==INVALID_SET_FILE_POINTER) {
963                 *error=mono_w32error_get_last ();
964                 return(FALSE);
965         }
966
967         /* extend or truncate */
968
969         length_hi = length >> 32;
970         offset_set=mono_w32file_seek (handle, length & 0xFFFFFFFF, &length_hi,
971                                    FILE_BEGIN);
972         if(offset_set==INVALID_SET_FILE_POINTER) {
973                 *error=mono_w32error_get_last ();
974                 return(FALSE);
975         }
976
977         result = mono_w32file_truncate (handle);
978         if(result==FALSE) {
979                 *error=mono_w32error_get_last ();
980                 return(FALSE);
981         }
982
983         /* restore file pointer */
984
985         offset_set=mono_w32file_seek (handle, offset & 0xFFFFFFFF, &offset_hi,
986                                    FILE_BEGIN);
987         if(offset_set==INVALID_SET_FILE_POINTER) {
988                 *error=mono_w32error_get_last ();
989                 return(FALSE);
990         }
991
992         return result;
993 }
994
995 MonoBoolean
996 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
997                                         gint64 last_access_time,
998                                         gint64 last_write_time, gint32 *error)
999 {
1000         gboolean ret;
1001         const FILETIME *creation_filetime;
1002         const FILETIME *access_filetime;
1003         const FILETIME *write_filetime;
1004
1005         *error=ERROR_SUCCESS;
1006         
1007         if (creation_time < 0)
1008                 creation_filetime = NULL;
1009         else
1010                 creation_filetime = (FILETIME *)&creation_time;
1011
1012         if (last_access_time < 0)
1013                 access_filetime = NULL;
1014         else
1015                 access_filetime = (FILETIME *)&last_access_time;
1016
1017         if (last_write_time < 0)
1018                 write_filetime = NULL;
1019         else
1020                 write_filetime = (FILETIME *)&last_write_time;
1021
1022         ret=mono_w32file_set_times (handle, creation_filetime, access_filetime, write_filetime);
1023         if(ret==FALSE) {
1024                 *error=mono_w32error_get_last ();
1025         }
1026
1027         return(ret);
1028 }
1029
1030 HANDLE 
1031 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
1032 {
1033         return mono_w32file_get_console_output ();
1034 }
1035
1036 HANDLE 
1037 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
1038 {
1039         return mono_w32file_get_console_input ();
1040 }
1041
1042 HANDLE 
1043 ves_icall_System_IO_MonoIO_get_ConsoleError ()
1044 {
1045         return mono_w32file_get_console_error ();
1046 }
1047
1048 MonoBoolean
1049 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, HANDLE *write_handle, gint32 *error)
1050 {
1051         gboolean ret;
1052
1053         ret=mono_w32file_create_pipe (read_handle, write_handle, 0);
1054
1055         if(ret==FALSE) {
1056                 *error = mono_w32error_get_last ();
1057                 /* FIXME: throw an exception? */
1058                 return(FALSE);
1059         }
1060         
1061         return(TRUE);
1062 }
1063
1064 MonoBoolean
1065 ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle, HANDLE source_handle,
1066                 HANDLE target_process_handle, HANDLE *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error)
1067 {
1068 #ifndef HOST_WIN32
1069         *target_handle = mono_w32handle_duplicate (source_handle);
1070 #else
1071         gboolean ret;
1072
1073         MONO_ENTER_GC_SAFE;
1074         ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options);
1075         MONO_EXIT_GC_SAFE;
1076
1077         if (!ret) {
1078                 *error = mono_w32error_get_last ();
1079                 /* FIXME: throw an exception? */
1080                 return(FALSE);
1081         }
1082 #endif
1083
1084         return(TRUE);
1085 }
1086
1087 #ifndef HOST_WIN32
1088 gunichar2 
1089 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
1090 {
1091         return (gunichar2) '/'; /* forward slash */
1092 }
1093
1094 gunichar2 
1095 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
1096 {
1097         return (gunichar2) '/'; /* forward slash */
1098 }
1099
1100 gunichar2 
1101 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
1102 {
1103         if (IS_PORTABILITY_SET)
1104                 return (gunichar2) '\\';        /* backslash */
1105         else
1106                 return (gunichar2) '/'; /* forward slash */
1107 }
1108
1109 gunichar2 
1110 ves_icall_System_IO_MonoIO_get_PathSeparator ()
1111 {
1112         return (gunichar2) ':'; /* colon */
1113 }
1114 #endif /* !HOST_WIN32 */
1115
1116 static const gunichar2
1117 invalid_path_chars [] = {
1118 #if defined (TARGET_WIN32)
1119         0x0022,                         /* double quote, which seems allowed in MS.NET but should be rejected */
1120         0x003c,                         /* less than */
1121         0x003e,                         /* greater than */
1122         0x007c,                         /* pipe */
1123         0x0008,
1124         0x0010,
1125         0x0011,
1126         0x0012,
1127         0x0014,
1128         0x0015,
1129         0x0016,
1130         0x0017,
1131         0x0018,
1132         0x0019,
1133 #endif
1134         0x0000                          /* null */
1135 };
1136
1137 MonoArray *
1138 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
1139 {
1140         MonoError error;
1141         MonoArray *chars;
1142         MonoDomain *domain;
1143         int i, n;
1144
1145         domain = mono_domain_get ();
1146         n = sizeof (invalid_path_chars) / sizeof (gunichar2);
1147         chars = mono_array_new_checked (domain, mono_defaults.char_class, n, &error);
1148         if (mono_error_set_pending_exception (&error))
1149                 return NULL;
1150
1151         for (i = 0; i < n; ++ i)
1152                 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
1153         
1154         return chars;
1155 }
1156
1157 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
1158                                       gint64 length, gint32 *error)
1159 {
1160         *error=ERROR_SUCCESS;
1161         mono_w32file_lock (handle, position, length, error);
1162 }
1163
1164 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
1165                                         gint64 length, gint32 *error)
1166 {
1167         *error=ERROR_SUCCESS;
1168         mono_w32file_unlock (handle, position, length, error);
1169 }
1170
1171 //Support for io-layer free mmap'd files.
1172
1173 #if defined (TARGET_IOS) || defined (TARGET_ANDROID)
1174
1175 gint64
1176 mono_filesize_from_path (MonoString *string)
1177 {
1178         MonoError error;
1179         struct stat buf;
1180         gint64 res;
1181         char *path = mono_string_to_utf8_checked (string, &error);
1182         mono_error_raise_exception (&error); /* OK to throw, external only without a good alternative */
1183
1184         gint stat_res;
1185         MONO_ENTER_GC_SAFE;
1186         stat_res = stat (path, &buf);
1187         MONO_EXIT_GC_SAFE;
1188         if (stat_res == -1)
1189                 res = -1;
1190         else
1191                 res = (gint64)buf.st_size;
1192
1193         g_free (path);
1194
1195         return res;
1196 }
1197
1198 gint64
1199 mono_filesize_from_fd (int fd)
1200 {
1201         struct stat buf;
1202         int res;
1203
1204         MONO_ENTER_GC_SAFE;
1205         res = fstat (fd, &buf);
1206         MONO_EXIT_GC_SAFE;
1207         
1208         if (res == -1)
1209                 return (gint64)-1;
1210
1211         return (gint64)buf.st_size;
1212 }
1213
1214 #endif
1215
1216 #ifndef HOST_WIN32
1217 void mono_w32handle_dump (void);
1218
1219 void ves_icall_System_IO_MonoIO_DumpHandles (void)
1220 {
1221         mono_w32handle_dump ();
1222 }
1223 #endif /* !HOST_WIN32 */