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,
698 MonoBoolean async, gint32 *error)
704 *error=ERROR_SUCCESS;
706 ret=CreateFile (mono_string_chars (filename),
707 convert_access (access_mode), convert_share (share),
708 NULL, convert_mode (mode),
709 FILE_ATTRIBUTE_NORMAL | ((async) ? FILE_FLAG_OVERLAPPED : 0),
711 if(ret==INVALID_HANDLE_VALUE) {
712 *error=GetLastError ();
719 ves_icall_System_IO_MonoIO_Close (HANDLE handle, gint32 *error)
725 *error=ERROR_SUCCESS;
727 ret=CloseHandle (handle);
729 *error=GetLastError ();
736 ves_icall_System_IO_MonoIO_Read (HANDLE handle, MonoArray *dest,
737 gint32 dest_offset, gint32 count,
746 *error=ERROR_SUCCESS;
748 if (dest_offset + count > mono_array_length (dest))
751 buffer = mono_array_addr (dest, guchar, dest_offset);
752 result = ReadFile (handle, buffer, count, &n, NULL);
755 *error=GetLastError ();
763 ves_icall_System_IO_MonoIO_Write (HANDLE handle, MonoArray *src,
764 gint32 src_offset, gint32 count,
773 *error=ERROR_SUCCESS;
775 if (src_offset + count > mono_array_length (src))
778 buffer = mono_array_addr (src, guchar, src_offset);
779 result = WriteFile (handle, buffer, count, &n, NULL);
782 *error=GetLastError ();
790 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
797 *error=ERROR_SUCCESS;
799 offset_hi = offset >> 32;
800 offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
801 convert_seekorigin (origin));
803 if(offset==INVALID_SET_FILE_POINTER) {
804 *error=GetLastError ();
807 return offset | ((gint64)offset_hi << 32);
811 ves_icall_System_IO_MonoIO_Flush (HANDLE handle, gint32 *error)
817 *error=ERROR_SUCCESS;
819 ret=FlushFileBuffers (handle);
821 *error=GetLastError ();
828 ves_icall_System_IO_MonoIO_GetLength (HANDLE handle, gint32 *error)
835 *error=ERROR_SUCCESS;
837 length = GetFileSize (handle, &length_hi);
838 if(length==INVALID_FILE_SIZE) {
839 *error=GetLastError ();
842 return length | ((gint64)length_hi << 32);
846 ves_icall_System_IO_MonoIO_SetLength (HANDLE handle, gint64 length,
849 gint64 offset, offset_set;
856 *error=ERROR_SUCCESS;
858 /* save file pointer */
861 offset = SetFilePointer (handle, 0, &offset_hi, FILE_CURRENT);
862 if(offset==INVALID_SET_FILE_POINTER) {
863 *error=GetLastError ();
867 /* extend or truncate */
869 length_hi = length >> 32;
870 offset_set=SetFilePointer (handle, length & 0xFFFFFFFF, &length_hi,
872 if(offset_set==INVALID_SET_FILE_POINTER) {
873 *error=GetLastError ();
877 result = SetEndOfFile (handle);
879 *error=GetLastError ();
883 /* restore file pointer */
885 offset_set=SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
887 if(offset_set==INVALID_SET_FILE_POINTER) {
888 *error=GetLastError ();
896 ves_icall_System_IO_MonoIO_SetFileTime (HANDLE handle, gint64 creation_time,
897 gint64 last_access_time,
898 gint64 last_write_time, gint32 *error)
901 const FILETIME *creation_filetime;
902 const FILETIME *last_access_filetime;
903 const FILETIME *last_write_filetime;
907 *error=ERROR_SUCCESS;
909 if (creation_time < 0)
910 creation_filetime = NULL;
912 creation_filetime = (FILETIME *)&creation_time;
914 if (last_access_time < 0)
915 last_access_filetime = NULL;
917 last_access_filetime = (FILETIME *)&last_access_time;
919 if (last_write_time < 0)
920 last_write_filetime = NULL;
922 last_write_filetime = (FILETIME *)&last_write_time;
924 ret=SetFileTime (handle, creation_filetime, last_access_filetime, last_write_filetime);
926 *error=GetLastError ();
933 ves_icall_System_IO_MonoIO_get_ConsoleOutput ()
937 return GetStdHandle (STD_OUTPUT_HANDLE);
941 ves_icall_System_IO_MonoIO_get_ConsoleInput ()
945 return GetStdHandle (STD_INPUT_HANDLE);
949 ves_icall_System_IO_MonoIO_get_ConsoleError ()
953 return GetStdHandle (STD_ERROR_HANDLE);
957 ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle,
958 HANDLE *write_handle)
960 SECURITY_ATTRIBUTES attr;
965 attr.nLength=sizeof(SECURITY_ATTRIBUTES);
966 attr.bInheritHandle=TRUE;
967 attr.lpSecurityDescriptor=NULL;
969 ret=CreatePipe (read_handle, write_handle, &attr, 0);
971 /* FIXME: throw an exception? */
979 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
981 #if defined (PLATFORM_WIN32)
982 return (gunichar2) ':'; /* colon */
984 return (gunichar2) '/'; /* forward slash */
989 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
991 #if defined (PLATFORM_WIN32)
992 return (gunichar2) '\\'; /* backslash */
994 return (gunichar2) '/'; /* forward slash */
999 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
1001 #if defined (PLATFORM_WIN32)
1002 return (gunichar2) '/'; /* forward slash */
1004 return (gunichar2) '/'; /* slash, same as DirectorySeparatorChar */
1009 ves_icall_System_IO_MonoIO_get_PathSeparator ()
1011 #if defined (PLATFORM_WIN32)
1012 return (gunichar2) ';'; /* semicolon */
1014 return (gunichar2) ':'; /* colon */
1018 static const gunichar2
1019 invalid_path_chars [] = {
1020 #if defined (PLATFORM_WIN32)
1021 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */
1022 0x003c, /* less than */
1023 0x003e, /* greater than */
1040 ves_icall_System_IO_MonoIO_get_InvalidPathChars ()
1046 MONO_ARCH_SAVE_REGS;
1048 domain = mono_domain_get ();
1049 n = sizeof (invalid_path_chars) / sizeof (gunichar2);
1050 chars = mono_array_new (domain, mono_defaults.char_class, n);
1052 for (i = 0; i < n; ++ i)
1053 mono_array_set (chars, gunichar2, i, invalid_path_chars [i]);
1059 ves_icall_System_IO_MonoIO_GetTempPath (MonoString **mono_name)
1064 name=g_new0 (gunichar2, 256);
1066 ret=GetTempPath (256, name);
1068 /* Buffer was too short. Try again... */
1070 name=g_new0 (gunichar2, ret+2); /* include the terminator */
1071 ret=GetTempPath (ret, name);
1076 g_message (G_GNUC_PRETTY_FUNCTION
1077 ": Temp path is [%s] (len %d)", name, ret);
1080 *mono_name=mono_string_new_utf16 (mono_domain_get (), name,
1089 void ves_icall_System_IO_MonoIO_Lock (HANDLE handle, gint64 position,
1090 gint64 length, gint32 *error)
1094 *error=ERROR_SUCCESS;
1096 ret=LockFile (handle, position & 0xFFFFFFFF, position >> 32,
1097 length & 0xFFFFFFFF, length >> 32);
1099 *error = GetLastError ();
1103 void ves_icall_System_IO_MonoIO_Unlock (HANDLE handle, gint64 position,
1104 gint64 length, gint32 *error)
1108 *error=ERROR_SUCCESS;
1110 ret=UnlockFile (handle, position & 0xFFFFFFFF, position >> 32,
1111 length & 0xFFFFFFFF, length >> 32);
1113 *error = GetLastError ();