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);
336 result = lstat (filename, &buf);
340 if (S_ISLNK (buf.st_mode)) {
341 result = stat (filename, &linkbuf);
345 buf.st_mode |= ~S_IFDIR; /* force it to be returned as regular file */
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;
370 test_file (const char *filename, int attrs, int mask)
374 file_attr = get_file_attributes (filename);
375 if (file_attr == FALSE)
378 return ((file_attr & mask) == attrs);
381 /* scandir using glib */
383 mono_io_scandir (const gchar *dirname, const gchar *pattern, int attrs,
384 int mask, gchar ***namelist)
386 GError *error = NULL;
391 GPatternSpec *patspec;
394 mask = convert_attrs (mask);
396 dir = g_dir_open (dirname, 0, &error);
398 /* g_dir_open returns ENOENT on directories on which we don't
399 * have read/x permission */
400 gint errnum = get_error_from_g_file_error (error->code);
401 g_error_free (error);
402 if (errnum == ERROR_FILE_NOT_FOUND && g_file_test (dirname, G_FILE_TEST_IS_DIR))
403 errnum = ERROR_ACCESS_DENIED;
405 SetLastError (errnum);
409 patspec = g_pattern_spec_new (pattern);
410 names = g_ptr_array_new ();
411 while ((name = g_dir_read_name (dir)) != NULL) {
412 if (!g_pattern_match_string (patspec, name))
415 full_name = g_build_filename (dirname, name, NULL);
416 if (FALSE == test_file (full_name, attrs, mask)) {
421 g_ptr_array_add (names, full_name);
424 g_pattern_spec_free (patspec);
428 g_ptr_array_sort (names, file_compare);
429 g_ptr_array_set_size (names, result + 1);
431 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
433 g_ptr_array_free (names, TRUE);
440 ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *_path, MonoString *_pattern,
441 gint attrs, gint mask, gint32 *error)
450 MonoString *str_name;
451 #ifndef PLATFORM_WIN32
458 *error = ERROR_SUCCESS;
460 path = mono_string_to_utf8 (_path);
461 pattern = mono_string_to_utf8 (_pattern);
462 nnames = mono_io_scandir (path, pattern, attrs, mask, &namelist);
464 *error = GetLastError ();
470 domain = mono_domain_get ();
471 result = mono_array_new (domain, mono_defaults.string_class, nnames);
473 for (i = 0; i < nnames; i++) {
475 str_name = mono_string_new (domain, namelist [i]);
477 utf16 = mono_unicode_from_external (namelist [i], &nbytes);
479 g_message ("Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n",
484 str_name = mono_string_from_utf16 (utf16);
487 mono_array_setref (result, i - removed, str_name);
492 shrinked = mono_array_new (domain, mono_defaults.string_class, nnames - removed);
493 for (i = 0; i < (nnames - removed); i++) {
495 str = mono_array_get (result, MonoString *, i);
496 mono_array_setref (shrinked, i, str);
501 g_strfreev (namelist);
508 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error)
517 buf = g_new (gunichar2, len);
519 *error=ERROR_SUCCESS;
522 if (GetCurrentDirectory (len, buf) > 0) {
527 result = mono_string_new_utf16 (mono_domain_get (), buf, len);
529 *error=GetLastError ();
537 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
544 *error=ERROR_SUCCESS;
546 ret=SetCurrentDirectory (mono_string_chars (path));
548 *error=GetLastError ();
555 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest,
562 *error=ERROR_SUCCESS;
564 ret=MoveFile (mono_string_chars (path), mono_string_chars (dest));
566 *error=GetLastError ();
573 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
574 MonoBoolean overwrite, gint32 *error)
580 *error=ERROR_SUCCESS;
582 ret=CopyFile (mono_string_chars (path), mono_string_chars (dest), !overwrite);
584 *error=GetLastError ();
591 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
597 *error=ERROR_SUCCESS;
599 ret=DeleteFile (mono_string_chars (path));
601 *error=GetLastError ();
608 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
614 *error=ERROR_SUCCESS;
616 ret=GetFileAttributes (mono_string_chars (path));
619 * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
620 * headers is wrong, hence this temporary workaround.
622 * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
625 /* if(ret==INVALID_FILE_ATTRIBUTES) { */
626 *error=GetLastError ();
633 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
640 *error=ERROR_SUCCESS;
642 ret=SetFileAttributes (mono_string_chars (path),
643 convert_attrs (attrs));
645 *error=GetLastError ();
652 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
658 *error=ERROR_SUCCESS;
660 ret=GetFileType (handle);
661 if(ret==FILE_TYPE_UNKNOWN) {
662 /* Not necessarily an error, but the caller will have
663 * to decide based on the error value.
665 *error=GetLastError ();
672 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
676 WIN32_FILE_ATTRIBUTE_DATA data;
680 *error=ERROR_SUCCESS;
682 result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data);
685 convert_win32_file_attribute_data (&data,
686 mono_string_chars (path),
689 *error=GetLastError ();
696 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
697 gint32 access_mode, gint32 share, gint32 options,
705 *error=ERROR_SUCCESS;
708 if (options & FileOptions_Encrypted)
709 attributes = FILE_ATTRIBUTE_ENCRYPTED;
711 attributes = FILE_ATTRIBUTE_NORMAL;
712 if (options & FileOptions_DeleteOnClose)
713 attributes |= FILE_FLAG_DELETE_ON_CLOSE;
714 if (options & FileOptions_SequentialScan)
715 attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
716 if (options & FileOptions_RandomAccess)
717 attributes |= FILE_FLAG_RANDOM_ACCESS;
719 /* Not sure if we should set FILE_FLAG_OVERLAPPED, how does this mix with the "Async" bool here? */
720 if (options & FileOptions_Asynchronous)
721 attributes |= FILE_FLAG_OVERLAPPED;
723 if (options & FileOptions_WriteThrough)
724 attributes |= FILE_FLAG_WRITE_THROUGH;
726 attributes = FILE_ATTRIBUTE_NORMAL;
729 ret=CreateFile (mono_string_chars (filename),
730 convert_access (access_mode), convert_share (share),
731 NULL, convert_mode (mode),
734 if(ret==INVALID_HANDLE_VALUE) {
735 *error=GetLastError ();
742 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
748 *error=ERROR_SUCCESS;
750 ret=CloseHandle (handle);
752 *error=GetLastError ();
759 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
760 gint32 dest_offset, gint32 count,
769 *error=ERROR_SUCCESS;
771 if (dest_offset + count > mono_array_length (dest))
774 buffer = mono_array_addr (dest, guchar, dest_offset);
775 result = ReadFile (handle, buffer, count, &n, NULL);
778 *error=GetLastError ();
786 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
787 gint32 src_offset, gint32 count,
796 *error=ERROR_SUCCESS;
798 if (src_offset + count > mono_array_length (src))
801 buffer = mono_array_addr (src, guchar, src_offset);
802 result = WriteFile (handle, buffer, count, &n, NULL);
805 *error=GetLastError ();
813 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
820 *error=ERROR_SUCCESS;
822 offset_hi = offset >> 32;
823 offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
824 convert_seekorigin (origin));
826 if(offset==INVALID_SET_FILE_POINTER) {
827 *error=GetLastError ();
830 return offset | ((gint64)offset_hi << 32);
834 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
840 *error=ERROR_SUCCESS;
842 ret=FlushFileBuffers (handle);
844 *error=GetLastError ();
851 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
858 *error=ERROR_SUCCESS;
860 length = GetFileSize (handle, &length_hi);
861 if(length==INVALID_FILE_SIZE) {
862 *error=GetLastError ();
865 return length | ((gint64)length_hi << 32);
869 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
872 gint64 offset, offset_set;
879 *error=ERROR_SUCCESS;
881 /* save file pointer */
884 offset = SetFilePointer (handle, 0, &offset_hi, FILE_CURRENT);
885 if(offset==INVALID_SET_FILE_POINTER) {
886 *error=GetLastError ();
890 /* extend or truncate */
892 length_hi = length >> 32;
893 offset_set=SetFilePointer (handle, length & 0xFFFFFFFF, &length_hi,
895 if(offset_set==INVALID_SET_FILE_POINTER) {
896 *error=GetLastError ();
900 result = SetEndOfFile (handle);
902 *error=GetLastError ();
906 /* restore file pointer */
908 offset_set=SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
910 if(offset_set==INVALID_SET_FILE_POINTER) {
911 *error=GetLastError ();
919 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
920 gint64 last_access_time,
921 gint64 last_write_time, gint32 *error)
924 const FILETIME *creation_filetime;
925 const FILETIME *last_access_filetime;
926 const FILETIME *last_write_filetime;
930 *error=ERROR_SUCCESS;
932 if (creation_time < 0)
933 creation_filetime = NULL;
935 creation_filetime = (FILETIME *)&creation_time;
937 if (last_access_time < 0)
938 last_access_filetime = NULL;
940 last_access_filetime = (FILETIME *)&last_access_time;
942 if (last_write_time < 0)
943 last_write_filetime = NULL;
945 last_write_filetime = (FILETIME *)&last_write_time;
947 ret=SetFileTime (handle, creation_filetime, last_access_filetime, last_write_filetime);
949 *error=GetLastError ();
956 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
960 return GetStdHandle (STD_OUTPUT_HANDLE);
964 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
968 return GetStdHandle (STD_INPUT_HANDLE);
972 ves_icall_System_IO_MonoIO_get_ConsoleError ()
976 return GetStdHandle (STD_ERROR_HANDLE);
980 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle,
981 HANDLE *write_handle)
983 SECURITY_ATTRIBUTES attr;
988 attr.nLength=sizeof(SECURITY_ATTRIBUTES);
989 attr.bInheritHandle=TRUE;
990 attr.lpSecurityDescriptor=NULL;
992 ret=CreatePipe (read_handle, write_handle, &attr, 0);
994 /* FIXME: throw an exception? */
1002 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
1004 #if defined (PLATFORM_WIN32)
1005 return (gunichar2) ':'; /* colon */
1007 return (gunichar2) '/'; /* forward slash */
1012 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
1014 #if defined (PLATFORM_WIN32)
1015 return (gunichar2) '\\'; /* backslash */
1017 return (gunichar2) '/'; /* forward slash */
1022 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
1024 #if defined (PLATFORM_WIN32)
1025 return (gunichar2) '/'; /* forward slash */
1027 return (gunichar2) '/'; /* slash, same as DirectorySeparatorChar */
1032 ves_icall_System_IO_MonoIO_get_PathSeparator ()
1034 #if defined (PLATFORM_WIN32)
1035 return (gunichar2) ';'; /* semicolon */
1037 return (gunichar2) ':'; /* colon */
1041 static const gunichar2
1042 invalid_path_chars [] = {
1043 #if defined (PLATFORM_WIN32)
1044 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */
1045 0x003c, /* less than */
1046 0x003e, /* greater than */
1063 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
1069 MONO_ARCH_SAVE_REGS;
1071 domain = mono_domain_get ();
1072 n = sizeof (invalid_path_chars) / sizeof (gunichar2);
1073 chars = mono_array_new (domain, mono_defaults.char_class, n);
1075 for (i = 0; i < n; ++ i)
1076 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
1082 ves_icall_System_IO_MonoIO_GetTempPath (MonoString **mono_name)
1087 name=g_new0 (gunichar2, 256);
1089 ret=GetTempPath (256, name);
1091 /* Buffer was too short. Try again... */
1093 name=g_new0 (gunichar2, ret+2); /* include the terminator */
1094 ret=GetTempPath (ret, name);
1099 g_message (G_GNUC_PRETTY_FUNCTION
1100 ": Temp path is [%s] (len %d)", name, ret);
1103 *mono_name=mono_string_new_utf16 (mono_domain_get (), name,
1112 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
1113 gint64 length, gint32 *error)
1117 *error=ERROR_SUCCESS;
1119 ret=LockFile (handle, position & 0xFFFFFFFF, position >> 32,
1120 length & 0xFFFFFFFF, length >> 32);
1122 *error = GetLastError ();
1126 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
1127 gint64 length, gint32 *error)
1131 *error=ERROR_SUCCESS;
1133 ret=UnlockFile (handle, position & 0xFFFFFFFF, position >> 32,
1134 length & 0xFFFFFFFF, length >> 32);
1136 *error = GetLastError ();