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 <mono/utils/mono-io-portability.h>
39 #include <mono/metadata/w32handle.h>
40 #include <mono/utils/w32api.h>
41 #include "icall-decl.h"
45 /* conversion functions */
47 static guint32
convert_mode(MonoFileMode mono_mode
)
52 case FileMode_CreateNew
:
61 case FileMode_OpenOrCreate
:
64 case FileMode_Truncate
:
65 mode
=TRUNCATE_EXISTING
;
71 g_warning("System.IO.FileMode has unknown value 0x%x",
80 static guint32
convert_access(MonoFileAccess mono_access
)
88 case FileAccess_Write
:
91 case FileAccess_ReadWrite
:
92 access
=GENERIC_READ
|GENERIC_WRITE
;
95 g_warning("System.IO.FileAccess has unknown value 0x%x",
104 static guint32
convert_share(MonoFileShare mono_share
)
108 if (mono_share
& FileShare_Read
) {
109 share
|= FILE_SHARE_READ
;
111 if (mono_share
& FileShare_Write
) {
112 share
|= FILE_SHARE_WRITE
;
114 if (mono_share
& FileShare_Delete
) {
115 share
|= FILE_SHARE_DELETE
;
118 if (mono_share
& ~(FileShare_Read
|FileShare_Write
|FileShare_Delete
)) {
119 g_warning("System.IO.FileShare has unknown value 0x%x",
129 static guint32
convert_stdhandle(guint32 fd
)
135 stdhandle
=STD_INPUT_HANDLE
;
138 stdhandle
=STD_OUTPUT_HANDLE
;
141 stdhandle
=STD_ERROR_HANDLE
;
144 g_warning("unknown standard file descriptor %d", fd
);
145 stdhandle
=STD_INPUT_HANDLE
;
152 static guint32
convert_seekorigin(MonoSeekOrigin origin
)
157 case SeekOrigin_Begin
:
158 w32origin
=FILE_BEGIN
;
160 case SeekOrigin_Current
:
161 w32origin
=FILE_CURRENT
;
167 g_warning("System.IO.SeekOrigin has unknown value 0x%x",
170 w32origin
=FILE_CURRENT
;
176 /* Managed file attributes have nearly but not quite the same values
177 * as the w32 equivalents.
179 static guint32
convert_attrs(MonoFileAttributes attrs
)
181 if(attrs
& FileAttributes_Encrypted
) {
182 attrs
= (MonoFileAttributes
)(attrs
| FILE_ATTRIBUTE_ENCRYPTED
);
188 /* System.IO.MonoIO internal calls */
193 ves_icall_System_IO_MonoIO_CreateDirectory (const gunichar2
*path
, gint32
*error
)
197 *error
=ERROR_SUCCESS
;
199 ret
=mono_w32file_create_directory (path
);
201 *error
=mono_w32error_get_last ();
208 ves_icall_System_IO_MonoIO_RemoveDirectory (const gunichar2
*path
, gint32
*error
)
212 *error
=ERROR_SUCCESS
;
214 ret
=mono_w32file_remove_directory (path
);
216 *error
=mono_w32error_get_last ();
223 ves_icall_System_IO_MonoIO_FindFirstFile (const gunichar2
*path_with_pattern
, MonoStringHandleOut file_name
, gint32
*file_attr
, gint32
*ioerror
, MonoError
*error
)
226 WIN32_FIND_DATA data
;
228 hnd
= mono_w32file_find_first (path_with_pattern
, &data
);
230 if (hnd
== INVALID_HANDLE_VALUE
) {
231 MONO_HANDLE_ASSIGN (file_name
, NULL_HANDLE_STRING
);
233 *ioerror
= mono_w32error_get_last ();
238 while (data
.cFileName
[len
]) len
++;
239 MONO_HANDLE_ASSIGN (file_name
, mono_string_new_utf16_handle (mono_domain_get (), data
.cFileName
, len
, error
));
240 return_val_if_nok (error
, INVALID_HANDLE_VALUE
);
242 *file_attr
= data
.dwFileAttributes
;
243 *ioerror
= ERROR_SUCCESS
;
249 ves_icall_System_IO_MonoIO_FindNextFile (HANDLE hnd
, MonoStringHandleOut file_name
, gint32
*file_attr
, gint32
*ioerror
, MonoError
*error
)
252 WIN32_FIND_DATA data
;
254 res
= mono_w32file_find_next (hnd
, &data
);
257 MONO_HANDLE_ASSIGN (file_name
, NULL_HANDLE_STRING
);
259 *ioerror
= mono_w32error_get_last ();
264 while (data
.cFileName
[len
]) len
++;
265 MONO_HANDLE_ASSIGN (file_name
, mono_string_new_utf16_handle (mono_domain_get (), data
.cFileName
, len
, error
));
266 return_val_if_nok (error
, FALSE
);
268 *file_attr
= data
.dwFileAttributes
;
269 *ioerror
= ERROR_SUCCESS
;
275 ves_icall_System_IO_MonoIO_FindCloseFile (HANDLE hnd
)
277 return mono_w32file_find_close (hnd
);
281 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32
*io_error
, MonoError
*error
)
283 MonoStringHandle result
;
287 len
= MAX_PATH
+ 1; /*FIXME this is too smal under most unix systems.*/
288 buf
= g_new (gunichar2
, len
);
290 *io_error
= ERROR_SUCCESS
;
291 result
= MONO_HANDLE_NEW (MonoString
, NULL
);
293 res_len
= mono_w32file_get_cwd (len
, buf
);
294 if (res_len
> len
) { /*buf is too small.*/
295 int old_res_len
= res_len
;
297 buf
= g_new (gunichar2
, res_len
);
298 res_len
= mono_w32file_get_cwd (res_len
, buf
) == old_res_len
;
306 MONO_HANDLE_ASSIGN (result
, mono_string_new_utf16_handle (mono_domain_get (), buf
, len
, error
));
308 *io_error
=mono_w32error_get_last ();
312 return_val_if_nok (error
, NULL_HANDLE_STRING
);
317 ves_icall_System_IO_MonoIO_SetCurrentDirectory (const gunichar2
*path
,
322 *error
=ERROR_SUCCESS
;
324 ret
=mono_w32file_set_cwd (path
);
326 *error
=mono_w32error_get_last ();
333 ves_icall_System_IO_MonoIO_MoveFile (const gunichar2
*path
, const gunichar2
*dest
, gint32
*error
)
335 *error
=ERROR_SUCCESS
;
336 return mono_w32file_move (path
, dest
, error
);
340 ves_icall_System_IO_MonoIO_ReplaceFile (const gunichar2
*source_file_name
, const gunichar2
*destination_file_name
,
341 const gunichar2
*destination_backup_file_name
, MonoBoolean ignore_metadata_errors
,
344 guint32 replace_flags
= REPLACEFILE_WRITE_THROUGH
;
346 *error
= ERROR_SUCCESS
;
347 if (ignore_metadata_errors
)
348 replace_flags
|= REPLACEFILE_IGNORE_MERGE_ERRORS
;
350 /* FIXME: source and destination file names must not be NULL, but apparently they might be! */
351 return mono_w32file_replace (destination_file_name
, source_file_name
,
352 destination_backup_file_name
, replace_flags
, error
);
356 ves_icall_System_IO_MonoIO_CopyFile (const gunichar2
*path
, const gunichar2
*dest
,
357 MonoBoolean overwrite
, gint32
*error
)
359 *error
=ERROR_SUCCESS
;
360 return mono_w32file_copy (path
, dest
, overwrite
, error
);
364 ves_icall_System_IO_MonoIO_DeleteFile (const gunichar2
*path
, gint32
*error
)
368 *error
=ERROR_SUCCESS
;
370 ret
=mono_w32file_delete (path
);
372 *error
=mono_w32error_get_last ();
379 ves_icall_System_IO_MonoIO_GetFileAttributes (const gunichar2
*path
, gint32
*error
)
382 *error
=ERROR_SUCCESS
;
384 ret
= mono_w32file_get_attributes (path
);
387 * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
388 * headers is wrong, hence this temporary workaround.
390 * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
393 /* if(ret==INVALID_FILE_ATTRIBUTES) { */
394 *error
=mono_w32error_get_last ();
400 ves_icall_System_IO_MonoIO_SetFileAttributes (const gunichar2
*path
, gint32 attrs
,
404 *error
=ERROR_SUCCESS
;
406 ret
=mono_w32file_set_attributes (path
,
407 convert_attrs ((MonoFileAttributes
)attrs
));
409 *error
=mono_w32error_get_last ();
415 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle
, gint32
*error
)
419 *error
=ERROR_SUCCESS
;
421 ret
=mono_w32file_get_type (handle
);
422 if(ret
==FILE_TYPE_UNKNOWN
) {
423 /* Not necessarily an error, but the caller will have
424 * to decide based on the error value.
426 *error
=mono_w32error_get_last ();
433 ves_icall_System_IO_MonoIO_GetFileStat (const gunichar2
*path
, MonoIOStat
*stat
, gint32
*error
)
437 *error
=ERROR_SUCCESS
;
439 result
= mono_w32file_get_attributes_ex (path
, stat
);
442 *error
=mono_w32error_get_last ();
443 memset (stat
, 0, sizeof (MonoIOStat
));
450 ves_icall_System_IO_MonoIO_Open (const gunichar2
*filename
, gint32 mode
,
451 gint32 access_mode
, gint32 share
, gint32 options
,
455 int attributes
, attrs
;
457 *error
=ERROR_SUCCESS
;
460 if (options
& FileOptions_Encrypted
)
461 attributes
= FILE_ATTRIBUTE_ENCRYPTED
;
463 attributes
= FILE_ATTRIBUTE_NORMAL
;
464 if (options
& FileOptions_DeleteOnClose
)
465 attributes
|= FILE_FLAG_DELETE_ON_CLOSE
;
466 if (options
& FileOptions_SequentialScan
)
467 attributes
|= FILE_FLAG_SEQUENTIAL_SCAN
;
468 if (options
& FileOptions_RandomAccess
)
469 attributes
|= FILE_FLAG_RANDOM_ACCESS
;
471 if (options
& FileOptions_Temporary
)
472 attributes
|= FILE_ATTRIBUTE_TEMPORARY
;
474 if (options
& FileOptions_WriteThrough
)
475 attributes
|= FILE_FLAG_WRITE_THROUGH
;
477 attributes
= FILE_ATTRIBUTE_NORMAL
;
479 /* If we're opening a directory we need to set the extra flag
481 attrs
= mono_w32file_get_attributes (filename
);
482 if (attrs
!= INVALID_FILE_ATTRIBUTES
) {
483 if (attrs
& FILE_ATTRIBUTE_DIRECTORY
) {
484 attributes
|= FILE_FLAG_BACKUP_SEMANTICS
;
488 ret
=mono_w32file_create (filename
, convert_access ((MonoFileAccess
)access_mode
), convert_share ((MonoFileShare
)share
), convert_mode ((MonoFileMode
)mode
), attributes
);
489 if(ret
==INVALID_HANDLE_VALUE
) {
490 *error
=mono_w32error_get_last ();
497 ves_icall_System_IO_MonoIO_Close (HANDLE handle
, gint32
*error
)
500 *error
=ERROR_SUCCESS
;
502 ret
=mono_w32file_close (handle
);
504 *error
=mono_w32error_get_last ();
510 ves_icall_System_IO_MonoIO_Cancel (HANDLE handle
, gint32
*error
)
513 *error
=ERROR_SUCCESS
;
515 ret
= mono_w32file_cancel (handle
);
517 *error
= mono_w32error_get_last ();
523 ves_icall_System_IO_MonoIO_Read (HANDLE handle
, MonoArrayHandle dest
,
524 gint32 dest_offset
, gint32 count
,
532 *io_error
=ERROR_SUCCESS
;
534 MONO_CHECK_ARG_NULL_HANDLE (dest
, 0);
536 if (dest_offset
> mono_array_handle_length (dest
) - count
) {
537 mono_error_set_argument (error
, "array", "array too small. numBytes/offset wrong.");
541 guint32 buffer_handle
= 0;
542 buffer
= MONO_ARRAY_HANDLE_PIN (dest
, guchar
, dest_offset
, &buffer_handle
);
543 result
= mono_w32file_read (handle
, buffer
, count
, &n
, io_error
);
544 mono_gchandle_free_internal (buffer_handle
);
553 ves_icall_System_IO_MonoIO_Write (HANDLE handle
, MonoArrayHandle src
,
554 gint32 src_offset
, gint32 count
,
562 *io_error
=ERROR_SUCCESS
;
564 MONO_CHECK_ARG_NULL_HANDLE (src
, 0);
566 if (src_offset
> mono_array_handle_length (src
) - count
) {
567 mono_error_set_argument (error
, "array", "array too small. numBytes/offset wrong.");
571 guint32 src_handle
= 0;
572 buffer
= MONO_ARRAY_HANDLE_PIN (src
, guchar
, src_offset
, &src_handle
);
573 result
= mono_w32file_write (handle
, buffer
, count
, &n
, io_error
);
574 mono_gchandle_free_internal (src_handle
);
583 ves_icall_System_IO_MonoIO_Seek (HANDLE handle
, gint64 offset
, gint32 origin
,
588 *error
=ERROR_SUCCESS
;
590 offset_hi
= offset
>> 32;
591 offset
= mono_w32file_seek (handle
, (gint32
) (offset
& 0xFFFFFFFF), &offset_hi
,
592 convert_seekorigin ((MonoSeekOrigin
)origin
));
594 if(offset
==INVALID_SET_FILE_POINTER
) {
595 *error
=mono_w32error_get_last ();
598 return offset
| ((gint64
)offset_hi
<< 32);
602 ves_icall_System_IO_MonoIO_Flush (HANDLE handle
, gint32
*error
)
606 *error
=ERROR_SUCCESS
;
608 ret
=mono_w32file_flush (handle
);
610 *error
=mono_w32error_get_last ();
617 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle
, gint32
*error
)
619 *error
=ERROR_SUCCESS
;
620 return mono_w32file_get_file_size (handle
, error
);
623 /* FIXME make gc suspendable */
625 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle
, gint64 length
,
628 gint64 offset
, offset_set
;
633 *error
=ERROR_SUCCESS
;
635 /* save file pointer */
638 offset
= mono_w32file_seek (handle
, 0, &offset_hi
, FILE_CURRENT
);
639 if(offset
==INVALID_SET_FILE_POINTER
) {
640 *error
=mono_w32error_get_last ();
644 /* extend or truncate */
646 length_hi
= length
>> 32;
647 offset_set
=mono_w32file_seek (handle
, length
& 0xFFFFFFFF, &length_hi
,
649 if(offset_set
==INVALID_SET_FILE_POINTER
) {
650 *error
=mono_w32error_get_last ();
654 result
= mono_w32file_truncate (handle
);
656 *error
=mono_w32error_get_last ();
660 /* restore file pointer */
662 offset_set
=mono_w32file_seek (handle
, offset
& 0xFFFFFFFF, &offset_hi
,
664 if(offset_set
==INVALID_SET_FILE_POINTER
) {
665 *error
=mono_w32error_get_last ();
673 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle
, gint64 creation_time
,
674 gint64 last_access_time
,
675 gint64 last_write_time
, gint32
*error
)
678 const FILETIME
*creation_filetime
;
679 const FILETIME
*access_filetime
;
680 const FILETIME
*write_filetime
;
682 *error
=ERROR_SUCCESS
;
684 if (creation_time
< 0)
685 creation_filetime
= NULL
;
687 creation_filetime
= (FILETIME
*)&creation_time
;
689 if (last_access_time
< 0)
690 access_filetime
= NULL
;
692 access_filetime
= (FILETIME
*)&last_access_time
;
694 if (last_write_time
< 0)
695 write_filetime
= NULL
;
697 write_filetime
= (FILETIME
*)&last_write_time
;
699 ret
=mono_w32file_set_times (handle
, creation_filetime
, access_filetime
, write_filetime
);
701 *error
=mono_w32error_get_last ();
708 ves_icall_System_IO_MonoIO_get_ConsoleOutput (void)
710 return mono_w32file_get_console_output ();
714 ves_icall_System_IO_MonoIO_get_ConsoleInput (void)
716 return mono_w32file_get_console_input ();
720 ves_icall_System_IO_MonoIO_get_ConsoleError (void)
722 return mono_w32file_get_console_error ();
726 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE
*read_handle
, HANDLE
*write_handle
, gint32
*error
)
728 *error
= ERROR_SUCCESS
;
732 ret
=mono_w32file_create_pipe (read_handle
, write_handle
, 0);
735 *error
= mono_w32error_get_last ();
736 /* FIXME: throw an exception? */
744 ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle
, HANDLE source_handle
,
745 HANDLE target_process_handle
, HANDLE
*target_handle
, gint32 access
, gint32 inherit
, gint32 options
, gint32
*error
)
748 MonoW32Handle
*source_handle_data
;
750 if (!mono_w32handle_lookup_and_ref (source_handle
, &source_handle_data
)) {
751 *error
= ERROR_INVALID_HANDLE
;
755 *target_handle
= mono_w32handle_duplicate (source_handle_data
);
757 mono_w32handle_unref ((MonoW32Handle
*)source_handle
);
762 ret
=DuplicateHandle (source_process_handle
, source_handle
, target_process_handle
, target_handle
, access
, inherit
, options
);
766 *error
= mono_w32error_get_last ();
767 /* FIXME: throw an exception? */
777 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar (void)
779 return (gunichar2
) '/'; /* forward slash */
783 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar (void)
785 return (gunichar2
) '/'; /* forward slash */
789 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar (void)
791 if (IS_PORTABILITY_SET
)
792 return (gunichar2
) '\\'; /* backslash */
794 return (gunichar2
) '/'; /* forward slash */
798 ves_icall_System_IO_MonoIO_get_PathSeparator (void)
800 return (gunichar2
) ':'; /* colon */
802 #endif /* !HOST_WIN32 */
804 static const gunichar2
805 invalid_path_chars
[] = {
806 #if defined (TARGET_WIN32)
807 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */
808 0x003c, /* less than */
809 0x003e, /* greater than */
826 ves_icall_System_IO_MonoIO_get_InvalidPathChars (MonoError
*error
)
828 MonoArrayHandle chars
= MONO_HANDLE_NEW (MonoArray
, NULL
);
832 domain
= mono_domain_get ();
833 n
= sizeof (invalid_path_chars
) / sizeof (gunichar2
);
834 MONO_HANDLE_ASSIGN (chars
, mono_array_new_handle (domain
, mono_defaults
.char_class
, n
, error
));
835 return_val_if_nok (error
, MONO_HANDLE_CAST (MonoArray
, mono_new_null ()));
837 for (i
= 0; i
< n
; ++ i
)
838 MONO_HANDLE_ARRAY_SETVAL (chars
, gunichar2
, i
, invalid_path_chars
[i
]);
843 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle
, gint64 position
,
844 gint64 length
, gint32
*error
)
846 *error
=ERROR_SUCCESS
;
847 mono_w32file_lock (handle
, position
, length
, error
);
850 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle
, gint64 position
,
851 gint64 length
, gint32
*error
)
853 *error
=ERROR_SUCCESS
;
854 mono_w32file_unlock (handle
, position
, length
, error
);
859 void mono_w32handle_dump (void);
861 void ves_icall_System_IO_MonoIO_DumpHandles (void)
863 mono_w32handle_dump ();
865 #endif /* !HOST_WIN32 */
867 #endif /* !ENABLE_NETCORE */
869 //Support for io-layer free mmap'd files.
871 #if defined (TARGET_IOS) || defined (TARGET_ANDROID)
874 mono_filesize_from_path (MonoString
*string
)
879 char *path
= mono_string_to_utf8_checked_internal (string
, error
);
880 mono_error_raise_exception_deprecated (error
); /* OK to throw, external only without a good alternative */
884 stat_res
= stat (path
, &buf
);
889 res
= (gint64
)buf
.st_size
;
897 mono_filesize_from_fd (int fd
)
903 res
= fstat (fd
, &buf
);
909 return (gint64
)buf
.st_size
;