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