2003-02-21 Zoltan Varga <vargaz@freemail.hu>
[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  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/metadata/object.h>
14 #include <mono/io-layer/io-layer.h>
15 #include <mono/metadata/file-io.h>
16 #include <mono/metadata/exception.h>
17 #include <mono/metadata/appdomain.h>
18
19 #undef DEBUG
20
21 /* conversion functions */
22
23 static guint32 convert_mode(MonoFileMode mono_mode)
24 {
25         guint32 mode;
26
27         switch(mono_mode) {
28         case FileMode_CreateNew:
29                 mode=CREATE_NEW;
30                 break;
31         case FileMode_Create:
32                 mode=CREATE_ALWAYS;
33                 break;
34         case FileMode_Open:
35                 mode=OPEN_EXISTING;
36                 break;
37         case FileMode_OpenOrCreate:
38                 mode=OPEN_ALWAYS;
39                 break;
40         case FileMode_Truncate:
41                 mode=TRUNCATE_EXISTING;
42                 break;
43         case FileMode_Append:
44                 mode=OPEN_ALWAYS;
45                 break;
46         default:
47                 g_warning("System.IO.FileMode has unknown value 0x%x",
48                           mono_mode);
49                 /* Safe fallback */
50                 mode=OPEN_EXISTING;
51         }
52         
53         return(mode);
54 }
55
56 static guint32 convert_access(MonoFileAccess mono_access)
57 {
58         guint32 access;
59         
60         switch(mono_access) {
61         case FileAccess_Read:
62                 access=GENERIC_READ;
63                 break;
64         case FileAccess_Write:
65                 access=GENERIC_WRITE;
66                 break;
67         case FileAccess_ReadWrite:
68                 access=GENERIC_READ|GENERIC_WRITE;
69                 break;
70         default:
71                 g_warning("System.IO.FileAccess has unknown value 0x%x",
72                           mono_access);
73                 /* Safe fallback */
74                 access=GENERIC_READ;
75         }
76         
77         return(access);
78 }
79
80 static guint32 convert_share(MonoFileShare mono_share)
81 {
82         guint32 share;
83         
84         switch(mono_share) {
85         case FileShare_None:
86                 share=0;
87                 break;
88         case FileShare_Read:
89                 share=FILE_SHARE_READ;
90                 break;
91         case FileShare_Write:
92                 share=FILE_SHARE_WRITE;
93                 break;
94         case FileShare_ReadWrite:
95                 share=FILE_SHARE_READ|FILE_SHARE_WRITE;
96                 break;
97         default:
98                 g_warning("System.IO.FileShare has unknown value 0x%x",
99                           mono_share);
100                 /* Safe fallback */
101                 share=0;
102         }
103         
104         return(share);
105 }
106
107 #if 0
108 static guint32 convert_stdhandle(guint32 fd)
109 {
110         guint32 stdhandle;
111         
112         switch(fd) {
113         case 0:
114                 stdhandle=STD_INPUT_HANDLE;
115                 break;
116         case 1:
117                 stdhandle=STD_OUTPUT_HANDLE;
118                 break;
119         case 2:
120                 stdhandle=STD_ERROR_HANDLE;
121                 break;
122         default:
123                 g_warning("unknown standard file descriptor %d", fd);
124                 stdhandle=STD_INPUT_HANDLE;
125         }
126         
127         return(stdhandle);
128 }
129 #endif
130
131 static guint32 convert_seekorigin(MonoSeekOrigin origin)
132 {
133         guint32 w32origin;
134         
135         switch(origin) {
136         case SeekOrigin_Begin:
137                 w32origin=FILE_BEGIN;
138                 break;
139         case SeekOrigin_Current:
140                 w32origin=FILE_CURRENT;
141                 break;
142         case SeekOrigin_End:
143                 w32origin=FILE_END;
144                 break;
145         default:
146                 g_warning("System.IO.SeekOrigin has unknown value 0x%x",
147                           origin);
148                 /* Safe fallback */
149                 w32origin=FILE_CURRENT;
150         }
151         
152         return(w32origin);
153 }
154
155 static gint64 convert_filetime (const FILETIME *filetime)
156 {
157         gint64 *ticks;
158
159         ticks = (gint64 *)filetime;
160         return *ticks;
161 }
162
163 static void convert_win32_file_attribute_data (const WIN32_FILE_ATTRIBUTE_DATA *data, const gunichar2 *name, MonoIOStat *stat)
164 {
165         int len;
166         
167         stat->attributes = data->dwFileAttributes;
168         stat->creation_time = convert_filetime (&data->ftCreationTime);
169         stat->last_access_time = convert_filetime (&data->ftLastAccessTime);
170         stat->last_write_time = convert_filetime (&data->ftLastWriteTime);
171         stat->length = ((gint64)data->nFileSizeHigh << 32) | data->nFileSizeLow;
172
173         len = 0;
174         while (name [len])
175                 ++ len;
176
177         stat->name = mono_string_new_utf16 (mono_domain_get (), name, len);
178 }
179
180 /* System.IO.MonoIO internal calls */
181
182 MonoBoolean
183 ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error)
184 {
185         gboolean ret;
186         
187         MONO_ARCH_SAVE_REGS;
188
189         *error=ERROR_SUCCESS;
190         
191         ret=CreateDirectory (mono_string_chars (path), NULL);
192         if(ret==FALSE) {
193                 *error=GetLastError ();
194         }
195         
196         return(ret);
197 }
198
199 MonoBoolean
200 ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error)
201 {
202         gboolean ret;
203         
204         MONO_ARCH_SAVE_REGS;
205
206         *error=ERROR_SUCCESS;
207         
208         ret=RemoveDirectory (mono_string_chars (path));
209         if(ret==FALSE) {
210                 *error=GetLastError ();
211         }
212         
213         return(ret);
214 }
215
216 HANDLE 
217 ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path, MonoIOStat *stat,
218                                           gint32 *error)
219 {
220         WIN32_FIND_DATA data;
221         HANDLE result;
222
223         MONO_ARCH_SAVE_REGS;
224
225         *error=ERROR_SUCCESS;
226         
227         result = FindFirstFile (mono_string_chars (path), &data);
228
229         /* note: WIN32_FIND_DATA is an extension of WIN32_FILE_ATTRIBUTE_DATA */
230
231         if (result != INVALID_HANDLE_VALUE) {
232                 convert_win32_file_attribute_data ((const WIN32_FILE_ATTRIBUTE_DATA *)&data,
233                                                    &data.cFileName [0], stat);
234         } else {
235                 *error=GetLastError ();
236         }
237
238         return result;
239 }
240
241 MonoBoolean
242 ves_icall_System_IO_MonoIO_FindNextFile (HANDLE find, MonoIOStat *stat,
243                                          gint32 *error)
244 {
245         WIN32_FIND_DATA data;
246         gboolean result;
247
248         MONO_ARCH_SAVE_REGS;
249
250         *error=ERROR_SUCCESS;
251         
252         result = FindNextFile (find, &data);
253         if (result) {
254                 convert_win32_file_attribute_data ((const WIN32_FILE_ATTRIBUTE_DATA *)&data,
255                                                    &data.cFileName [0], stat);
256         } else {
257                 *error=GetLastError ();
258         }
259
260         return result;
261 }
262
263 MonoBoolean
264 ves_icall_System_IO_MonoIO_FindClose (HANDLE find, gint32 *error)
265 {
266         gboolean ret;
267         
268         MONO_ARCH_SAVE_REGS;
269
270         *error=ERROR_SUCCESS;
271         
272         ret=FindClose (find);
273         if(ret==FALSE) {
274                 *error=GetLastError ();
275         }
276         
277         return(ret);
278 }
279
280 MonoString *
281 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error)
282 {
283         MonoString *result;
284         gunichar2 *buf;
285         int len;
286
287         MONO_ARCH_SAVE_REGS;
288
289         len = MAX_PATH + 1;
290         buf = g_new (gunichar2, len);
291         
292         *error=ERROR_SUCCESS;
293         result = NULL;
294
295         if (GetCurrentDirectory (len, buf) > 0) {
296                 len = 0;
297                 while (buf [len])
298                         ++ len;
299
300                 result = mono_string_new_utf16 (mono_domain_get (), buf, len);
301         } else {
302                 *error=GetLastError ();
303         }
304
305         g_free (buf);
306         return result;
307 }
308
309 MonoBoolean
310 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
311                                                 gint32 *error)
312 {
313         gboolean ret;
314         
315         MONO_ARCH_SAVE_REGS;
316
317         *error=ERROR_SUCCESS;
318         
319         ret=SetCurrentDirectory (mono_string_chars (path));
320         if(ret==FALSE) {
321                 *error=GetLastError ();
322         }
323         
324         return(ret);
325 }
326
327 MonoBoolean
328 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest,
329                                      gint32 *error)
330 {
331         gboolean ret;
332         
333         MONO_ARCH_SAVE_REGS;
334
335         *error=ERROR_SUCCESS;
336         
337         ret=MoveFile (mono_string_chars (path), mono_string_chars (dest));
338         if(ret==FALSE) {
339                 *error=GetLastError ();
340         }
341         
342         return(ret);
343 }
344
345 MonoBoolean
346 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
347                                      MonoBoolean overwrite, gint32 *error)
348 {
349         gboolean ret;
350         
351         MONO_ARCH_SAVE_REGS;
352
353         *error=ERROR_SUCCESS;
354         
355         ret=CopyFile (mono_string_chars (path), mono_string_chars (dest), !overwrite);
356         if(ret==FALSE) {
357                 *error=GetLastError ();
358         }
359         
360         return(ret);
361 }
362
363 MonoBoolean
364 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
365 {
366         gboolean ret;
367         
368         MONO_ARCH_SAVE_REGS;
369
370         *error=ERROR_SUCCESS;
371         
372         ret=DeleteFile (mono_string_chars (path));
373         if(ret==FALSE) {
374                 *error=GetLastError ();
375         }
376         
377         return(ret);
378 }
379
380 gint32 
381 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
382 {
383         gint32 ret;
384         
385         MONO_ARCH_SAVE_REGS;
386
387         *error=ERROR_SUCCESS;
388         
389         ret=GetFileAttributes (mono_string_chars (path));
390         if(ret==INVALID_FILE_ATTRIBUTES) {
391                 *error=GetLastError ();
392         }
393         
394         return(ret);
395 }
396
397 MonoBoolean
398 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
399                                               gint32 *error)
400 {
401         gboolean ret;
402         
403         MONO_ARCH_SAVE_REGS;
404
405         *error=ERROR_SUCCESS;
406         
407         ret=SetFileAttributes (mono_string_chars (path), attrs);
408         if(ret==FALSE) {
409                 *error=GetLastError ();
410         }
411         
412         return(ret);
413 }
414
415 gint32
416 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
417 {
418         gboolean ret;
419         
420         MONO_ARCH_SAVE_REGS;
421
422         *error=ERROR_SUCCESS;
423         
424         ret=GetFileType (handle);
425         if(ret==FILE_TYPE_UNKNOWN) {
426                 /* Not necessarily an error, but the caller will have
427                  * to decide based on the error value.
428                  */
429                 *error=GetLastError ();
430         }
431         
432         return(ret);
433 }
434
435 MonoBoolean
436 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
437                                         gint32 *error)
438 {
439         gboolean result;
440         WIN32_FILE_ATTRIBUTE_DATA data;
441
442         MONO_ARCH_SAVE_REGS;
443
444         *error=ERROR_SUCCESS;
445         
446         result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data);
447
448         if (result) {
449                 convert_win32_file_attribute_data (&data,
450                                                    mono_string_chars (path),
451                                                    stat);
452         } else {
453                 *error=GetLastError ();
454         }
455
456         return result;
457 }
458
459 HANDLE 
460 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
461                                  gint32 access_mode, gint32 share,
462                                  gint32 *error)
463 {
464         HANDLE ret;
465         
466         MONO_ARCH_SAVE_REGS;
467
468         *error=ERROR_SUCCESS;
469         
470         ret=CreateFile (mono_string_chars (filename),
471                         convert_access (access_mode), convert_share (share),
472                         NULL, convert_mode (mode), FILE_ATTRIBUTE_NORMAL,
473                         NULL);
474         if(ret==INVALID_HANDLE_VALUE) {
475                 *error=GetLastError ();
476         }
477         
478         return(ret);
479 }
480
481 MonoBoolean
482 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
483 {
484         gboolean ret;
485         
486         MONO_ARCH_SAVE_REGS;
487
488         *error=ERROR_SUCCESS;
489         
490         ret=CloseHandle (handle);
491         if(ret==FALSE) {
492                 *error=GetLastError ();
493         }
494         
495         return(ret);
496 }
497
498 gint32 
499 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
500                                  gint32 dest_offset, gint32 count,
501                                  gint32 *error)
502 {
503         guchar *buffer;
504         gboolean result;
505         guint32 n;
506
507         MONO_ARCH_SAVE_REGS;
508
509         *error=ERROR_SUCCESS;
510         
511         if (dest_offset + count > mono_array_length (dest))
512                 return 0;
513
514         buffer = mono_array_addr (dest, guchar, dest_offset);
515         result = ReadFile (handle, buffer, count, &n, NULL);
516
517         if (!result) {
518                 *error=GetLastError ();
519                 return -1;
520         }
521
522         return (gint32)n;
523 }
524
525 gint32 
526 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
527                                   gint32 src_offset, gint32 count,
528                                   gint32 *error)
529 {
530         guchar *buffer;
531         gboolean result;
532         guint32 n;
533
534         MONO_ARCH_SAVE_REGS;
535
536         *error=ERROR_SUCCESS;
537         
538         if (src_offset + count > mono_array_length (src))
539                 return 0;
540         
541         buffer = mono_array_addr (src, guchar, src_offset);
542         result = WriteFile (handle, buffer, count, &n, NULL);
543
544         if (!result) {
545                 *error=GetLastError ();
546                 return -1;
547         }
548
549         return (gint32)n;
550 }
551
552 gint64 
553 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
554                                  gint32 *error)
555 {
556         guint32 offset_hi;
557
558         MONO_ARCH_SAVE_REGS;
559
560         *error=ERROR_SUCCESS;
561         
562         offset_hi = offset >> 32;
563         offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
564                                  convert_seekorigin (origin));
565
566         if(offset==INVALID_SET_FILE_POINTER) {
567                 *error=GetLastError ();
568         }
569         
570         return offset | ((gint64)offset_hi << 32);
571 }
572
573 MonoBoolean
574 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
575 {
576         gboolean ret;
577         
578         MONO_ARCH_SAVE_REGS;
579
580         *error=ERROR_SUCCESS;
581         
582         ret=FlushFileBuffers (handle);
583         if(ret==FALSE) {
584                 *error=GetLastError ();
585         }
586         
587         return(ret);
588 }
589
590 gint64 
591 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
592 {
593         gint64 length;
594         guint32 length_hi;
595
596         MONO_ARCH_SAVE_REGS;
597
598         *error=ERROR_SUCCESS;
599         
600         length = GetFileSize (handle, &length_hi);
601         if(length==INVALID_FILE_SIZE) {
602                 *error=GetLastError ();
603         }
604         
605         return length | ((gint64)length_hi << 32);
606 }
607
608 MonoBoolean
609 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
610                                       gint32 *error)
611 {
612         gint64 offset, offset_set;
613         gint32 offset_hi;
614         gint32 length_hi;
615         gboolean result;
616
617         MONO_ARCH_SAVE_REGS;
618
619         *error=ERROR_SUCCESS;
620         
621         /* save file pointer */
622
623         offset_hi = 0;
624         offset = SetFilePointer (handle, 0, &offset_hi, FILE_CURRENT);
625         if(offset==INVALID_SET_FILE_POINTER) {
626                 *error=GetLastError ();
627                 return(FALSE);
628         }
629
630         /* extend or truncate */
631
632         length_hi = length >> 32;
633         offset_set=SetFilePointer (handle, length & 0xFFFFFFFF, &length_hi,
634                                    FILE_BEGIN);
635         if(offset_set==INVALID_SET_FILE_POINTER) {
636                 *error=GetLastError ();
637                 return(FALSE);
638         }
639
640         result = SetEndOfFile (handle);
641         if(result==FALSE) {
642                 *error=GetLastError ();
643                 return(FALSE);
644         }
645
646         /* restore file pointer */
647
648         offset_set=SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
649                                    FILE_BEGIN);
650         if(offset_set==INVALID_SET_FILE_POINTER) {
651                 *error=GetLastError ();
652                 return(FALSE);
653         }
654
655         return result;
656 }
657
658 MonoBoolean
659 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
660                                         gint64 last_access_time,
661                                         gint64 last_write_time, gint32 *error)
662 {
663         gboolean ret;
664         const FILETIME *creation_filetime;
665         const FILETIME *last_access_filetime;
666         const FILETIME *last_write_filetime;
667
668         MONO_ARCH_SAVE_REGS;
669
670         *error=ERROR_SUCCESS;
671         
672         if (creation_time < 0)
673                 creation_filetime = NULL;
674         else
675                 creation_filetime = (FILETIME *)&creation_time;
676
677         if (last_access_time < 0)
678                 last_access_filetime = NULL;
679         else
680                 last_access_filetime = (FILETIME *)&last_access_time;
681
682         if (last_write_time < 0)
683                 last_write_filetime = NULL;
684         else
685                 last_write_filetime = (FILETIME *)&last_write_time;
686
687         ret=SetFileTime (handle, creation_filetime, last_access_filetime, last_write_filetime);
688         if(ret==FALSE) {
689                 *error=GetLastError ();
690         }
691         
692         return(ret);
693 }
694
695 HANDLE 
696 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
697 {
698         MONO_ARCH_SAVE_REGS;
699
700         return GetStdHandle (STD_OUTPUT_HANDLE);
701 }
702
703 HANDLE 
704 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
705 {
706         MONO_ARCH_SAVE_REGS;
707
708         return GetStdHandle (STD_INPUT_HANDLE);
709 }
710
711 HANDLE 
712 ves_icall_System_IO_MonoIO_get_ConsoleError ()
713 {
714         MONO_ARCH_SAVE_REGS;
715
716         return GetStdHandle (STD_ERROR_HANDLE);
717 }
718
719 MonoBoolean
720 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle,
721                                        HANDLE *write_handle)
722 {
723         SECURITY_ATTRIBUTES attr;
724         gboolean ret;
725         
726         MONO_ARCH_SAVE_REGS;
727
728         attr.nLength=sizeof(SECURITY_ATTRIBUTES);
729         attr.bInheritHandle=TRUE;
730         attr.lpSecurityDescriptor=NULL;
731         
732         ret=CreatePipe (read_handle, write_handle, &attr, 0);
733         if(ret==FALSE) {
734                 /* FIXME: throw an exception? */
735                 return(FALSE);
736         }
737         
738         return(TRUE);
739 }
740
741 gunichar2 
742 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
743 {
744         MONO_ARCH_SAVE_REGS;
745
746 #if defined (PLATFORM_WIN32)
747         return (gunichar2) 0x003a;      /* colon */
748 #else
749         return (gunichar2) 0x002f;      /* forward slash */
750 #endif
751 }
752
753 gunichar2 
754 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
755 {
756         MONO_ARCH_SAVE_REGS;
757
758 #if defined (PLATFORM_WIN32)
759         return (gunichar2) 0x005c;      /* backslash */
760 #else
761         return (gunichar2) 0x002f;      /* forward slash */
762 #endif
763 }
764
765 gunichar2 
766 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
767 {
768         MONO_ARCH_SAVE_REGS;
769
770 #if defined (PLATFORM_WIN32)
771         return (gunichar2) 0x002f;      /* forward slash */
772 #else
773         return (gunichar2) 0x005c;      /* backslash */
774 #endif
775 }
776
777 gunichar2 
778 ves_icall_System_IO_MonoIO_get_PathSeparator ()
779 {
780         MONO_ARCH_SAVE_REGS;
781
782 #if defined (PLATFORM_WIN32)
783         return (gunichar2) 0x003b;      /* semicolon */
784 #else
785         return (gunichar2) 0x003a;      /* colon */
786 #endif
787 }
788
789 static gunichar2 invalid_path_chars [] = {
790 #if defined (PLATFORM_WIN32)
791         0x0022,                         /* double quote */
792         0x003c,                         /* less than */
793         0x003e,                         /* greater than */
794         0x007c,                         /* pipe */
795 #endif
796         0x0000                          /* null */
797 };
798
799 MonoArray *
800 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
801 {
802         MonoArray *chars;
803         MonoDomain *domain;
804         int i, n;
805
806         MONO_ARCH_SAVE_REGS;
807
808         domain = mono_domain_get ();
809         chars = mono_array_new (domain, mono_defaults.char_class, 5);
810
811         n = sizeof (invalid_path_chars) / sizeof (gunichar2);
812
813         for (i = 0; i < n; ++ i)
814                 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
815         
816         return chars;
817 }