3 * File IO internal calls
6 * Dick Porter (dick@ximian.com)
7 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
12 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
23 #ifdef HAVE_SYS_STAT_H
26 #ifdef HAVE_SYS_TYPES_H
27 #include <sys/types.h>
30 #include <mono/metadata/object.h>
31 #include <mono/metadata/w32file.h>
32 #include <mono/metadata/w32error.h>
33 #include <mono/metadata/w32file-internals.h>
34 #include <mono/metadata/exception.h>
35 #include <mono/metadata/appdomain.h>
36 #include <mono/metadata/marshal.h>
37 #include <mono/utils/strenc.h>
38 #include <utils/mono-io-portability.h>
39 #include <mono/metadata/w32handle.h>
40 #include <mono/utils/w32api.h>
44 /* conversion functions */
46 static guint32 convert_mode(MonoFileMode mono_mode)
51 case FileMode_CreateNew:
60 case FileMode_OpenOrCreate:
63 case FileMode_Truncate:
64 mode=TRUNCATE_EXISTING;
70 g_warning("System.IO.FileMode has unknown value 0x%x",
79 static guint32 convert_access(MonoFileAccess mono_access)
87 case FileAccess_Write:
90 case FileAccess_ReadWrite:
91 access=GENERIC_READ|GENERIC_WRITE;
94 g_warning("System.IO.FileAccess has unknown value 0x%x",
103 static guint32 convert_share(MonoFileShare mono_share)
107 if (mono_share & FileShare_Read) {
108 share |= FILE_SHARE_READ;
110 if (mono_share & FileShare_Write) {
111 share |= FILE_SHARE_WRITE;
113 if (mono_share & FileShare_Delete) {
114 share |= FILE_SHARE_DELETE;
117 if (mono_share & ~(FileShare_Read|FileShare_Write|FileShare_Delete)) {
118 g_warning("System.IO.FileShare has unknown value 0x%x",
128 static guint32 convert_stdhandle(guint32 fd)
134 stdhandle=STD_INPUT_HANDLE;
137 stdhandle=STD_OUTPUT_HANDLE;
140 stdhandle=STD_ERROR_HANDLE;
143 g_warning("unknown standard file descriptor %d", fd);
144 stdhandle=STD_INPUT_HANDLE;
151 static guint32 convert_seekorigin(MonoSeekOrigin origin)
156 case SeekOrigin_Begin:
157 w32origin=FILE_BEGIN;
159 case SeekOrigin_Current:
160 w32origin=FILE_CURRENT;
166 g_warning("System.IO.SeekOrigin has unknown value 0x%x",
169 w32origin=FILE_CURRENT;
175 /* Managed file attributes have nearly but not quite the same values
176 * as the w32 equivalents.
178 static guint32 convert_attrs(MonoFileAttributes attrs)
180 if(attrs & FileAttributes_Encrypted) {
181 attrs = (MonoFileAttributes)(attrs | FILE_ATTRIBUTE_ENCRYPTED);
187 /* System.IO.MonoIO internal calls */
190 ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error)
194 *error=ERROR_SUCCESS;
196 ret=mono_w32file_create_directory (mono_string_chars (path));
198 *error=mono_w32error_get_last ();
205 ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error)
209 *error=ERROR_SUCCESS;
211 ret=mono_w32file_remove_directory (mono_string_chars (path));
213 *error=mono_w32error_get_last ();
220 ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path_with_pattern, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
223 WIN32_FIND_DATA data;
226 hnd = mono_w32file_find_first (mono_string_chars (path_with_pattern), &data);
228 if (hnd == INVALID_HANDLE_VALUE) {
231 *ioerror = mono_w32error_get_last ();
235 mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
236 mono_error_set_pending_exception (&error);
238 *file_attr = data.dwFileAttributes;
239 *ioerror = ERROR_SUCCESS;
245 ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd, MonoString **file_name, gint32 *file_attr, gint32 *ioerror)
248 WIN32_FIND_DATA data;
251 res = mono_w32file_find_next (hnd, &data);
256 *ioerror = mono_w32error_get_last ();
260 mono_gc_wbarrier_generic_store (file_name, (MonoObject*) mono_string_from_utf16_checked (data.cFileName, &error));
261 mono_error_set_pending_exception (&error);
263 *file_attr = data.dwFileAttributes;
264 *ioerror = ERROR_SUCCESS;
270 ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd)
272 return mono_w32file_find_close (hnd);
276 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *io_error)
283 len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/
284 buf = g_new (gunichar2, len);
287 *io_error=ERROR_SUCCESS;
290 res_len = mono_w32file_get_cwd (len, buf);
291 if (res_len > len) { /*buf is too small.*/
292 int old_res_len = res_len;
294 buf = g_new (gunichar2, res_len);
295 res_len = mono_w32file_get_cwd (res_len, buf) == old_res_len;
303 result = mono_string_new_utf16_checked (mono_domain_get (), buf, len, &error);
305 *io_error=mono_w32error_get_last ();
309 mono_error_set_pending_exception (&error);
314 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
319 *error=ERROR_SUCCESS;
321 ret=mono_w32file_set_cwd (mono_string_chars (path));
323 *error=mono_w32error_get_last ();
330 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, gint32 *error)
332 *error=ERROR_SUCCESS;
333 return mono_w32file_move (mono_string_chars (path), mono_string_chars (dest), error);
337 ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName,
338 MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors,
341 gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL;
342 guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH;
345 utf16_sourceFileName = mono_string_chars (sourceFileName);
346 if (destinationFileName)
347 utf16_destinationFileName = mono_string_chars (destinationFileName);
348 if (destinationBackupFileName)
349 utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName);
351 *error = ERROR_SUCCESS;
352 if (ignoreMetadataErrors)
353 replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS;
355 /* FIXME: source and destination file names must not be NULL, but apparently they might be! */
356 return mono_w32file_replace (utf16_destinationFileName, utf16_sourceFileName,
357 utf16_destinationBackupFileName, replaceFlags, error);
361 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
362 MonoBoolean overwrite, gint32 *error)
364 *error=ERROR_SUCCESS;
365 return mono_w32file_copy (mono_string_chars (path), mono_string_chars (dest), overwrite, error);
369 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
373 *error=ERROR_SUCCESS;
375 ret=mono_w32file_delete (mono_string_chars (path));
377 *error=mono_w32error_get_last ();
384 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
387 *error=ERROR_SUCCESS;
389 ret = mono_w32file_get_attributes (mono_string_chars (path));
392 * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
393 * headers is wrong, hence this temporary workaround.
395 * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
398 /* if(ret==INVALID_FILE_ATTRIBUTES) { */
399 *error=mono_w32error_get_last ();
405 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
409 *error=ERROR_SUCCESS;
411 ret=mono_w32file_set_attributes (mono_string_chars (path),
412 convert_attrs ((MonoFileAttributes)attrs));
414 *error=mono_w32error_get_last ();
420 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
424 *error=ERROR_SUCCESS;
426 ret=mono_w32file_get_type (handle);
427 if(ret==FILE_TYPE_UNKNOWN) {
428 /* Not necessarily an error, but the caller will have
429 * to decide based on the error value.
431 *error=mono_w32error_get_last ();
438 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, gint32 *error)
442 *error=ERROR_SUCCESS;
444 result = mono_w32file_get_attributes_ex (mono_string_chars (path), stat);
447 *error=mono_w32error_get_last ();
448 memset (stat, 0, sizeof (MonoIOStat));
455 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
456 gint32 access_mode, gint32 share, gint32 options,
460 int attributes, attrs;
463 chars = mono_string_chars (filename);
464 *error=ERROR_SUCCESS;
467 if (options & FileOptions_Encrypted)
468 attributes = FILE_ATTRIBUTE_ENCRYPTED;
470 attributes = FILE_ATTRIBUTE_NORMAL;
471 if (options & FileOptions_DeleteOnClose)
472 attributes |= FILE_FLAG_DELETE_ON_CLOSE;
473 if (options & FileOptions_SequentialScan)
474 attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
475 if (options & FileOptions_RandomAccess)
476 attributes |= FILE_FLAG_RANDOM_ACCESS;
478 if (options & FileOptions_Temporary)
479 attributes |= FILE_ATTRIBUTE_TEMPORARY;
481 if (options & FileOptions_WriteThrough)
482 attributes |= FILE_FLAG_WRITE_THROUGH;
484 attributes = FILE_ATTRIBUTE_NORMAL;
486 /* If we're opening a directory we need to set the extra flag
488 attrs = mono_w32file_get_attributes (chars);
489 if (attrs != INVALID_FILE_ATTRIBUTES) {
490 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
491 attributes |= FILE_FLAG_BACKUP_SEMANTICS;
495 ret=mono_w32file_create (chars, convert_access ((MonoFileAccess)access_mode), convert_share ((MonoFileShare)share), convert_mode ((MonoFileMode)mode), attributes);
496 if(ret==INVALID_HANDLE_VALUE) {
497 *error=mono_w32error_get_last ();
504 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
507 *error=ERROR_SUCCESS;
509 ret=mono_w32file_close (handle);
511 *error=mono_w32error_get_last ();
517 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
518 gint32 dest_offset, gint32 count,
525 *error=ERROR_SUCCESS;
527 MONO_CHECK_ARG_NULL (dest, 0);
529 if (dest_offset > mono_array_length (dest) - count) {
530 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
534 buffer = mono_array_addr (dest, guchar, dest_offset);
536 result = mono_w32file_read (handle, buffer, count, &n);
539 *error=mono_w32error_get_last ();
547 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
548 gint32 src_offset, gint32 count,
555 *error=ERROR_SUCCESS;
557 MONO_CHECK_ARG_NULL (src, 0);
559 if (src_offset > mono_array_length (src) - count) {
560 mono_set_pending_exception (mono_get_exception_argument ("array", "array too small. numBytes/offset wrong."));
564 buffer = mono_array_addr (src, guchar, src_offset);
565 result = mono_w32file_write (handle, buffer, count, &n);
568 *error=mono_w32error_get_last ();
576 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
581 *error=ERROR_SUCCESS;
583 offset_hi = offset >> 32;
584 offset = mono_w32file_seek (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi,
585 convert_seekorigin ((MonoSeekOrigin)origin));
587 if(offset==INVALID_SET_FILE_POINTER) {
588 *error=mono_w32error_get_last ();
591 return offset | ((gint64)offset_hi << 32);
595 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
599 *error=ERROR_SUCCESS;
601 ret=mono_w32file_flush (handle);
603 *error=mono_w32error_get_last ();
610 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
612 *error=ERROR_SUCCESS;
613 return mono_w32file_get_file_size (handle, error);
616 /* FIXME make gc suspendable */
618 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
621 gint64 offset, offset_set;
626 *error=ERROR_SUCCESS;
628 /* save file pointer */
631 offset = mono_w32file_seek (handle, 0, &offset_hi, FILE_CURRENT);
632 if(offset==INVALID_SET_FILE_POINTER) {
633 *error=mono_w32error_get_last ();
637 /* extend or truncate */
639 length_hi = length >> 32;
640 offset_set=mono_w32file_seek (handle, length & 0xFFFFFFFF, &length_hi,
642 if(offset_set==INVALID_SET_FILE_POINTER) {
643 *error=mono_w32error_get_last ();
647 result = mono_w32file_truncate (handle);
649 *error=mono_w32error_get_last ();
653 /* restore file pointer */
655 offset_set=mono_w32file_seek (handle, offset & 0xFFFFFFFF, &offset_hi,
657 if(offset_set==INVALID_SET_FILE_POINTER) {
658 *error=mono_w32error_get_last ();
666 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
667 gint64 last_access_time,
668 gint64 last_write_time, gint32 *error)
671 const FILETIME *creation_filetime;
672 const FILETIME *access_filetime;
673 const FILETIME *write_filetime;
675 *error=ERROR_SUCCESS;
677 if (creation_time < 0)
678 creation_filetime = NULL;
680 creation_filetime = (FILETIME *)&creation_time;
682 if (last_access_time < 0)
683 access_filetime = NULL;
685 access_filetime = (FILETIME *)&last_access_time;
687 if (last_write_time < 0)
688 write_filetime = NULL;
690 write_filetime = (FILETIME *)&last_write_time;
692 ret=mono_w32file_set_times (handle, creation_filetime, access_filetime, write_filetime);
694 *error=mono_w32error_get_last ();
701 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
703 return mono_w32file_get_console_output ();
707 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
709 return mono_w32file_get_console_input ();
713 ves_icall_System_IO_MonoIO_get_ConsoleError ()
715 return mono_w32file_get_console_error ();
719 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, HANDLE *write_handle, gint32 *error)
723 ret=mono_w32file_create_pipe (read_handle, write_handle, 0);
726 *error = mono_w32error_get_last ();
727 /* FIXME: throw an exception? */
735 ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle, HANDLE source_handle,
736 HANDLE target_process_handle, HANDLE *target_handle, gint32 access, gint32 inherit, gint32 options, gint32 *error)
739 *target_handle = mono_w32handle_duplicate (source_handle);
744 ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options);
748 *error = mono_w32error_get_last ();
749 /* FIXME: throw an exception? */
759 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
761 return (gunichar2) '/'; /* forward slash */
765 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
767 return (gunichar2) '/'; /* forward slash */
771 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
773 if (IS_PORTABILITY_SET)
774 return (gunichar2) '\\'; /* backslash */
776 return (gunichar2) '/'; /* forward slash */
780 ves_icall_System_IO_MonoIO_get_PathSeparator ()
782 return (gunichar2) ':'; /* colon */
784 #endif /* !HOST_WIN32 */
786 static const gunichar2
787 invalid_path_chars [] = {
788 #if defined (TARGET_WIN32)
789 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */
790 0x003c, /* less than */
791 0x003e, /* greater than */
808 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
815 domain = mono_domain_get ();
816 n = sizeof (invalid_path_chars) / sizeof (gunichar2);
817 chars = mono_array_new_checked (domain, mono_defaults.char_class, n, &error);
818 if (mono_error_set_pending_exception (&error))
821 for (i = 0; i < n; ++ i)
822 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
827 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
828 gint64 length, gint32 *error)
830 *error=ERROR_SUCCESS;
831 mono_w32file_lock (handle, position, length, error);
834 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
835 gint64 length, gint32 *error)
837 *error=ERROR_SUCCESS;
838 mono_w32file_unlock (handle, position, length, error);
841 //Support for io-layer free mmap'd files.
843 #if defined (TARGET_IOS) || defined (TARGET_ANDROID)
846 mono_filesize_from_path (MonoString *string)
851 char *path = mono_string_to_utf8_checked (string, &error);
852 mono_error_raise_exception (&error); /* OK to throw, external only without a good alternative */
856 stat_res = stat (path, &buf);
861 res = (gint64)buf.st_size;
869 mono_filesize_from_fd (int fd)
875 res = fstat (fd, &buf);
881 return (gint64)buf.st_size;
887 void mono_w32handle_dump (void);
889 void ves_icall_System_IO_MonoIO_DumpHandles (void)
891 mono_w32handle_dump ();
893 #endif /* !HOST_WIN32 */