2 * file-io.c: File IO internal calls
5 * Dick Porter (dick@ximian.com)
6 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 * (C) 2001,2002,2003 Ximian, Inc.
9 * Copyright (c) 2004,2005,2006 Novell, Inc. (http://www.novell.com)
18 #ifdef HAVE_SYS_STAT_H
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
25 #include <mono/metadata/object.h>
26 #include <mono/io-layer/io-layer.h>
27 #include <mono/metadata/file-io.h>
28 #include <mono/metadata/exception.h>
29 #include <mono/metadata/appdomain.h>
30 #include <mono/metadata/marshal.h>
31 #include <mono/utils/strenc.h>
35 /* conversion functions */
37 static guint32 convert_mode(MonoFileMode mono_mode)
42 case FileMode_CreateNew:
51 case FileMode_OpenOrCreate:
54 case FileMode_Truncate:
55 mode=TRUNCATE_EXISTING;
61 g_warning("System.IO.FileMode has unknown value 0x%x",
70 static guint32 convert_access(MonoFileAccess mono_access)
78 case FileAccess_Write:
81 case FileAccess_ReadWrite:
82 access=GENERIC_READ|GENERIC_WRITE;
85 g_warning("System.IO.FileAccess has unknown value 0x%x",
94 static guint32 convert_share(MonoFileShare mono_share)
103 share=FILE_SHARE_READ;
105 case FileShare_Write:
106 share=FILE_SHARE_WRITE;
108 case FileShare_ReadWrite:
109 share=FILE_SHARE_READ|FILE_SHARE_WRITE;
112 g_warning("System.IO.FileShare has unknown value 0x%x",
122 static guint32 convert_stdhandle(guint32 fd)
128 stdhandle=STD_INPUT_HANDLE;
131 stdhandle=STD_OUTPUT_HANDLE;
134 stdhandle=STD_ERROR_HANDLE;
137 g_warning("unknown standard file descriptor %d", fd);
138 stdhandle=STD_INPUT_HANDLE;
145 static guint32 convert_seekorigin(MonoSeekOrigin origin)
150 case SeekOrigin_Begin:
151 w32origin=FILE_BEGIN;
153 case SeekOrigin_Current:
154 w32origin=FILE_CURRENT;
160 g_warning("System.IO.SeekOrigin has unknown value 0x%x",
163 w32origin=FILE_CURRENT;
169 static gint64 convert_filetime (const FILETIME *filetime)
171 guint64 ticks = filetime->dwHighDateTime;
173 ticks += filetime->dwLowDateTime;
174 return (gint64)ticks;
177 static void convert_win32_file_attribute_data (const WIN32_FILE_ATTRIBUTE_DATA *data, const gunichar2 *name, MonoIOStat *stat)
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;
191 stat->name = mono_string_new_utf16 (mono_domain_get (), name, len);
194 /* Managed file attributes have nearly but not quite the same values
195 * as the w32 equivalents.
197 static guint32 convert_attrs(MonoFileAttributes attrs)
199 if(attrs & FileAttributes_Encrypted) {
200 attrs |= FILE_ATTRIBUTE_ENCRYPTED;
206 /* System.IO.MonoIO internal calls */
209 ves_icall_System_IO_MonoIO_CreateDirectory (MonoString *path, gint32 *error)
215 *error=ERROR_SUCCESS;
217 ret=CreateDirectory (mono_string_chars (path), NULL);
219 *error=GetLastError ();
226 ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error)
232 *error=ERROR_SUCCESS;
234 ret=RemoveDirectory (mono_string_chars (path));
236 *error=GetLastError ();
243 get_error_from_g_file_error (gint error)
246 case G_FILE_ERROR_ACCES:
247 error = ERROR_ACCESS_DENIED;
249 case G_FILE_ERROR_NAMETOOLONG:
250 error = ERROR_FILENAME_EXCED_RANGE;
252 case G_FILE_ERROR_NOENT:
253 error = ERROR_FILE_NOT_FOUND;
255 case G_FILE_ERROR_NOTDIR:
256 error = ERROR_FILE_NOT_FOUND;
258 case G_FILE_ERROR_ROFS:
259 error = ERROR_ACCESS_DENIED;
261 case G_FILE_ERROR_TXTBSY:
262 error = ERROR_SHARING_VIOLATION;
264 case G_FILE_ERROR_NOSPC:
265 error = ERROR_HANDLE_DISK_FULL;
267 case G_FILE_ERROR_NFILE:
268 case G_FILE_ERROR_MFILE:
269 error = ERROR_TOO_MANY_OPEN_FILES;
271 case G_FILE_ERROR_BADF:
272 error = ERROR_INVALID_HANDLE;
274 case G_FILE_ERROR_INVAL:
275 error = ERROR_INVALID_PARAMETER;
277 case G_FILE_ERROR_AGAIN:
278 error = ERROR_SHARING_VIOLATION;
280 case G_FILE_ERROR_INTR:
281 error = ERROR_IO_PENDING;
283 case G_FILE_ERROR_PERM:
284 error = ERROR_ACCESS_DENIED;
286 case G_FILE_ERROR_FAILED:
287 error = ERROR_INVALID_PARAMETER;
289 case G_FILE_ERROR_NXIO:
290 case G_FILE_ERROR_NOMEM:
291 case G_FILE_ERROR_NODEV:
292 case G_FILE_ERROR_FAULT:
293 case G_FILE_ERROR_LOOP:
294 case G_FILE_ERROR_PIPE:
295 case G_FILE_ERROR_IO:
297 error = ERROR_GEN_FAILURE;
306 file_compare (gconstpointer a, gconstpointer b)
308 gchar *astr = *(gchar **) a;
309 gchar *bstr = *(gchar **) b;
311 return strcmp (astr, bstr);
315 get_file_attributes (const char *filename)
317 #ifdef PLATFORM_WIN32
321 full16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
322 if (full16 == NULL) {
323 g_message ("Bad encoding for '%s'\n", filename);
327 result = GetFileAttributes (full16);
335 gboolean issymlink = FALSE;
337 result = lstat (filename, &buf);
341 if (S_ISLNK (buf.st_mode)) {
343 result = stat (filename, &linkbuf);
349 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
350 if (S_ISSOCK (buf.st_mode))
351 buf.st_mode &= ~S_IFSOCK; /* don't consider socket protection */
354 if (S_ISDIR (buf.st_mode))
355 file_attrs |= FILE_ATTRIBUTE_DIRECTORY;
357 file_attrs |= FILE_ATTRIBUTE_ARCHIVE;
359 if ((buf.st_mode & S_IWUSR) == 0)
360 file_attrs |= FILE_ATTRIBUTE_READONLY;
362 if (*filename == '.')
363 file_attrs |= FILE_ATTRIBUTE_HIDDEN;
366 file_attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
374 test_file (const char *filename, int attrs, int mask)
378 file_attr = get_file_attributes (filename);
379 if (file_attr == FALSE)
382 return ((file_attr & mask) == attrs);
385 /* scandir using glib */
387 mono_io_scandir (const gchar *dirname, const gchar *pattern, int attrs,
388 int mask, gchar ***namelist)
390 GError *error = NULL;
395 GPatternSpec *patspec;
398 mask = convert_attrs (mask);
400 dir = g_dir_open (dirname, 0, &error);
402 /* g_dir_open returns ENOENT on directories on which we don't
403 * have read/x permission */
404 gint errnum = get_error_from_g_file_error (error->code);
405 g_error_free (error);
406 if (errnum == ERROR_FILE_NOT_FOUND && g_file_test (dirname, G_FILE_TEST_IS_DIR))
407 errnum = ERROR_ACCESS_DENIED;
409 SetLastError (errnum);
413 patspec = g_pattern_spec_new (pattern);
414 names = g_ptr_array_new ();
415 while ((name = g_dir_read_name (dir)) != NULL) {
416 if (!g_pattern_match_string (patspec, name))
419 full_name = g_build_filename (dirname, name, NULL);
420 if (FALSE == test_file (full_name, attrs, mask)) {
425 g_ptr_array_add (names, full_name);
428 g_pattern_spec_free (patspec);
432 g_ptr_array_sort (names, file_compare);
433 g_ptr_array_set_size (names, result + 1);
435 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
437 g_ptr_array_free (names, TRUE);
444 ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *_path, MonoString *_pattern,
445 gint attrs, gint mask, gint32 *error)
454 MonoString *str_name;
455 #ifndef PLATFORM_WIN32
462 *error = ERROR_SUCCESS;
464 path = mono_string_to_utf8 (_path);
465 pattern = mono_string_to_utf8 (_pattern);
466 nnames = mono_io_scandir (path, pattern, attrs, mask, &namelist);
468 *error = GetLastError ();
474 domain = mono_domain_get ();
475 result = mono_array_new (domain, mono_defaults.string_class, nnames);
477 for (i = 0; i < nnames; i++) {
479 str_name = mono_string_new (domain, namelist [i]);
481 utf16 = mono_unicode_from_external (namelist [i], &nbytes);
483 g_message ("Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n",
488 str_name = mono_string_from_utf16 (utf16);
491 mono_array_setref (result, i - removed, str_name);
496 shrinked = mono_array_new (domain, mono_defaults.string_class, nnames - removed);
497 for (i = 0; i < (nnames - removed); i++) {
499 str = mono_array_get (result, MonoString *, i);
500 mono_array_setref (shrinked, i, str);
505 g_strfreev (namelist);
512 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error)
521 buf = g_new (gunichar2, len);
523 *error=ERROR_SUCCESS;
526 if (GetCurrentDirectory (len, buf) > 0) {
531 result = mono_string_new_utf16 (mono_domain_get (), buf, len);
533 *error=GetLastError ();
541 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
548 *error=ERROR_SUCCESS;
550 ret=SetCurrentDirectory (mono_string_chars (path));
552 *error=GetLastError ();
559 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest,
566 *error=ERROR_SUCCESS;
568 ret=MoveFile (mono_string_chars (path), mono_string_chars (dest));
570 *error=GetLastError ();
577 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
578 MonoBoolean overwrite, gint32 *error)
584 *error=ERROR_SUCCESS;
586 ret=CopyFile (mono_string_chars (path), mono_string_chars (dest), !overwrite);
588 *error=GetLastError ();
595 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
601 *error=ERROR_SUCCESS;
603 ret=DeleteFile (mono_string_chars (path));
605 *error=GetLastError ();
612 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
618 *error=ERROR_SUCCESS;
620 ret=GetFileAttributes (mono_string_chars (path));
623 * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
624 * headers is wrong, hence this temporary workaround.
626 * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
629 /* if(ret==INVALID_FILE_ATTRIBUTES) { */
630 *error=GetLastError ();
637 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
644 *error=ERROR_SUCCESS;
646 ret=SetFileAttributes (mono_string_chars (path),
647 convert_attrs (attrs));
649 *error=GetLastError ();
656 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
662 *error=ERROR_SUCCESS;
664 ret=GetFileType (handle);
665 if(ret==FILE_TYPE_UNKNOWN) {
666 /* Not necessarily an error, but the caller will have
667 * to decide based on the error value.
669 *error=GetLastError ();
676 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
680 WIN32_FILE_ATTRIBUTE_DATA data;
684 *error=ERROR_SUCCESS;
686 result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data);
689 convert_win32_file_attribute_data (&data,
690 mono_string_chars (path),
693 *error=GetLastError ();
700 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
701 gint32 access_mode, gint32 share, gint32 options,
705 int attributes, attrs;
706 gunichar2 *chars = mono_string_chars (filename);
710 *error=ERROR_SUCCESS;
713 if (options & FileOptions_Encrypted)
714 attributes = FILE_ATTRIBUTE_ENCRYPTED;
716 attributes = FILE_ATTRIBUTE_NORMAL;
717 if (options & FileOptions_DeleteOnClose)
718 attributes |= FILE_FLAG_DELETE_ON_CLOSE;
719 if (options & FileOptions_SequentialScan)
720 attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
721 if (options & FileOptions_RandomAccess)
722 attributes |= FILE_FLAG_RANDOM_ACCESS;
724 /* Not sure if we should set FILE_FLAG_OVERLAPPED, how does this mix with the "Async" bool here? */
725 if (options & FileOptions_Asynchronous)
726 attributes |= FILE_FLAG_OVERLAPPED;
728 if (options & FileOptions_WriteThrough)
729 attributes |= FILE_FLAG_WRITE_THROUGH;
731 attributes = FILE_ATTRIBUTE_NORMAL;
733 /* If we're opening a directory we need to set the extra flag
735 attrs = GetFileAttributes (chars);
736 if (attrs != INVALID_FILE_ATTRIBUTES) {
737 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
738 attributes |= FILE_FLAG_BACKUP_SEMANTICS;
742 ret=CreateFile (chars, convert_access (access_mode),
743 convert_share (share), NULL, convert_mode (mode),
745 if(ret==INVALID_HANDLE_VALUE) {
746 *error=GetLastError ();
753 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
759 *error=ERROR_SUCCESS;
761 ret=CloseHandle (handle);
763 *error=GetLastError ();
770 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
771 gint32 dest_offset, gint32 count,
780 *error=ERROR_SUCCESS;
782 if (dest_offset + count > mono_array_length (dest))
785 buffer = mono_array_addr (dest, guchar, dest_offset);
786 result = ReadFile (handle, buffer, count, &n, NULL);
789 *error=GetLastError ();
797 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
798 gint32 src_offset, gint32 count,
807 *error=ERROR_SUCCESS;
809 if (src_offset + count > mono_array_length (src))
812 buffer = mono_array_addr (src, guchar, src_offset);
813 result = WriteFile (handle, buffer, count, &n, NULL);
816 *error=GetLastError ();
824 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
831 *error=ERROR_SUCCESS;
833 offset_hi = offset >> 32;
834 offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
835 convert_seekorigin (origin));
837 if(offset==INVALID_SET_FILE_POINTER) {
838 *error=GetLastError ();
841 return offset | ((gint64)offset_hi << 32);
845 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
851 *error=ERROR_SUCCESS;
853 ret=FlushFileBuffers (handle);
855 *error=GetLastError ();
862 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
869 *error=ERROR_SUCCESS;
871 length = GetFileSize (handle, &length_hi);
872 if(length==INVALID_FILE_SIZE) {
873 *error=GetLastError ();
876 return length | ((gint64)length_hi << 32);
880 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
883 gint64 offset, offset_set;
890 *error=ERROR_SUCCESS;
892 /* save file pointer */
895 offset = SetFilePointer (handle, 0, &offset_hi, FILE_CURRENT);
896 if(offset==INVALID_SET_FILE_POINTER) {
897 *error=GetLastError ();
901 /* extend or truncate */
903 length_hi = length >> 32;
904 offset_set=SetFilePointer (handle, length & 0xFFFFFFFF, &length_hi,
906 if(offset_set==INVALID_SET_FILE_POINTER) {
907 *error=GetLastError ();
911 result = SetEndOfFile (handle);
913 *error=GetLastError ();
917 /* restore file pointer */
919 offset_set=SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
921 if(offset_set==INVALID_SET_FILE_POINTER) {
922 *error=GetLastError ();
930 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
931 gint64 last_access_time,
932 gint64 last_write_time, gint32 *error)
935 const FILETIME *creation_filetime;
936 const FILETIME *last_access_filetime;
937 const FILETIME *last_write_filetime;
941 *error=ERROR_SUCCESS;
943 if (creation_time < 0)
944 creation_filetime = NULL;
946 creation_filetime = (FILETIME *)&creation_time;
948 if (last_access_time < 0)
949 last_access_filetime = NULL;
951 last_access_filetime = (FILETIME *)&last_access_time;
953 if (last_write_time < 0)
954 last_write_filetime = NULL;
956 last_write_filetime = (FILETIME *)&last_write_time;
958 ret=SetFileTime (handle, creation_filetime, last_access_filetime, last_write_filetime);
960 *error=GetLastError ();
967 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
971 return GetStdHandle (STD_OUTPUT_HANDLE);
975 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
979 return GetStdHandle (STD_INPUT_HANDLE);
983 ves_icall_System_IO_MonoIO_get_ConsoleError ()
987 return GetStdHandle (STD_ERROR_HANDLE);
991 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle,
992 HANDLE *write_handle)
994 SECURITY_ATTRIBUTES attr;
999 attr.nLength=sizeof(SECURITY_ATTRIBUTES);
1000 attr.bInheritHandle=TRUE;
1001 attr.lpSecurityDescriptor=NULL;
1003 ret=CreatePipe (read_handle, write_handle, &attr, 0);
1005 /* FIXME: throw an exception? */
1013 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
1015 #if defined (PLATFORM_WIN32)
1016 return (gunichar2) ':'; /* colon */
1018 return (gunichar2) '/'; /* forward slash */
1023 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
1025 #if defined (PLATFORM_WIN32)
1026 return (gunichar2) '\\'; /* backslash */
1028 return (gunichar2) '/'; /* forward slash */
1033 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
1035 #if defined (PLATFORM_WIN32)
1036 return (gunichar2) '/'; /* forward slash */
1038 return (gunichar2) '/'; /* slash, same as DirectorySeparatorChar */
1043 ves_icall_System_IO_MonoIO_get_PathSeparator ()
1045 #if defined (PLATFORM_WIN32)
1046 return (gunichar2) ';'; /* semicolon */
1048 return (gunichar2) ':'; /* colon */
1052 static const gunichar2
1053 invalid_path_chars [] = {
1054 #if defined (PLATFORM_WIN32)
1055 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */
1056 0x003c, /* less than */
1057 0x003e, /* greater than */
1074 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
1080 MONO_ARCH_SAVE_REGS;
1082 domain = mono_domain_get ();
1083 n = sizeof (invalid_path_chars) / sizeof (gunichar2);
1084 chars = mono_array_new (domain, mono_defaults.char_class, n);
1086 for (i = 0; i < n; ++ i)
1087 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
1093 ves_icall_System_IO_MonoIO_GetTempPath (MonoString **mono_name)
1098 name=g_new0 (gunichar2, 256);
1100 ret=GetTempPath (256, name);
1102 /* Buffer was too short. Try again... */
1104 name=g_new0 (gunichar2, ret+2); /* include the terminator */
1105 ret=GetTempPath (ret, name);
1110 g_message (G_GNUC_PRETTY_FUNCTION
1111 ": Temp path is [%s] (len %d)", name, ret);
1114 *mono_name=mono_string_new_utf16 (mono_domain_get (), name,
1123 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
1124 gint64 length, gint32 *error)
1128 *error=ERROR_SUCCESS;
1130 ret=LockFile (handle, position & 0xFFFFFFFF, position >> 32,
1131 length & 0xFFFFFFFF, length >> 32);
1133 *error = GetLastError ();
1137 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
1138 gint64 length, gint32 *error)
1142 *error=ERROR_SUCCESS;
1144 ret=UnlockFile (handle, position & 0xFFFFFFFF, position >> 32,
1145 length & 0xFFFFFFFF, length >> 32);
1147 *error = GetLastError ();