[w32file] Move MonoIO.Find{First,Next,Close} to managed
[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 HANDLE
289 ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
290 {
291         HANDLE hnd;
292         WIN32_FIND_DATA data;
293         MonoError error;
294
295         hnd = mono_w32file_find_first (mono_string_chars (path_with_pattern), &data);
296
297         if (hnd == INVALID_HANDLE_VALUE) {
298                 *file_name = NULL;
299                 *file_attr = 0;
300                 *ioerror = mono_w32error_get_last ();
301                 return hnd;
302         }
303
304         mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
305         mono_error_set_pending_exception (&error);
306
307         *file_attr = data.dwFileAttributes;
308         *ioerror = ERROR_SUCCESS;
309
310         return hnd;
311 }
312
313 MonoBoolean
314 ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
315 {
316         MonoBoolean res;
317         WIN32_FIND_DATA data;
318         MonoError error;
319
320         res = mono_w32file_find_next (hnd, &data);
321
322         if (res == FALSE) {
323                 *file_name = NULL;
324                 *file_attr = 0;
325                 *ioerror = mono_w32error_get_last ();
326                 return res;
327         }
328
329         mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
330         mono_error_set_pending_exception (&error);
331
332         *file_attr = data.dwFileAttributes;
333         *ioerror = ERROR_SUCCESS;
334
335         return res;
336 }
337
338 MonoBoolean
339 ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd)
340 {
341         return mono_w32file_find_close (hnd);
342 }
343
344 MonoString *
345 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error)
346 {
347         MonoError error;
348         MonoString *result;
349         gunichar2 *buf;
350         int len, res_len;
351
352         len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/
353         buf = g_new (gunichar2, len);
354         
355         error_init (&error);
356         *io_error=ERROR_SUCCESS;
357         result = NULL;
358
359         res_len = mono_w32file_get_cwd (len, buf);
360         if (res_len > len) { /*buf is too small.*/
361                 int old_res_len = res_len;
362                 g_free (buf);
363                 buf = g_new (gunichar2, res_len);
364                 res_len = mono_w32file_get_cwd (res_len, buf) == old_res_len;
365         }
366         
367         if (res_len) {
368                 len = 0;
369                 while (buf [len])
370                         ++ len;
371
372                 result = mono_string_new_utf16_checked (mono_domain_get (), buf, len, &error);
373         } else {
374                 *io_error=mono_w32error_get_last ();
375         }
376
377         g_free (buf);
378         mono_error_set_pending_exception (&error);
379         return result;
380 }
381
382 MonoBoolean
383 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
384                                                 gint32 *error)
385 {
386         gboolean ret;
387         
388         *error=ERROR_SUCCESS;
389         
390         ret=mono_w32file_set_cwd (mono_string_chars (path));
391         if(ret==FALSE) {
392                 *error=mono_w32error_get_last ();
393         }
394         
395         return(ret);
396 }
397
398 MonoBoolean
399 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, gint32 *error)
400 {
401         *error=ERROR_SUCCESS;
402         return mono_w32file_move (mono_string_chars (path), mono_string_chars (dest), error);
403 }
404
405 MonoBoolean
406 ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName,
407                                         MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors,
408                                         gint32 *error)
409 {
410         gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL;
411         guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH;
412
413         if (sourceFileName)
414                 utf16_sourceFileName = mono_string_chars (sourceFileName);
415         if (destinationFileName)
416                 utf16_destinationFileName = mono_string_chars (destinationFileName);
417         if (destinationBackupFileName)
418                 utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName);
419
420         *error = ERROR_SUCCESS;
421         if (ignoreMetadataErrors)
422                 replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS;
423
424         /* FIXME: source and destination file names must not be NULL, but apparently they might be! */
425         return mono_w32file_replace (utf16_destinationFileName, utf16_sourceFileName,
426                                           utf16_destinationBackupFileName, replaceFlags, error);
427 }
428
429 MonoBoolean
430 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
431                                      MonoBoolean overwrite, gint32 *error)
432 {
433         *error=ERROR_SUCCESS;
434         return mono_w32file_copy (mono_string_chars (path), mono_string_chars (dest), overwrite, error);
435 }
436
437 MonoBoolean
438 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
439 {
440         gboolean ret;
441         
442         *error=ERROR_SUCCESS;
443         
444         ret=mono_w32file_delete (mono_string_chars (path));
445         if(ret==FALSE) {
446                 *error=mono_w32error_get_last ();
447         }
448         
449         return(ret);
450 }
451
452 gint32 
453 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
454 {
455         gint32 ret;
456         *error=ERROR_SUCCESS;
457         
458         ret=get_file_attributes (mono_string_chars (path));
459
460         /* 
461          * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
462          * headers is wrong, hence this temporary workaround.
463          * See
464          * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
465          */
466         if (ret==-1) {
467           /* if(ret==INVALID_FILE_ATTRIBUTES) { */
468                 *error=mono_w32error_get_last ();
469         }
470         return(ret);
471 }
472
473 MonoBoolean
474 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
475                                               gint32 *error)
476 {
477         gboolean ret;
478         *error=ERROR_SUCCESS;
479         
480         ret=mono_w32file_set_attributes (mono_string_chars (path),
481                 convert_attrs ((MonoFileAttributes)attrs));
482         if(ret==FALSE) {
483                 *error=mono_w32error_get_last ();
484         }
485         return(ret);
486 }
487
488 gint32
489 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
490 {
491         gboolean ret;
492
493         *error=ERROR_SUCCESS;
494         
495         ret=mono_w32file_get_type (handle);
496         if(ret==FILE_TYPE_UNKNOWN) {
497                 /* Not necessarily an error, but the caller will have
498                  * to decide based on the error value.
499                  */
500                 *error=mono_w32error_get_last ();
501         }
502         
503         return(ret);
504 }
505
506 MonoBoolean
507 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, gint32 *error)
508 {
509         gboolean result;
510
511         *error=ERROR_SUCCESS;
512         
513         result = get_file_attributes_ex (mono_string_chars (path), stat);
514
515         if (!result) {
516                 *error=mono_w32error_get_last ();
517                 memset (stat, 0, sizeof (MonoIOStat));
518         }
519
520         return result;
521 }
522
523 HANDLE 
524 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
525                                  gint32 access_mode, gint32 share, gint32 options,
526                                  gint32 *error)
527 {
528         HANDLE ret;
529         int attributes, attrs;
530         gunichar2 *chars;
531
532         chars = mono_string_chars (filename);   
533         *error=ERROR_SUCCESS;
534
535         if (options != 0){
536                 if (options & FileOptions_Encrypted)
537                         attributes = FILE_ATTRIBUTE_ENCRYPTED;
538                 else
539                         attributes = FILE_ATTRIBUTE_NORMAL;
540                 if (options & FileOptions_DeleteOnClose)
541                         attributes |= FILE_FLAG_DELETE_ON_CLOSE;
542                 if (options & FileOptions_SequentialScan)
543                         attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
544                 if (options & FileOptions_RandomAccess)
545                         attributes |= FILE_FLAG_RANDOM_ACCESS;
546
547                 if (options & FileOptions_Temporary)
548                         attributes |= FILE_ATTRIBUTE_TEMPORARY;
549                 
550                 if (options & FileOptions_WriteThrough)
551                         attributes |= FILE_FLAG_WRITE_THROUGH;
552         } else
553                 attributes = FILE_ATTRIBUTE_NORMAL;
554
555         /* If we're opening a directory we need to set the extra flag
556          */
557         attrs = get_file_attributes (chars);
558         if (attrs != INVALID_FILE_ATTRIBUTES) {
559                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
560                         attributes |= FILE_FLAG_BACKUP_SEMANTICS;
561                 }
562         }
563         
564         ret=mono_w32file_create (chars, convert_access ((MonoFileAccess)access_mode), convert_share ((MonoFileShare)share), convert_mode ((MonoFileMode)mode), attributes);
565         if(ret==INVALID_HANDLE_VALUE) {
566                 *error=mono_w32error_get_last ();
567         } 
568         
569         return(ret);
570 }
571
572 MonoBoolean
573 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
574 {
575         gboolean ret;
576         *error=ERROR_SUCCESS;
577         
578         ret=mono_w32file_close (handle);
579         if(ret==FALSE) {
580                 *error=mono_w32error_get_last ();
581         }
582         return(ret);
583 }
584
585 gint32 
586 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
587                                  gint32 dest_offset, gint32 count,
588                                  gint32 *error)
589 {
590         guchar *buffer;
591         gboolean result;
592         guint32 n;
593
594         *error=ERROR_SUCCESS;
595
596         MONO_CHECK_ARG_NULL (dest, 0);
597
598         if (dest_offset > mono_array_length (dest) - count) {
599                 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
600                 return 0;
601         }
602
603         buffer = mono_array_addr (dest, guchar, dest_offset);
604
605         result = mono_w32file_read (handle, buffer, count, &n);
606
607         if (!result) {
608                 *error=mono_w32error_get_last ();
609                 return -1;
610         }
611
612         return (gint32)n;
613 }
614
615 gint32 
616 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
617                                   gint32 src_offset, gint32 count,
618                                   gint32 *error)
619 {
620         guchar *buffer;
621         gboolean result;
622         guint32 n;
623
624         *error=ERROR_SUCCESS;
625
626         MONO_CHECK_ARG_NULL (src, 0);
627         
628         if (src_offset > mono_array_length (src) - count) {
629                 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
630                 return 0;
631         }
632         
633         buffer = mono_array_addr (src, guchar, src_offset);
634         result = mono_w32file_write (handle, buffer, count, &n);
635
636         if (!result) {
637                 *error=mono_w32error_get_last ();
638                 return -1;
639         }
640
641         return (gint32)n;
642 }
643
644 gint64 
645 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
646                                  gint32 *error)
647 {
648         gint32 offset_hi;
649
650         *error=ERROR_SUCCESS;
651         
652         offset_hi = offset >> 32;
653         offset = mono_w32file_seek (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi,
654                                  convert_seekorigin ((MonoSeekOrigin)origin));
655
656         if(offset==INVALID_SET_FILE_POINTER) {
657                 *error=mono_w32error_get_last ();
658         }
659
660         return offset | ((gint64)offset_hi << 32);
661 }
662
663 MonoBoolean
664 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
665 {
666         gboolean ret;
667
668         *error=ERROR_SUCCESS;
669         
670         ret=mono_w32file_flush (handle);
671         if(ret==FALSE) {
672                 *error=mono_w32error_get_last ();
673         }
674         
675         return(ret);
676 }
677
678 gint64
679 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
680 {
681         *error=ERROR_SUCCESS;
682         return mono_w32file_get_file_size (handle, error);
683 }
684
685 /* FIXME make gc suspendable */
686 MonoBoolean
687 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
688                                       gint32 *error)
689 {
690         gint64 offset, offset_set;
691         gint32 offset_hi;
692         gint32 length_hi;
693         gboolean result;
694
695         *error=ERROR_SUCCESS;
696         
697         /* save file pointer */
698
699         offset_hi = 0;
700         offset = mono_w32file_seek (handle, 0, &offset_hi, FILE_CURRENT);
701         if(offset==INVALID_SET_FILE_POINTER) {
702                 *error=mono_w32error_get_last ();
703                 return(FALSE);
704         }
705
706         /* extend or truncate */
707
708         length_hi = length >> 32;
709         offset_set=mono_w32file_seek (handle, length & 0xFFFFFFFF, &length_hi,
710                                    FILE_BEGIN);
711         if(offset_set==INVALID_SET_FILE_POINTER) {
712                 *error=mono_w32error_get_last ();
713                 return(FALSE);
714         }
715
716         result = mono_w32file_truncate (handle);
717         if(result==FALSE) {
718                 *error=mono_w32error_get_last ();
719                 return(FALSE);
720         }
721
722         /* restore file pointer */
723
724         offset_set=mono_w32file_seek (handle, offset & 0xFFFFFFFF, &offset_hi,
725                                    FILE_BEGIN);
726         if(offset_set==INVALID_SET_FILE_POINTER) {
727                 *error=mono_w32error_get_last ();
728                 return(FALSE);
729         }
730
731         return result;
732 }
733
734 MonoBoolean
735 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
736                                         gint64 last_access_time,
737                                         gint64 last_write_time, gint32 *error)
738 {
739         gboolean ret;
740         const FILETIME *creation_filetime;
741         const FILETIME *access_filetime;
742         const FILETIME *write_filetime;
743
744         *error=ERROR_SUCCESS;
745         
746         if (creation_time < 0)
747                 creation_filetime = NULL;
748         else
749                 creation_filetime = (FILETIME *)&creation_time;
750
751         if (last_access_time < 0)
752                 access_filetime = NULL;
753         else
754                 access_filetime = (FILETIME *)&last_access_time;
755
756         if (last_write_time < 0)
757                 write_filetime = NULL;
758         else
759                 write_filetime = (FILETIME *)&last_write_time;
760
761         ret=mono_w32file_set_times (handle, creation_filetime, access_filetime, write_filetime);
762         if(ret==FALSE) {
763                 *error=mono_w32error_get_last ();
764         }
765
766         return(ret);
767 }
768
769 HANDLE 
770 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
771 {
772         return mono_w32file_get_console_output ();
773 }
774
775 HANDLE 
776 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
777 {
778         return mono_w32file_get_console_input ();
779 }
780
781 HANDLE 
782 ves_icall_System_IO_MonoIO_get_ConsoleError ()
783 {
784         return mono_w32file_get_console_error ();
785 }
786
787 MonoBoolean
788 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, HANDLE *write_handle, gint32 *error)
789 {
790         gboolean ret;
791
792         ret=mono_w32file_create_pipe (read_handle, write_handle, 0);
793
794         if(ret==FALSE) {
795                 *error = mono_w32error_get_last ();
796                 /* FIXME: throw an exception? */
797                 return(FALSE);
798         }
799         
800         return(TRUE);
801 }
802
803 MonoBoolean
804 ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle, HANDLE source_handle,
805                 HANDLE target_process_handle, HANDLE *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error)
806 {
807 #ifndef HOST_WIN32
808         *target_handle = mono_w32handle_duplicate (source_handle);
809 #else
810         gboolean ret;
811
812         MONO_ENTER_GC_SAFE;
813         ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options);
814         MONO_EXIT_GC_SAFE;
815
816         if (!ret) {
817                 *error = mono_w32error_get_last ();
818                 /* FIXME: throw an exception? */
819                 return(FALSE);
820         }
821 #endif
822
823         return(TRUE);
824 }
825
826 #ifndef HOST_WIN32
827 gunichar2 
828 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
829 {
830         return (gunichar2) '/'; /* forward slash */
831 }
832
833 gunichar2 
834 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
835 {
836         return (gunichar2) '/'; /* forward slash */
837 }
838
839 gunichar2 
840 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
841 {
842         if (IS_PORTABILITY_SET)
843                 return (gunichar2) '\\';        /* backslash */
844         else
845                 return (gunichar2) '/'; /* forward slash */
846 }
847
848 gunichar2 
849 ves_icall_System_IO_MonoIO_get_PathSeparator ()
850 {
851         return (gunichar2) ':'; /* colon */
852 }
853 #endif /* !HOST_WIN32 */
854
855 static const gunichar2
856 invalid_path_chars [] = {
857 #if defined (TARGET_WIN32)
858         0x0022,                         /* double quote, which seems allowed in MS.NET but should be rejected */
859         0x003c,                         /* less than */
860         0x003e,                         /* greater than */
861         0x007c,                         /* pipe */
862         0x0008,
863         0x0010,
864         0x0011,
865         0x0012,
866         0x0014,
867         0x0015,
868         0x0016,
869         0x0017,
870         0x0018,
871         0x0019,
872 #endif
873         0x0000                          /* null */
874 };
875
876 MonoArray *
877 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
878 {
879         MonoError error;
880         MonoArray *chars;
881         MonoDomain *domain;
882         int i, n;
883
884         domain = mono_domain_get ();
885         n = sizeof (invalid_path_chars) / sizeof (gunichar2);
886         chars = mono_array_new_checked (domain, mono_defaults.char_class, n, &error);
887         if (mono_error_set_pending_exception (&error))
888                 return NULL;
889
890         for (i = 0; i < n; ++ i)
891                 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
892         
893         return chars;
894 }
895
896 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
897                                       gint64 length, gint32 *error)
898 {
899         *error=ERROR_SUCCESS;
900         mono_w32file_lock (handle, position, length, error);
901 }
902
903 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
904                                         gint64 length, gint32 *error)
905 {
906         *error=ERROR_SUCCESS;
907         mono_w32file_unlock (handle, position, length, error);
908 }
909
910 //Support for io-layer free mmap'd files.
911
912 #if defined (TARGET_IOS) || defined (TARGET_ANDROID)
913
914 gint64
915 mono_filesize_from_path (MonoString *string)
916 {
917         MonoError error;
918         struct stat buf;
919         gint64 res;
920         char *path = mono_string_to_utf8_checked (string, &error);
921         mono_error_raise_exception (&error); /* OK to throw, external only without a good alternative */
922
923         gint stat_res;
924         MONO_ENTER_GC_SAFE;
925         stat_res = stat (path, &buf);
926         MONO_EXIT_GC_SAFE;
927         if (stat_res == -1)
928                 res = -1;
929         else
930                 res = (gint64)buf.st_size;
931
932         g_free (path);
933
934         return res;
935 }
936
937 gint64
938 mono_filesize_from_fd (int fd)
939 {
940         struct stat buf;
941         int res;
942
943         MONO_ENTER_GC_SAFE;
944         res = fstat (fd, &buf);
945         MONO_EXIT_GC_SAFE;
946         
947         if (res == -1)
948                 return (gint64)-1;
949
950         return (gint64)buf.st_size;
951 }
952
953 #endif
954
955 #ifndef HOST_WIN32
956 void mono_w32handle_dump (void);
957
958 void ves_icall_System_IO_MonoIO_DumpHandles (void)
959 {
960         mono_w32handle_dump ();
961 }
962 #endif /* !HOST_WIN32 */