X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Ffile-io.c;h=8e250b72624d95a259e95ad5da3e17f8921be9e5;hb=5763d1dafd43f4bde3e386a898e7fe9304d6ae86;hp=0f2e6c78cea896a2958632e18d42c578a8dd38d6;hpb=b6b13e72e91d5b529a6306ce53bda685932c77db;p=mono.git diff --git a/mono/metadata/file-io.c b/mono/metadata/file-io.c index 0f2e6c78cea..8e250b72624 100644 --- a/mono/metadata/file-io.c +++ b/mono/metadata/file-io.c @@ -10,11 +10,14 @@ */ #include + #include #include #include #include +#ifdef HAVE_UNISTD_H #include +#endif #ifdef HAVE_SYS_STAT_H #include #endif @@ -93,28 +96,25 @@ static guint32 convert_access(MonoFileAccess mono_access) static guint32 convert_share(MonoFileShare mono_share) { - guint32 share; + guint32 share = 0; - switch(mono_share) { - case FileShare_None: - share=0; - break; - case FileShare_Read: - share=FILE_SHARE_READ; - break; - case FileShare_Write: - share=FILE_SHARE_WRITE; - break; - case FileShare_ReadWrite: - share=FILE_SHARE_READ|FILE_SHARE_WRITE; - break; - default: + if (mono_share & FileShare_Read) { + share |= FILE_SHARE_READ; + } + if (mono_share & FileShare_Write) { + share |= FILE_SHARE_WRITE; + } + if (mono_share & FileShare_Delete) { + share |= FILE_SHARE_DELETE; + } + + if (mono_share & ~(FileShare_Read|FileShare_Write|FileShare_Delete)) { g_warning("System.IO.FileShare has unknown value 0x%x", mono_share); /* Safe fallback */ share=0; } - + return(share); } @@ -203,6 +203,73 @@ static guint32 convert_attrs(MonoFileAttributes attrs) return(attrs); } +/* + * On Win32, GetFileAttributes|Ex () seems to try opening the file, + * which might lead to sharing violation errors, whereas FindFirstFile + * always succeeds. These 2 wrappers resort to FindFirstFile if + * GetFileAttributes|Ex () has failed. + */ +static guint32 +get_file_attributes (const gunichar2 *path) +{ + guint32 res; + WIN32_FIND_DATA find_data; + HANDLE find_handle; + gint32 error; + + res = GetFileAttributes (path); + if (res != -1) + return res; + + error = GetLastError (); + + if (error != ERROR_SHARING_VIOLATION) + return res; + + find_handle = FindFirstFile (path, &find_data); + + if (find_handle == INVALID_HANDLE_VALUE) + return res; + + FindClose (find_handle); + + return find_data.dwFileAttributes; +} + +static gboolean +get_file_attributes_ex (const gunichar2 *path, WIN32_FILE_ATTRIBUTE_DATA *data) +{ + gboolean res; + WIN32_FIND_DATA find_data; + HANDLE find_handle; + gint32 error; + + res = GetFileAttributesEx (path, GetFileExInfoStandard, data); + if (res) + return TRUE; + + error = GetLastError (); + + if (error != ERROR_SHARING_VIOLATION) + return FALSE; + + find_handle = FindFirstFile (path, &find_data); + + if (find_handle == INVALID_HANDLE_VALUE) + return FALSE; + + FindClose (find_handle); + + data->dwFileAttributes = find_data.dwFileAttributes; + data->ftCreationTime = find_data.ftCreationTime; + data->ftLastAccessTime = find_data.ftLastAccessTime; + data->ftLastWriteTime = find_data.ftLastWriteTime; + data->nFileSizeHigh = find_data.nFileSizeHigh; + data->nFileSizeLow = find_data.nFileSizeLow; + + return TRUE; +} + /* System.IO.MonoIO internal calls */ MonoBoolean @@ -239,268 +306,83 @@ ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error) return(ret); } -static gint -get_error_from_g_file_error (gint error) -{ - switch (error) { - case G_FILE_ERROR_ACCES: - error = ERROR_ACCESS_DENIED; - break; - case G_FILE_ERROR_NAMETOOLONG: - error = ERROR_FILENAME_EXCED_RANGE; - break; - case G_FILE_ERROR_NOENT: - error = ERROR_FILE_NOT_FOUND; - break; - case G_FILE_ERROR_NOTDIR: - error = ERROR_FILE_NOT_FOUND; - break; - case G_FILE_ERROR_ROFS: - error = ERROR_ACCESS_DENIED; - break; - case G_FILE_ERROR_TXTBSY: - error = ERROR_SHARING_VIOLATION; - break; - case G_FILE_ERROR_NOSPC: - error = ERROR_HANDLE_DISK_FULL; - break; - case G_FILE_ERROR_NFILE: - case G_FILE_ERROR_MFILE: - error = ERROR_TOO_MANY_OPEN_FILES; - break; - case G_FILE_ERROR_BADF: - error = ERROR_INVALID_HANDLE; - break; - case G_FILE_ERROR_INVAL: - error = ERROR_INVALID_PARAMETER; - break; - case G_FILE_ERROR_AGAIN: - error = ERROR_SHARING_VIOLATION; - break; - case G_FILE_ERROR_INTR: - error = ERROR_IO_PENDING; - break; - case G_FILE_ERROR_PERM: - error = ERROR_ACCESS_DENIED; - break; - case G_FILE_ERROR_FAILED: - error = ERROR_INVALID_PARAMETER; - break; - case G_FILE_ERROR_NXIO: - case G_FILE_ERROR_NOMEM: - case G_FILE_ERROR_NODEV: - case G_FILE_ERROR_FAULT: - case G_FILE_ERROR_LOOP: - case G_FILE_ERROR_PIPE: - case G_FILE_ERROR_IO: - default: - error = ERROR_GEN_FAILURE; - break; - - } - - return error; -} - -static gint -file_compare (gconstpointer a, gconstpointer b) -{ - gchar *astr = *(gchar **) a; - gchar *bstr = *(gchar **) b; - - return strcmp (astr, bstr); -} - -static gint -get_file_attributes (const char *filename) +MonoArray * +ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path, + MonoString *path_with_pattern, + gint attrs, gint mask, + gint32 *error) { -#ifdef PLATFORM_WIN32 - gunichar2 *full16; - gint result; - - full16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL); - if (full16 == NULL) { - g_message ("Bad encoding for '%s'\n", filename); - return FALSE; - } + MonoDomain *domain; + MonoArray *result; + int i; + WIN32_FIND_DATA data; + HANDLE find_handle; + GPtrArray *names; + gchar *utf8_path, *utf8_result, *full_name; + + MONO_ARCH_SAVE_REGS; - result = GetFileAttributes (full16); - g_free (full16); - return result; -#else - struct stat buf; - struct stat linkbuf; - int result; - int file_attrs; + *error = ERROR_SUCCESS; - result = lstat (filename, &buf); - if (result == -1) - return FALSE; + domain = mono_domain_get (); + mask = convert_attrs (mask); + + find_handle = FindFirstFile (mono_string_chars (path_with_pattern), + &data); + if (find_handle == INVALID_HANDLE_VALUE) { + gint32 find_error = GetLastError (); + + if (find_error == ERROR_FILE_NOT_FOUND) { + /* No files, so just return an empty array */ + result = mono_array_new (domain, + mono_defaults.string_class, + 0); - if (S_ISLNK (buf.st_mode)) { - result = stat (filename, &linkbuf); - if (result != -1) { - buf = linkbuf; - } else { - buf.st_mode |= ~S_IFDIR; /* force it to be returned as regular file */ + return(result); } + + *error = find_error; + return(NULL); } - /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */ - if (S_ISSOCK (buf.st_mode)) - buf.st_mode &= ~S_IFSOCK; /* don't consider socket protection */ - - file_attrs = 0; - if (S_ISDIR (buf.st_mode)) - file_attrs |= FILE_ATTRIBUTE_DIRECTORY; - else - file_attrs |= FILE_ATTRIBUTE_ARCHIVE; - - if ((buf.st_mode & S_IWUSR) == 0) - file_attrs |= FILE_ATTRIBUTE_READONLY; - - if (*filename == '.') - file_attrs |= FILE_ATTRIBUTE_HIDDEN; - - return file_attrs; -#endif -} - -static gboolean -test_file (const char *filename, int attrs, int mask) -{ - int file_attr; - - file_attr = get_file_attributes (filename); - if (file_attr == FALSE) - return FALSE; - - return ((file_attr & mask) == attrs); -} - -/* scandir using glib */ -static gint -mono_io_scandir (const gchar *dirname, const gchar *pattern, int attrs, - int mask, gchar ***namelist) -{ - GError *error = NULL; - GDir *dir; - GPtrArray *names; - const gchar *name; - gint result; - GPatternSpec *patspec; - gchar *full_name; - - mask = convert_attrs (mask); - *namelist = NULL; - dir = g_dir_open (dirname, 0, &error); - if (dir == NULL) { - /* g_dir_open returns ENOENT on directories on which we don't - * have read/x permission */ - gint errnum = get_error_from_g_file_error (error->code); - g_error_free (error); - if (errnum == ERROR_FILE_NOT_FOUND && g_file_test (dirname, G_FILE_TEST_IS_DIR)) - errnum = ERROR_ACCESS_DENIED; - - SetLastError (errnum); - return -1; - } - - patspec = g_pattern_spec_new (pattern); names = g_ptr_array_new (); - while ((name = g_dir_read_name (dir)) != NULL) { - if (!g_pattern_match_string (patspec, name)) - continue; + utf8_path = mono_string_to_utf8 (path); - full_name = g_build_filename (dirname, name, NULL); - if (FALSE == test_file (full_name, attrs, mask)) { - g_free (full_name); + do { + if ((data.cFileName[0] == '.' && data.cFileName[1] == 0) || + (data.cFileName[0] == '.' && data.cFileName[1] == '.' && data.cFileName[2] == 0)) { continue; } + + if ((data.dwFileAttributes & mask) == attrs) { + utf8_result = g_utf16_to_utf8 (data.cFileName, -1, NULL, NULL, NULL); + if (utf8_result == NULL) { + continue; + } + + full_name = g_build_filename (utf8_path, utf8_result, NULL); + g_ptr_array_add (names, full_name); + + g_free (utf8_result); + } + } while(FindNextFile (find_handle, &data)); - g_ptr_array_add (names, full_name); - } - - g_pattern_spec_free (patspec); - g_dir_close (dir); - result = names->len; - if (result > 0) { - g_ptr_array_sort (names, file_compare); - g_ptr_array_set_size (names, result + 1); - - *namelist = (gchar **) g_ptr_array_free (names, FALSE); - } else { - g_ptr_array_free (names, TRUE); - } - - return result; -} - -MonoArray * -ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *_path, MonoString *_pattern, - gint attrs, gint mask, gint32 *error) -{ - MonoDomain *domain; - MonoArray *result; - gchar **namelist; - gchar *path; - gchar *pattern; - int i, nnames; - int removed; - MonoString *str_name; -#ifndef PLATFORM_WIN32 - gunichar2 *utf16; - gsize nbytes; -#endif - - MONO_ARCH_SAVE_REGS; - - *error = ERROR_SUCCESS; - - path = mono_string_to_utf8 (_path); - pattern = mono_string_to_utf8 (_pattern); - nnames = mono_io_scandir (path, pattern, attrs, mask, &namelist); - if (nnames < 0) { + if (FindClose (find_handle) == FALSE) { *error = GetLastError (); - g_free (pattern); - g_free (path); - return NULL; - } - - domain = mono_domain_get (); - result = mono_array_new (domain, mono_defaults.string_class, nnames); - removed = 0; - for (i = 0; i < nnames; i++) { -#if PLATFORM_WIN32 - str_name = mono_string_new (domain, namelist [i]); -#else - utf16 = mono_unicode_from_external (namelist [i], &nbytes); - if (utf16 == NULL) { - g_message ("Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", - namelist [i]); - removed++; - continue; + result = NULL; + } else { + result = mono_array_new (domain, mono_defaults.string_class, names->len); + for (i = 0; i < names->len; i++) { + mono_array_setref (result, i, mono_string_new (domain, g_ptr_array_index (names, i))); } - str_name = mono_string_from_utf16 (utf16); - g_free (utf16); -#endif - mono_array_setref (result, i - removed, str_name); } - if (removed > 0) { - MonoArray *shrinked; - shrinked = mono_array_new (domain, mono_defaults.string_class, nnames - removed); - for (i = 0; i < (nnames - removed); i++) { - MonoString *str; - str = mono_array_get (result, MonoString *, i); - mono_array_setref (shrinked, i, str); - } - result = shrinked; + for (i = 0; i < names->len; i++) { + g_free (g_ptr_array_index (names, i)); } - - g_strfreev (namelist); - g_free (pattern); - g_free (path); + g_ptr_array_free (names, TRUE); + g_free (utf8_path); + return result; } @@ -569,6 +451,36 @@ ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, return(ret); } +MonoBoolean +ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName, + MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors, + gint32 *error) +{ + gboolean ret; + gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL; + guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH; + + MONO_ARCH_SAVE_REGS; + + if (sourceFileName) + utf16_sourceFileName = mono_string_chars (sourceFileName); + if (destinationFileName) + utf16_destinationFileName = mono_string_chars (destinationFileName); + if (destinationBackupFileName) + utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName); + + *error = ERROR_SUCCESS; + if (ignoreMetadataErrors) + replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS; + + ret = ReplaceFile (utf16_destinationFileName, utf16_sourceFileName, utf16_destinationBackupFileName, + replaceFlags, NULL, NULL); + if (ret == FALSE) + *error = GetLastError (); + + return ret; +} + MonoBoolean ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest, MonoBoolean overwrite, gint32 *error) @@ -613,7 +525,7 @@ ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error) *error=ERROR_SUCCESS; - ret=GetFileAttributes (mono_string_chars (path)); + ret=get_file_attributes (mono_string_chars (path)); /* * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32 @@ -679,7 +591,7 @@ ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, *error=ERROR_SUCCESS; - result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data); + result = get_file_attributes_ex (mono_string_chars (path), &data); if (result) { convert_win32_file_attribute_data (&data, @@ -698,7 +610,8 @@ ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, gint32 *error) { HANDLE ret; - int attributes; + int attributes, attrs; + gunichar2 *chars = mono_string_chars (filename); MONO_ARCH_SAVE_REGS; @@ -716,6 +629,9 @@ ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, if (options & FileOptions_RandomAccess) attributes |= FILE_FLAG_RANDOM_ACCESS; + if (options & FileOptions_Temporary) + attributes |= FILE_ATTRIBUTE_TEMPORARY; + /* Not sure if we should set FILE_FLAG_OVERLAPPED, how does this mix with the "Async" bool here? */ if (options & FileOptions_Asynchronous) attributes |= FILE_FLAG_OVERLAPPED; @@ -725,12 +641,18 @@ ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, } else attributes = FILE_ATTRIBUTE_NORMAL; - - ret=CreateFile (mono_string_chars (filename), - convert_access (access_mode), convert_share (share), - NULL, convert_mode (mode), - attributes, - NULL); + /* If we're opening a directory we need to set the extra flag + */ + attrs = get_file_attributes (chars); + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) { + attributes |= FILE_FLAG_BACKUP_SEMANTICS; + } + } + + ret=CreateFile (chars, convert_access (access_mode), + convert_share (share), NULL, convert_mode (mode), + attributes, NULL); if(ret==INVALID_HANDLE_VALUE) { *error=GetLastError (); }