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