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 /* force dangling symlinks or symlinks to directories
346 * to be returned as a regular file (see bug 79733)
348 buf.st_mode |= ~S_IFDIR;
351 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
352 if (S_ISSOCK (buf.st_mode))
353 buf.st_mode &= ~S_IFSOCK; /* don't consider socket protection */
356 if (S_ISDIR (buf.st_mode))
357 file_attrs |= FILE_ATTRIBUTE_DIRECTORY;
359 file_attrs |= FILE_ATTRIBUTE_ARCHIVE;
361 if ((buf.st_mode & S_IWUSR) == 0)
362 file_attrs |= FILE_ATTRIBUTE_READONLY;
364 if (*filename == '.')
365 file_attrs |= FILE_ATTRIBUTE_HIDDEN;
372 test_file (const char *filename, int attrs, int mask)
376 file_attr = get_file_attributes (filename);
377 if (file_attr == FALSE)
380 return ((file_attr & mask) == attrs);
383 /* scandir using glib */
385 mono_io_scandir (const gchar *dirname, const gchar *pattern, int attrs,
386 int mask, gchar ***namelist)
388 GError *error = NULL;
393 GPatternSpec *patspec;
396 mask = convert_attrs (mask);
398 dir = g_dir_open (dirname, 0, &error);
400 /* g_dir_open returns ENOENT on directories on which we don't
401 * have read/x permission */
402 gint errnum = get_error_from_g_file_error (error->code);
403 g_error_free (error);
404 if (errnum == ERROR_FILE_NOT_FOUND && g_file_test (dirname, G_FILE_TEST_IS_DIR))
405 errnum = ERROR_ACCESS_DENIED;
407 SetLastError (errnum);
411 patspec = g_pattern_spec_new (pattern);
412 names = g_ptr_array_new ();
413 while ((name = g_dir_read_name (dir)) != NULL) {
414 if (!g_pattern_match_string (patspec, name))
417 full_name = g_build_filename (dirname, name, NULL);
418 if (FALSE == test_file (full_name, attrs, mask)) {
423 g_ptr_array_add (names, full_name);
426 g_pattern_spec_free (patspec);
430 g_ptr_array_sort (names, file_compare);
431 g_ptr_array_set_size (names, result + 1);
433 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
435 g_ptr_array_free (names, TRUE);
442 ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *_path, MonoString *_pattern,
443 gint attrs, gint mask, gint32 *error)
452 MonoString *str_name;
453 #ifndef PLATFORM_WIN32
460 *error = ERROR_SUCCESS;
462 path = mono_string_to_utf8 (_path);
463 pattern = mono_string_to_utf8 (_pattern);
464 nnames = mono_io_scandir (path, pattern, attrs, mask, &namelist);
466 *error = GetLastError ();
472 domain = mono_domain_get ();
473 result = mono_array_new (domain, mono_defaults.string_class, nnames);
475 for (i = 0; i < nnames; i++) {
477 str_name = mono_string_new (domain, namelist [i]);
479 utf16 = mono_unicode_from_external (namelist [i], &nbytes);
481 g_message ("Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n",
486 str_name = mono_string_from_utf16 (utf16);
489 mono_array_setref (result, i - removed, str_name);
494 shrinked = mono_array_new (domain, mono_defaults.string_class, nnames - removed);
495 for (i = 0; i < (nnames - removed); i++) {
497 str = mono_array_get (result, MonoString *, i);
498 mono_array_setref (shrinked, i, str);
503 g_strfreev (namelist);
510 ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error)
519 buf = g_new (gunichar2, len);
521 *error=ERROR_SUCCESS;
524 if (GetCurrentDirectory (len, buf) > 0) {
529 result = mono_string_new_utf16 (mono_domain_get (), buf, len);
531 *error=GetLastError ();
539 ves_icall_System_IO_MonoIO_SetCurrentDirectory (MonoString *path,
546 *error=ERROR_SUCCESS;
548 ret=SetCurrentDirectory (mono_string_chars (path));
550 *error=GetLastError ();
557 ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest,
564 *error=ERROR_SUCCESS;
566 ret=MoveFile (mono_string_chars (path), mono_string_chars (dest));
568 *error=GetLastError ();
575 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
576 MonoBoolean overwrite, gint32 *error)
582 *error=ERROR_SUCCESS;
584 ret=CopyFile (mono_string_chars (path), mono_string_chars (dest), !overwrite);
586 *error=GetLastError ();
593 ves_icall_System_IO_MonoIO_DeleteFile (MonoString *path, gint32 *error)
599 *error=ERROR_SUCCESS;
601 ret=DeleteFile (mono_string_chars (path));
603 *error=GetLastError ();
610 ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
616 *error=ERROR_SUCCESS;
618 ret=GetFileAttributes (mono_string_chars (path));
621 * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
622 * headers is wrong, hence this temporary workaround.
624 * http://cygwin.com/ml/cygwin/2003-09/msg01771.html
627 /* if(ret==INVALID_FILE_ATTRIBUTES) { */
628 *error=GetLastError ();
635 ves_icall_System_IO_MonoIO_SetFileAttributes (MonoString *path, gint32 attrs,
642 *error=ERROR_SUCCESS;
644 ret=SetFileAttributes (mono_string_chars (path),
645 convert_attrs (attrs));
647 *error=GetLastError ();
654 ves_icall_System_IO_MonoIO_GetFileType (HANDLE handle, gint32 *error)
660 *error=ERROR_SUCCESS;
662 ret=GetFileType (handle);
663 if(ret==FILE_TYPE_UNKNOWN) {
664 /* Not necessarily an error, but the caller will have
665 * to decide based on the error value.
667 *error=GetLastError ();
674 ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
678 WIN32_FILE_ATTRIBUTE_DATA data;
682 *error=ERROR_SUCCESS;
684 result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data);
687 convert_win32_file_attribute_data (&data,
688 mono_string_chars (path),
691 *error=GetLastError ();
698 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
699 gint32 access_mode, gint32 share, gint32 options,
703 int attributes, attrs;
704 gunichar2 *chars = mono_string_chars (filename);
708 *error=ERROR_SUCCESS;
711 if (options & FileOptions_Encrypted)
712 attributes = FILE_ATTRIBUTE_ENCRYPTED;
714 attributes = FILE_ATTRIBUTE_NORMAL;
715 if (options & FileOptions_DeleteOnClose)
716 attributes |= FILE_FLAG_DELETE_ON_CLOSE;
717 if (options & FileOptions_SequentialScan)
718 attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
719 if (options & FileOptions_RandomAccess)
720 attributes |= FILE_FLAG_RANDOM_ACCESS;
722 /* Not sure if we should set FILE_FLAG_OVERLAPPED, how does this mix with the "Async" bool here? */
723 if (options & FileOptions_Asynchronous)
724 attributes |= FILE_FLAG_OVERLAPPED;
726 if (options & FileOptions_WriteThrough)
727 attributes |= FILE_FLAG_WRITE_THROUGH;
729 attributes = FILE_ATTRIBUTE_NORMAL;
731 /* If we're opening a directory we need to set the extra flag
733 attrs = GetFileAttributes (chars);
734 if (attrs != INVALID_FILE_ATTRIBUTES) {
735 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
736 attributes |= FILE_FLAG_BACKUP_SEMANTICS;
740 ret=CreateFile (chars, convert_access (access_mode),
741 convert_share (share), NULL, convert_mode (mode),
743 if(ret==INVALID_HANDLE_VALUE) {
744 *error=GetLastError ();
751 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
757 *error=ERROR_SUCCESS;
759 ret=CloseHandle (handle);
761 *error=GetLastError ();
768 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
769 gint32 dest_offset, gint32 count,
778 *error=ERROR_SUCCESS;
780 if (dest_offset + count > mono_array_length (dest))
783 buffer = mono_array_addr (dest, guchar, dest_offset);
784 result = ReadFile (handle, buffer, count, &n, NULL);
787 *error=GetLastError ();
795 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
796 gint32 src_offset, gint32 count,
805 *error=ERROR_SUCCESS;
807 if (src_offset + count > mono_array_length (src))
810 buffer = mono_array_addr (src, guchar, src_offset);
811 result = WriteFile (handle, buffer, count, &n, NULL);
814 *error=GetLastError ();
822 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
829 *error=ERROR_SUCCESS;
831 offset_hi = offset >> 32;
832 offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
833 convert_seekorigin (origin));
835 if(offset==INVALID_SET_FILE_POINTER) {
836 *error=GetLastError ();
839 return offset | ((gint64)offset_hi << 32);
843 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
849 *error=ERROR_SUCCESS;
851 ret=FlushFileBuffers (handle);
853 *error=GetLastError ();
860 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
867 *error=ERROR_SUCCESS;
869 length = GetFileSize (handle, &length_hi);
870 if(length==INVALID_FILE_SIZE) {
871 *error=GetLastError ();
874 return length | ((gint64)length_hi << 32);
878 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
881 gint64 offset, offset_set;
888 *error=ERROR_SUCCESS;
890 /* save file pointer */
893 offset = SetFilePointer (handle, 0, &offset_hi, FILE_CURRENT);
894 if(offset==INVALID_SET_FILE_POINTER) {
895 *error=GetLastError ();
899 /* extend or truncate */
901 length_hi = length >> 32;
902 offset_set=SetFilePointer (handle, length & 0xFFFFFFFF, &length_hi,
904 if(offset_set==INVALID_SET_FILE_POINTER) {
905 *error=GetLastError ();
909 result = SetEndOfFile (handle);
911 *error=GetLastError ();
915 /* restore file pointer */
917 offset_set=SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
919 if(offset_set==INVALID_SET_FILE_POINTER) {
920 *error=GetLastError ();
928 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
929 gint64 last_access_time,
930 gint64 last_write_time, gint32 *error)
933 const FILETIME *creation_filetime;
934 const FILETIME *last_access_filetime;
935 const FILETIME *last_write_filetime;
939 *error=ERROR_SUCCESS;
941 if (creation_time < 0)
942 creation_filetime = NULL;
944 creation_filetime = (FILETIME *)&creation_time;
946 if (last_access_time < 0)
947 last_access_filetime = NULL;
949 last_access_filetime = (FILETIME *)&last_access_time;
951 if (last_write_time < 0)
952 last_write_filetime = NULL;
954 last_write_filetime = (FILETIME *)&last_write_time;
956 ret=SetFileTime (handle, creation_filetime, last_access_filetime, last_write_filetime);
958 *error=GetLastError ();
965 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
969 return GetStdHandle (STD_OUTPUT_HANDLE);
973 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
977 return GetStdHandle (STD_INPUT_HANDLE);
981 ves_icall_System_IO_MonoIO_get_ConsoleError ()
985 return GetStdHandle (STD_ERROR_HANDLE);
989 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle,
990 HANDLE *write_handle)
992 SECURITY_ATTRIBUTES attr;
997 attr.nLength=sizeof(SECURITY_ATTRIBUTES);
998 attr.bInheritHandle=TRUE;
999 attr.lpSecurityDescriptor=NULL;
1001 ret=CreatePipe (read_handle, write_handle, &attr, 0);
1003 /* FIXME: throw an exception? */
1011 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
1013 #if defined (PLATFORM_WIN32)
1014 return (gunichar2) ':'; /* colon */
1016 return (gunichar2) '/'; /* forward slash */
1021 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
1023 #if defined (PLATFORM_WIN32)
1024 return (gunichar2) '\\'; /* backslash */
1026 return (gunichar2) '/'; /* forward slash */
1031 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
1033 #if defined (PLATFORM_WIN32)
1034 return (gunichar2) '/'; /* forward slash */
1036 return (gunichar2) '/'; /* slash, same as DirectorySeparatorChar */
1041 ves_icall_System_IO_MonoIO_get_PathSeparator ()
1043 #if defined (PLATFORM_WIN32)
1044 return (gunichar2) ';'; /* semicolon */
1046 return (gunichar2) ':'; /* colon */
1050 static const gunichar2
1051 invalid_path_chars [] = {
1052 #if defined (PLATFORM_WIN32)
1053 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */
1054 0x003c, /* less than */
1055 0x003e, /* greater than */
1072 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
1078 MONO_ARCH_SAVE_REGS;
1080 domain = mono_domain_get ();
1081 n = sizeof (invalid_path_chars) / sizeof (gunichar2);
1082 chars = mono_array_new (domain, mono_defaults.char_class, n);
1084 for (i = 0; i < n; ++ i)
1085 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
1091 ves_icall_System_IO_MonoIO_GetTempPath (MonoString **mono_name)
1096 name=g_new0 (gunichar2, 256);
1098 ret=GetTempPath (256, name);
1100 /* Buffer was too short. Try again... */
1102 name=g_new0 (gunichar2, ret+2); /* include the terminator */
1103 ret=GetTempPath (ret, name);
1108 g_message (G_GNUC_PRETTY_FUNCTION
1109 ": Temp path is [%s] (len %d)", name, ret);
1112 *mono_name=mono_string_new_utf16 (mono_domain_get (), name,
1121 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
1122 gint64 length, gint32 *error)
1126 *error=ERROR_SUCCESS;
1128 ret=LockFile (handle, position & 0xFFFFFFFF, position >> 32,
1129 length & 0xFFFFFFFF, length >> 32);
1131 *error = GetLastError ();
1135 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
1136 gint64 length, gint32 *error)
1140 *error=ERROR_SUCCESS;
1142 ret=UnlockFile (handle, position & 0xFFFFFFFF, position >> 32,
1143 length & 0xFFFFFFFF, length >> 32);
1145 *error = GetLastError ();