11 #ifdef HAVE_SYS_STATVFS_H
12 #include <sys/statvfs.h>
14 #if defined(HAVE_SYS_STATFS_H)
15 #include <sys/statfs.h>
17 #if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
18 #include <sys/param.h>
19 #include <sys/mount.h>
21 #include <sys/types.h>
25 #include <sys/ioctl.h>
27 #include <mono/utils/linux_magic.h>
35 #include "w32file-internals.h"
37 #include "w32file-unix-glob.h"
38 #include "w32handle.h"
39 #include "utils/mono-io-portability.h"
40 #include "utils/mono-logger-internals.h"
41 #include "utils/mono-os-mutex.h"
42 #include "utils/mono-threads.h"
43 #include "utils/mono-threads-api.h"
44 #include "utils/strenc.h"
56 /* Currently used for both FILE, CONSOLE and PIPE handle types.
57 * This may have to change in future. */
60 FileShare *share_info; /* Pointer into shared mem */
62 guint32 security_attributes;
76 * If SHM is disabled, this will point to a hash of FileShare structures, otherwise
77 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
80 static GHashTable *file_share_table;
81 static mono_mutex_t file_share_mutex;
84 time_t_to_filetime (time_t timeval, FILETIME *filetime)
88 ticks = ((guint64)timeval * 10000000) + 116444736000000000ULL;
89 filetime->dwLowDateTime = ticks & 0xFFFFFFFF;
90 filetime->dwHighDateTime = ticks >> 32;
94 file_share_release (FileShare *share_info)
96 /* Prevent new entries racing with us */
97 mono_os_mutex_lock (&file_share_mutex);
99 g_assert (share_info->handle_refs > 0);
100 share_info->handle_refs -= 1;
102 if (share_info->handle_refs == 0)
103 g_hash_table_remove (file_share_table, share_info);
105 mono_os_mutex_unlock (&file_share_mutex);
109 file_share_equal (gconstpointer ka, gconstpointer kb)
111 const FileShare *s1 = (const FileShare *)ka;
112 const FileShare *s2 = (const FileShare *)kb;
114 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
118 file_share_hash (gconstpointer data)
120 const FileShare *s = (const FileShare *)data;
126 file_share_get (guint64 device, guint64 inode, guint32 new_sharemode, guint32 new_access,
127 guint32 *old_sharemode, guint32 *old_access, FileShare **share_info)
129 FileShare *file_share;
130 gboolean exists = FALSE;
132 /* Prevent new entries racing with us */
133 mono_os_mutex_lock (&file_share_mutex);
138 * Instead of allocating a 4MB array, we use a hash table to keep track of this
139 * info. This is needed even if SHM is disabled, to track sharing inside
140 * the current process.
142 if (!file_share_table)
143 file_share_table = g_hash_table_new_full (file_share_hash, file_share_equal, NULL, g_free);
148 file_share = (FileShare *)g_hash_table_lookup (file_share_table, &tmp);
150 *old_sharemode = file_share->sharemode;
151 *old_access = file_share->access;
152 *share_info = file_share;
154 g_assert (file_share->handle_refs > 0);
155 file_share->handle_refs += 1;
159 file_share = g_new0 (FileShare, 1);
161 file_share->device = device;
162 file_share->inode = inode;
163 file_share->opened_by_pid = wapi_getpid ();
164 file_share->sharemode = new_sharemode;
165 file_share->access = new_access;
166 file_share->handle_refs = 1;
167 *share_info = file_share;
169 g_hash_table_insert (file_share_table, file_share, file_share);
172 mono_os_mutex_unlock (&file_share_mutex);
178 _wapi_open (const gchar *pathname, gint flags, mode_t mode)
181 gchar *located_filename;
183 if (flags & O_CREAT) {
184 located_filename = mono_portability_find_file (pathname, FALSE);
185 if (located_filename == NULL) {
186 fd = open (pathname, flags, mode);
188 fd = open (located_filename, flags, mode);
189 g_free (located_filename);
192 fd = open (pathname, flags, mode);
193 if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
194 gint saved_errno = errno;
195 located_filename = mono_portability_find_file (pathname, TRUE);
197 if (located_filename == NULL) {
202 fd = open (located_filename, flags, mode);
203 g_free (located_filename);
211 _wapi_access (const gchar *pathname, gint mode)
215 ret = access (pathname, mode);
216 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
217 gint saved_errno = errno;
218 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
220 if (located_filename == NULL) {
225 ret = access (located_filename, mode);
226 g_free (located_filename);
233 _wapi_chmod (const gchar *pathname, mode_t mode)
237 ret = chmod (pathname, mode);
238 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
239 gint saved_errno = errno;
240 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
242 if (located_filename == NULL) {
247 ret = chmod (located_filename, mode);
248 g_free (located_filename);
255 _wapi_utime (const gchar *filename, const struct utimbuf *buf)
259 ret = utime (filename, buf);
260 if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) {
261 gint saved_errno = errno;
262 gchar *located_filename = mono_portability_find_file (filename, TRUE);
264 if (located_filename == NULL) {
269 ret = utime (located_filename, buf);
270 g_free (located_filename);
277 _wapi_unlink (const gchar *pathname)
281 ret = unlink (pathname);
282 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && IS_PORTABILITY_SET) {
283 gint saved_errno = errno;
284 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
286 if (located_filename == NULL) {
291 ret = unlink (located_filename);
292 g_free (located_filename);
299 _wapi_rename (const gchar *oldpath, const gchar *newpath)
302 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
304 if (located_newpath == NULL) {
305 ret = rename (oldpath, newpath);
307 ret = rename (oldpath, located_newpath);
309 if (ret == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && IS_PORTABILITY_SET) {
310 gint saved_errno = errno;
311 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
313 if (located_oldpath == NULL) {
314 g_free (located_oldpath);
315 g_free (located_newpath);
321 ret = rename (located_oldpath, located_newpath);
322 g_free (located_oldpath);
324 g_free (located_newpath);
331 _wapi_stat (const gchar *path, struct stat *buf)
335 ret = stat (path, buf);
336 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
337 gint saved_errno = errno;
338 gchar *located_filename = mono_portability_find_file (path, TRUE);
340 if (located_filename == NULL) {
345 ret = stat (located_filename, buf);
346 g_free (located_filename);
353 _wapi_lstat (const gchar *path, struct stat *buf)
357 ret = lstat (path, buf);
358 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
359 gint saved_errno = errno;
360 gchar *located_filename = mono_portability_find_file (path, TRUE);
362 if (located_filename == NULL) {
367 ret = lstat (located_filename, buf);
368 g_free (located_filename);
375 _wapi_mkdir (const gchar *pathname, mode_t mode)
378 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
380 if (located_filename == NULL) {
381 ret = mkdir (pathname, mode);
383 ret = mkdir (located_filename, mode);
384 g_free (located_filename);
391 _wapi_rmdir (const gchar *pathname)
395 ret = rmdir (pathname);
396 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
397 gint saved_errno = errno;
398 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
400 if (located_filename == NULL) {
405 ret = rmdir (located_filename);
406 g_free (located_filename);
413 _wapi_chdir (const gchar *path)
418 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
419 gint saved_errno = errno;
420 gchar *located_filename = mono_portability_find_file (path, TRUE);
422 if (located_filename == NULL) {
427 ret = chdir (located_filename);
428 g_free (located_filename);
435 _wapi_basename (const gchar *filename)
437 gchar *new_filename = g_strdup (filename), *ret;
439 if (IS_PORTABILITY_SET) {
440 g_strdelimit (new_filename, "\\", '/');
443 if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
444 gint len = strlen (new_filename);
446 g_memmove (new_filename, new_filename + 2, len - 2);
447 new_filename[len - 2] = '\0';
450 ret = g_path_get_basename (new_filename);
451 g_free (new_filename);
457 _wapi_dirname (const gchar *filename)
459 gchar *new_filename = g_strdup (filename), *ret;
461 if (IS_PORTABILITY_SET) {
462 g_strdelimit (new_filename, "\\", '/');
465 if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
466 gint len = strlen (new_filename);
468 g_memmove (new_filename, new_filename + 2, len - 2);
469 new_filename[len - 2] = '\0';
472 ret = g_path_get_dirname (new_filename);
473 g_free (new_filename);
479 _wapi_g_dir_open (const gchar *path, guint flags, GError **error)
483 ret = g_dir_open (path, flags, error);
484 if (ret == NULL && ((*error)->code == G_FILE_ERROR_NOENT || (*error)->code == G_FILE_ERROR_NOTDIR || (*error)->code == G_FILE_ERROR_NAMETOOLONG) && IS_PORTABILITY_SET) {
485 gchar *located_filename = mono_portability_find_file (path, TRUE);
486 GError *tmp_error = NULL;
488 if (located_filename == NULL) {
492 ret = g_dir_open (located_filename, flags, &tmp_error);
493 g_free (located_filename);
494 if (tmp_error == NULL) {
495 g_clear_error (error);
503 get_errno_from_g_file_error (gint error)
507 case G_FILE_ERROR_ACCES: return EACCES;
510 case G_FILE_ERROR_NAMETOOLONG: return ENAMETOOLONG;
513 case G_FILE_ERROR_NOENT: return ENOENT;
516 case G_FILE_ERROR_NOTDIR: return ENOTDIR;
519 case G_FILE_ERROR_NXIO: return ENXIO;
522 case G_FILE_ERROR_NODEV: return ENODEV;
525 case G_FILE_ERROR_ROFS: return EROFS;
528 case G_FILE_ERROR_TXTBSY: return ETXTBSY;
531 case G_FILE_ERROR_FAULT: return EFAULT;
534 case G_FILE_ERROR_LOOP: return ELOOP;
537 case G_FILE_ERROR_NOSPC: return ENOSPC;
540 case G_FILE_ERROR_NOMEM: return ENOMEM;
543 case G_FILE_ERROR_MFILE: return EMFILE;
546 case G_FILE_ERROR_NFILE: return ENFILE;
549 case G_FILE_ERROR_BADF: return EBADF;
552 case G_FILE_ERROR_INVAL: return EINVAL;
555 case G_FILE_ERROR_PIPE: return EPIPE;
558 case G_FILE_ERROR_AGAIN: return EAGAIN;
561 case G_FILE_ERROR_INTR: return EINTR;
564 case G_FILE_ERROR_IO: return EIO;
567 case G_FILE_ERROR_PERM: return EPERM;
569 case G_FILE_ERROR_FAILED: return ERROR_INVALID_PARAMETER;
571 g_assert_not_reached ();
576 file_compare (gconstpointer a, gconstpointer b)
578 gchar *astr = *(gchar **) a;
579 gchar *bstr = *(gchar **) b;
581 return strcmp (astr, bstr);
584 /* scandir using glib */
586 _wapi_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
588 GError *error = NULL;
592 mono_w32file_unix_glob_t glob_buf;
595 dir = _wapi_g_dir_open (dirname, 0, &error);
597 /* g_dir_open returns ENOENT on directories on which we don't
598 * have read/x permission */
599 gint errnum = get_errno_from_g_file_error (error->code);
600 g_error_free (error);
601 if (errnum == ENOENT &&
602 !_wapi_access (dirname, F_OK) &&
603 _wapi_access (dirname, R_OK|X_OK)) {
611 if (IS_PORTABILITY_CASE) {
612 flags = W32FILE_UNIX_GLOB_IGNORECASE;
615 result = mono_w32file_unix_glob (dir, pattern, flags, &glob_buf);
616 if (g_str_has_suffix (pattern, ".*")) {
617 /* Special-case the patterns ending in '.*', as
618 * windows also matches entries with no extension with
621 * TODO: should this be a MONO_IOMAP option?
623 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
627 result2 = mono_w32file_unix_glob (dir, pattern2, flags | W32FILE_UNIX_GLOB_APPEND | W32FILE_UNIX_GLOB_UNIQUE, &glob_buf);
637 if (glob_buf.gl_pathc == 0) {
639 } else if (result != 0) {
643 names = g_ptr_array_new ();
644 for (i = 0; i < glob_buf.gl_pathc; i++) {
645 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
648 mono_w32file_unix_globfree (&glob_buf);
652 g_ptr_array_sort (names, file_compare);
653 g_ptr_array_set_size (names, result + 1);
655 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
657 g_ptr_array_free (names, TRUE);
664 _wapi_lock_file_region (gint fd, off_t offset, off_t length)
666 #if defined(__native_client__)
667 printf("WARNING: %s: fcntl() not available on Native Client!\n", __func__);
668 // behave as below -- locks are not available
671 struct flock lock_data;
674 if (offset < 0 || length < 0) {
675 SetLastError (ERROR_INVALID_PARAMETER);
679 lock_data.l_type = F_WRLCK;
680 lock_data.l_whence = SEEK_SET;
681 lock_data.l_start = offset;
682 lock_data.l_len = length;
685 ret = fcntl (fd, F_SETLK, &lock_data);
686 } while(ret == -1 && errno == EINTR);
688 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret);
692 * if locks are not available (NFS for example),
697 || errno == EOPNOTSUPP
706 SetLastError (ERROR_LOCK_VIOLATION);
711 #endif /* __native_client__ */
715 _wapi_unlock_file_region (gint fd, off_t offset, off_t length)
717 #if defined(__native_client__)
718 printf("WARNING: %s: fcntl() not available on Native Client!\n", __func__);
721 struct flock lock_data;
724 lock_data.l_type = F_UNLCK;
725 lock_data.l_whence = SEEK_SET;
726 lock_data.l_start = offset;
727 lock_data.l_len = length;
730 ret = fcntl (fd, F_SETLK, &lock_data);
731 } while(ret == -1 && errno == EINTR);
733 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret);
737 * if locks are not available (NFS for example),
742 || errno == EOPNOTSUPP
751 SetLastError (ERROR_LOCK_VIOLATION);
756 #endif /* __native_client__ */
759 static void file_close (gpointer handle, gpointer data);
760 static void file_details (gpointer data);
761 static const gchar* file_typename (void);
762 static gsize file_typesize (void);
763 static gint file_getfiletype(void);
764 static gboolean file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
765 static gboolean file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
766 static gboolean file_flush(gpointer handle);
767 static guint32 file_seek(gpointer handle, gint32 movedistance,
768 gint32 *highmovedistance, gint method);
769 static gboolean file_setendoffile(gpointer handle);
770 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
771 static gboolean file_getfiletime(gpointer handle, FILETIME *create_time,
772 FILETIME *access_time,
773 FILETIME *write_time);
774 static gboolean file_setfiletime(gpointer handle,
775 const FILETIME *create_time,
776 const FILETIME *access_time,
777 const FILETIME *write_time);
778 static guint32 GetDriveTypeFromPath (const gchar *utf8_root_path_name);
780 /* File handle is only signalled for overlapped IO */
781 static MonoW32HandleOps _wapi_file_ops = {
782 file_close, /* close */
786 NULL, /* special_wait */
788 file_details, /* details */
789 file_typename, /* typename */
790 file_typesize, /* typesize */
793 static void console_close (gpointer handle, gpointer data);
794 static void console_details (gpointer data);
795 static const gchar* console_typename (void);
796 static gsize console_typesize (void);
797 static gint console_getfiletype(void);
798 static gboolean console_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
799 static gboolean console_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
801 /* Console is mostly the same as file, except it can block waiting for
804 static MonoW32HandleOps _wapi_console_ops = {
805 console_close, /* close */
809 NULL, /* special_wait */
811 console_details, /* details */
812 console_typename, /* typename */
813 console_typesize, /* typesize */
816 static const gchar* find_typename (void);
817 static gsize find_typesize (void);
819 static MonoW32HandleOps _wapi_find_ops = {
824 NULL, /* special_wait */
827 find_typename, /* typename */
828 find_typesize, /* typesize */
831 static void pipe_close (gpointer handle, gpointer data);
832 static void pipe_details (gpointer data);
833 static const gchar* pipe_typename (void);
834 static gsize pipe_typesize (void);
835 static gint pipe_getfiletype (void);
836 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
837 static gboolean pipe_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
841 static MonoW32HandleOps _wapi_pipe_ops = {
842 pipe_close, /* close */
846 NULL, /* special_wait */
848 pipe_details, /* details */
849 pipe_typename, /* typename */
850 pipe_typesize, /* typesize */
853 static const struct {
854 /* File, console and pipe handles */
855 gint (*getfiletype)(void);
857 /* File, console and pipe handles */
858 gboolean (*readfile)(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
859 gboolean (*writefile)(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
860 gboolean (*flushfile)(gpointer handle);
863 guint32 (*seek)(gpointer handle, gint32 movedistance,
864 gint32 *highmovedistance, gint method);
865 gboolean (*setendoffile)(gpointer handle);
866 guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
867 gboolean (*getfiletime)(gpointer handle, FILETIME *create_time,
868 FILETIME *access_time,
869 FILETIME *write_time);
870 gboolean (*setfiletime)(gpointer handle,
871 const FILETIME *create_time,
872 const FILETIME *access_time,
873 const FILETIME *write_time);
874 } io_ops[MONO_W32HANDLE_COUNT]={
875 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
878 file_read, file_write,
879 file_flush, file_seek,
885 {console_getfiletype,
888 NULL, NULL, NULL, NULL, NULL, NULL},
890 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
892 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
894 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
896 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
897 /* socket (will need at least read and write) */
898 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
900 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
902 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
907 NULL, NULL, NULL, NULL, NULL, NULL},
910 static gboolean lock_while_writing = FALSE;
912 /* Some utility functions.
916 * Check if a file is writable by the current user.
918 * This is is a best effort kind of thing. It assumes a reasonable sane set
919 * of permissions by the underlying OS.
921 * We generally assume that basic unix permission bits are authoritative. Which might not
922 * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc)
924 * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file.
926 * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent
927 * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous
928 * and should not be used with a dynamic runtime.
931 is_file_writable (struct stat *st, const gchar *path)
934 // OS X Finder "locked" or `ls -lO` "uchg".
935 // This only covers one of several cases where an OS X file could be unwritable through special flags.
936 if (st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE))
940 /* Is it globally writable? */
941 if (st->st_mode & S_IWOTH)
944 /* Am I the owner? */
945 if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR))
948 /* Am I in the same group? */
949 if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP))
952 /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid
953 * but it's the only sane option we have on unix.
955 return access (path, W_OK) == 0;
959 static guint32 _wapi_stat_to_file_attributes (const gchar *pathname,
966 /* FIXME: this could definitely be better, but there seems to
967 * be no pattern to the attributes that are set
970 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
971 if (S_ISSOCK (buf->st_mode))
972 buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */
974 filename = _wapi_basename (pathname);
976 if (S_ISDIR (buf->st_mode)) {
977 attrs = FILE_ATTRIBUTE_DIRECTORY;
978 if (!is_file_writable (buf, pathname)) {
979 attrs |= FILE_ATTRIBUTE_READONLY;
981 if (filename[0] == '.') {
982 attrs |= FILE_ATTRIBUTE_HIDDEN;
985 if (!is_file_writable (buf, pathname)) {
986 attrs = FILE_ATTRIBUTE_READONLY;
988 if (filename[0] == '.') {
989 attrs |= FILE_ATTRIBUTE_HIDDEN;
991 } else if (filename[0] == '.') {
992 attrs = FILE_ATTRIBUTE_HIDDEN;
994 attrs = FILE_ATTRIBUTE_NORMAL;
999 if (S_ISLNK (lbuf->st_mode)) {
1000 attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
1010 _wapi_set_last_error_from_errno (void)
1012 SetLastError (_wapi_get_win32_file_error (errno));
1015 static void _wapi_set_last_path_error_from_errno (const gchar *dir,
1018 if (errno == ENOENT) {
1019 /* Check the path - if it's a missing directory then
1020 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
1026 dirname = _wapi_dirname (path);
1028 dirname = g_strdup (dir);
1031 if (_wapi_access (dirname, F_OK) == 0) {
1032 SetLastError (ERROR_FILE_NOT_FOUND);
1034 SetLastError (ERROR_PATH_NOT_FOUND);
1039 _wapi_set_last_error_from_errno ();
1045 static void file_close (gpointer handle, gpointer data)
1047 MonoW32HandleFile *file_handle = (MonoW32HandleFile *)data;
1048 gint fd = file_handle->fd;
1050 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing file handle %p [%s]", __func__, handle,
1051 file_handle->filename);
1053 if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE)
1054 _wapi_unlink (file_handle->filename);
1056 g_free (file_handle->filename);
1058 if (file_handle->share_info)
1059 file_share_release (file_handle->share_info);
1064 static void file_details (gpointer data)
1066 MonoW32HandleFile *file = (MonoW32HandleFile *)data;
1068 g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u",
1070 file->fileaccess&GENERIC_READ?'R':'.',
1071 file->fileaccess&GENERIC_WRITE?'W':'.',
1072 file->fileaccess&GENERIC_EXECUTE?'X':'.',
1073 file->sharemode&FILE_SHARE_READ?'R':'.',
1074 file->sharemode&FILE_SHARE_WRITE?'W':'.',
1075 file->sharemode&FILE_SHARE_DELETE?'D':'.',
1079 static const gchar* file_typename (void)
1084 static gsize file_typesize (void)
1086 return sizeof (MonoW32HandleFile);
1089 static gint file_getfiletype(void)
1091 return(FILE_TYPE_DISK);
1095 file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1097 MonoW32HandleFile *file_handle;
1100 MonoThreadInfo *info = mono_thread_info_current ();
1102 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1103 (gpointer *)&file_handle);
1105 g_warning ("%s: error looking up file handle %p", __func__,
1107 SetLastError (ERROR_INVALID_HANDLE);
1111 fd = file_handle->fd;
1112 if(bytesread!=NULL) {
1116 if(!(file_handle->fileaccess & GENERIC_READ) &&
1117 !(file_handle->fileaccess & GENERIC_ALL)) {
1118 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1119 __func__, handle, file_handle->fileaccess);
1121 SetLastError (ERROR_ACCESS_DENIED);
1126 ret = read (fd, buffer, numbytes);
1127 } while (ret == -1 && errno == EINTR &&
1128 !mono_thread_info_is_interrupt_state (info));
1133 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__,
1134 handle, strerror(err));
1135 SetLastError (_wapi_get_win32_file_error (err));
1139 if (bytesread != NULL) {
1147 file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
1149 MonoW32HandleFile *file_handle;
1152 off_t current_pos = 0;
1153 MonoThreadInfo *info = mono_thread_info_current ();
1155 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1156 (gpointer *)&file_handle);
1158 g_warning ("%s: error looking up file handle %p", __func__,
1160 SetLastError (ERROR_INVALID_HANDLE);
1164 fd = file_handle->fd;
1166 if(byteswritten!=NULL) {
1170 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1171 !(file_handle->fileaccess & GENERIC_ALL)) {
1172 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1174 SetLastError (ERROR_ACCESS_DENIED);
1178 if (lock_while_writing) {
1179 /* Need to lock the region we're about to write to,
1180 * because we only do advisory locking on POSIX
1183 current_pos = lseek (fd, (off_t)0, SEEK_CUR);
1184 if (current_pos == -1) {
1185 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__,
1186 handle, strerror (errno));
1187 _wapi_set_last_error_from_errno ();
1191 if (_wapi_lock_file_region (fd, current_pos,
1192 numbytes) == FALSE) {
1193 /* The error has already been set */
1199 ret = write (fd, buffer, numbytes);
1200 } while (ret == -1 && errno == EINTR &&
1201 !mono_thread_info_is_interrupt_state (info));
1203 if (lock_while_writing) {
1204 _wapi_unlock_file_region (fd, current_pos, numbytes);
1208 if (errno == EINTR) {
1211 _wapi_set_last_error_from_errno ();
1213 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s",
1214 __func__, handle, strerror(errno));
1219 if (byteswritten != NULL) {
1220 *byteswritten = ret;
1225 static gboolean file_flush(gpointer handle)
1227 MonoW32HandleFile *file_handle;
1231 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1232 (gpointer *)&file_handle);
1234 g_warning ("%s: error looking up file handle %p", __func__,
1236 SetLastError (ERROR_INVALID_HANDLE);
1240 fd = file_handle->fd;
1242 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1243 !(file_handle->fileaccess & GENERIC_ALL)) {
1244 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1246 SetLastError (ERROR_ACCESS_DENIED);
1252 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fsync of handle %p error: %s", __func__, handle,
1255 _wapi_set_last_error_from_errno ();
1262 static guint32 file_seek(gpointer handle, gint32 movedistance,
1263 gint32 *highmovedistance, gint method)
1265 MonoW32HandleFile *file_handle;
1267 gint64 offset, newpos;
1271 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1272 (gpointer *)&file_handle);
1274 g_warning ("%s: error looking up file handle %p", __func__,
1276 SetLastError (ERROR_INVALID_HANDLE);
1277 return(INVALID_SET_FILE_POINTER);
1280 fd = file_handle->fd;
1282 if(!(file_handle->fileaccess & GENERIC_READ) &&
1283 !(file_handle->fileaccess & GENERIC_WRITE) &&
1284 !(file_handle->fileaccess & GENERIC_ALL)) {
1285 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1287 SetLastError (ERROR_ACCESS_DENIED);
1288 return(INVALID_SET_FILE_POINTER);
1302 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: invalid seek type %d", __func__, method);
1304 SetLastError (ERROR_INVALID_PARAMETER);
1305 return(INVALID_SET_FILE_POINTER);
1308 #ifdef HAVE_LARGE_FILE_SUPPORT
1309 if(highmovedistance==NULL) {
1310 offset=movedistance;
1311 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld (low %d)", __func__,
1312 offset, movedistance);
1314 offset=((gint64) *highmovedistance << 32) | (guint32)movedistance;
1316 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
1319 offset=movedistance;
1322 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: moving handle %p by %lld bytes from %d", __func__,
1323 handle, (long long)offset, whence);
1325 #ifdef PLATFORM_ANDROID
1326 /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */
1327 newpos=lseek64(fd, offset, whence);
1329 newpos=lseek(fd, offset, whence);
1332 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek on handle %p returned error %s",
1333 __func__, handle, strerror(errno));
1335 _wapi_set_last_error_from_errno ();
1336 return(INVALID_SET_FILE_POINTER);
1339 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek returns %lld", __func__, newpos);
1341 #ifdef HAVE_LARGE_FILE_SUPPORT
1342 ret=newpos & 0xFFFFFFFF;
1343 if(highmovedistance!=NULL) {
1344 *highmovedistance=newpos>>32;
1348 if(highmovedistance!=NULL) {
1349 /* Accurate, but potentially dodgy :-) */
1350 *highmovedistance=0;
1354 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: move of handle %p returning %d/%d", __func__,
1355 handle, ret, highmovedistance==NULL?0:*highmovedistance);
1360 static gboolean file_setendoffile(gpointer handle)
1362 MonoW32HandleFile *file_handle;
1364 struct stat statbuf;
1367 MonoThreadInfo *info = mono_thread_info_current ();
1369 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1370 (gpointer *)&file_handle);
1372 g_warning ("%s: error looking up file handle %p", __func__,
1374 SetLastError (ERROR_INVALID_HANDLE);
1377 fd = file_handle->fd;
1379 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1380 !(file_handle->fileaccess & GENERIC_ALL)) {
1381 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1383 SetLastError (ERROR_ACCESS_DENIED);
1387 /* Find the current file position, and the file length. If
1388 * the file position is greater than the length, write to
1389 * extend the file with a hole. If the file position is less
1390 * than the length, truncate the file.
1393 ret=fstat(fd, &statbuf);
1395 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__,
1396 handle, strerror(errno));
1398 _wapi_set_last_error_from_errno ();
1402 pos=lseek(fd, (off_t)0, SEEK_CUR);
1404 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__,
1405 handle, strerror(errno));
1407 _wapi_set_last_error_from_errno ();
1411 #ifdef FTRUNCATE_DOESNT_EXTEND
1412 off_t size = statbuf.st_size;
1413 /* I haven't bothered to write the configure.ac stuff for this
1414 * because I don't know if any platform needs it. I'm leaving
1415 * this code just in case though
1418 /* Extend the file. Use write() here, because some
1419 * manuals say that ftruncate() behaviour is undefined
1420 * when the file needs extending. The POSIX spec says
1421 * that on XSI-conformant systems it extends, so if
1422 * every system we care about conforms, then we can
1426 ret = write (fd, "", 1);
1427 } while (ret == -1 && errno == EINTR &&
1428 !mono_thread_info_is_interrupt_state (info));
1431 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p extend write failed: %s", __func__, handle, strerror(errno));
1433 _wapi_set_last_error_from_errno ();
1437 /* And put the file position back after the write */
1438 ret = lseek (fd, pos, SEEK_SET);
1440 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p second lseek failed: %s",
1441 __func__, handle, strerror(errno));
1443 _wapi_set_last_error_from_errno ();
1449 /* Native Client has no ftruncate function, even in standalone sel_ldr. */
1450 #ifndef __native_client__
1451 /* always truncate, because the extend write() adds an extra
1452 * byte to the end of the file
1455 ret=ftruncate(fd, pos);
1457 while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1459 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ftruncate failed: %s", __func__,
1460 handle, strerror(errno));
1462 _wapi_set_last_error_from_errno ();
1470 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
1472 MonoW32HandleFile *file_handle;
1474 struct stat statbuf;
1479 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1480 (gpointer *)&file_handle);
1482 g_warning ("%s: error looking up file handle %p", __func__,
1484 SetLastError (ERROR_INVALID_HANDLE);
1485 return(INVALID_FILE_SIZE);
1487 fd = file_handle->fd;
1489 if(!(file_handle->fileaccess & GENERIC_READ) &&
1490 !(file_handle->fileaccess & GENERIC_WRITE) &&
1491 !(file_handle->fileaccess & GENERIC_ALL)) {
1492 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1494 SetLastError (ERROR_ACCESS_DENIED);
1495 return(INVALID_FILE_SIZE);
1498 /* If the file has a size with the low bits 0xFFFFFFFF the
1499 * caller can't tell if this is an error, so clear the error
1502 SetLastError (ERROR_SUCCESS);
1504 ret = fstat(fd, &statbuf);
1506 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__,
1507 handle, strerror(errno));
1509 _wapi_set_last_error_from_errno ();
1510 return(INVALID_FILE_SIZE);
1513 /* fstat indicates block devices as zero-length, so go a different path */
1515 if (S_ISBLK(statbuf.st_mode)) {
1517 if (ioctl(fd, BLKGETSIZE64, &bigsize) < 0) {
1518 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ioctl BLKGETSIZE64 failed: %s",
1519 __func__, handle, strerror(errno));
1521 _wapi_set_last_error_from_errno ();
1522 return(INVALID_FILE_SIZE);
1525 size = bigsize & 0xFFFFFFFF;
1526 if (highsize != NULL) {
1527 *highsize = bigsize>>32;
1530 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning block device size %d/%d",
1531 __func__, size, *highsize);
1537 #ifdef HAVE_LARGE_FILE_SUPPORT
1538 size = statbuf.st_size & 0xFFFFFFFF;
1539 if (highsize != NULL) {
1540 *highsize = statbuf.st_size>>32;
1543 if (highsize != NULL) {
1544 /* Accurate, but potentially dodgy :-) */
1547 size = statbuf.st_size;
1550 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning size %d/%d", __func__, size, *highsize);
1555 static gboolean file_getfiletime(gpointer handle, FILETIME *create_time,
1556 FILETIME *access_time,
1557 FILETIME *write_time)
1559 MonoW32HandleFile *file_handle;
1561 struct stat statbuf;
1562 guint64 create_ticks, access_ticks, write_ticks;
1565 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1566 (gpointer *)&file_handle);
1568 g_warning ("%s: error looking up file handle %p", __func__,
1570 SetLastError (ERROR_INVALID_HANDLE);
1573 fd = file_handle->fd;
1575 if(!(file_handle->fileaccess & GENERIC_READ) &&
1576 !(file_handle->fileaccess & GENERIC_ALL)) {
1577 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1578 __func__, handle, file_handle->fileaccess);
1580 SetLastError (ERROR_ACCESS_DENIED);
1584 ret=fstat(fd, &statbuf);
1586 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle,
1589 _wapi_set_last_error_from_errno ();
1593 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: atime: %ld ctime: %ld mtime: %ld", __func__,
1594 statbuf.st_atime, statbuf.st_ctime,
1597 /* Try and guess a meaningful create time by using the older
1600 /* The magic constant comes from msdn documentation
1601 * "Converting a time_t Value to a File Time"
1603 if(statbuf.st_atime < statbuf.st_ctime) {
1604 create_ticks=((guint64)statbuf.st_atime*10000000)
1605 + 116444736000000000ULL;
1607 create_ticks=((guint64)statbuf.st_ctime*10000000)
1608 + 116444736000000000ULL;
1611 access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
1612 write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
1614 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: aticks: %llu cticks: %llu wticks: %llu", __func__,
1615 access_ticks, create_ticks, write_ticks);
1617 if(create_time!=NULL) {
1618 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
1619 create_time->dwHighDateTime = create_ticks >> 32;
1622 if(access_time!=NULL) {
1623 access_time->dwLowDateTime = access_ticks & 0xFFFFFFFF;
1624 access_time->dwHighDateTime = access_ticks >> 32;
1627 if(write_time!=NULL) {
1628 write_time->dwLowDateTime = write_ticks & 0xFFFFFFFF;
1629 write_time->dwHighDateTime = write_ticks >> 32;
1635 static gboolean file_setfiletime(gpointer handle,
1636 const FILETIME *create_time G_GNUC_UNUSED,
1637 const FILETIME *access_time,
1638 const FILETIME *write_time)
1640 MonoW32HandleFile *file_handle;
1642 struct utimbuf utbuf;
1643 struct stat statbuf;
1644 guint64 access_ticks, write_ticks;
1647 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1648 (gpointer *)&file_handle);
1650 g_warning ("%s: error looking up file handle %p", __func__,
1652 SetLastError (ERROR_INVALID_HANDLE);
1655 fd = file_handle->fd;
1657 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1658 !(file_handle->fileaccess & GENERIC_ALL)) {
1659 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1661 SetLastError (ERROR_ACCESS_DENIED);
1665 if(file_handle->filename == NULL) {
1666 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p unknown filename", __func__, handle);
1668 SetLastError (ERROR_INVALID_HANDLE);
1672 /* Get the current times, so we can put the same times back in
1673 * the event that one of the FileTime structs is NULL
1675 ret=fstat (fd, &statbuf);
1677 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle,
1680 SetLastError (ERROR_INVALID_PARAMETER);
1684 if(access_time!=NULL) {
1685 access_ticks=((guint64)access_time->dwHighDateTime << 32) +
1686 access_time->dwLowDateTime;
1687 /* This is (time_t)0. We can actually go to INT_MIN,
1688 * but this will do for now.
1690 if (access_ticks < 116444736000000000ULL) {
1691 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set access time too early",
1693 SetLastError (ERROR_INVALID_PARAMETER);
1697 if (sizeof (utbuf.actime) == 4 && ((access_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) {
1698 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time that is too big for a 32bits time_t",
1700 SetLastError (ERROR_INVALID_PARAMETER);
1704 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1706 utbuf.actime=statbuf.st_atime;
1709 if(write_time!=NULL) {
1710 write_ticks=((guint64)write_time->dwHighDateTime << 32) +
1711 write_time->dwLowDateTime;
1712 /* This is (time_t)0. We can actually go to INT_MIN,
1713 * but this will do for now.
1715 if (write_ticks < 116444736000000000ULL) {
1716 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time too early",
1718 SetLastError (ERROR_INVALID_PARAMETER);
1721 if (sizeof (utbuf.modtime) == 4 && ((write_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) {
1722 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time that is too big for a 32bits time_t",
1724 SetLastError (ERROR_INVALID_PARAMETER);
1728 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1730 utbuf.modtime=statbuf.st_mtime;
1733 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting handle %p access %ld write %ld", __func__,
1734 handle, utbuf.actime, utbuf.modtime);
1736 ret = _wapi_utime (file_handle->filename, &utbuf);
1738 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p [%s] utime failed: %s", __func__,
1739 handle, file_handle->filename, strerror(errno));
1741 SetLastError (ERROR_INVALID_PARAMETER);
1748 static void console_close (gpointer handle, gpointer data)
1750 MonoW32HandleFile *console_handle = (MonoW32HandleFile *)data;
1751 gint fd = console_handle->fd;
1753 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing console handle %p", __func__, handle);
1755 g_free (console_handle->filename);
1758 if (console_handle->share_info)
1759 file_share_release (console_handle->share_info);
1764 static void console_details (gpointer data)
1766 file_details (data);
1769 static const gchar* console_typename (void)
1774 static gsize console_typesize (void)
1776 return sizeof (MonoW32HandleFile);
1779 static gint console_getfiletype(void)
1781 return(FILE_TYPE_CHAR);
1785 console_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1787 MonoW32HandleFile *console_handle;
1790 MonoThreadInfo *info = mono_thread_info_current ();
1792 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
1793 (gpointer *)&console_handle);
1795 g_warning ("%s: error looking up console handle %p", __func__,
1797 SetLastError (ERROR_INVALID_HANDLE);
1800 fd = console_handle->fd;
1802 if(bytesread!=NULL) {
1806 if(!(console_handle->fileaccess & GENERIC_READ) &&
1807 !(console_handle->fileaccess & GENERIC_ALL)) {
1808 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1809 __func__, handle, console_handle->fileaccess);
1811 SetLastError (ERROR_ACCESS_DENIED);
1816 ret=read(fd, buffer, numbytes);
1817 } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1820 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, handle,
1823 _wapi_set_last_error_from_errno ();
1827 if(bytesread!=NULL) {
1835 console_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
1837 MonoW32HandleFile *console_handle;
1840 MonoThreadInfo *info = mono_thread_info_current ();
1842 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
1843 (gpointer *)&console_handle);
1845 g_warning ("%s: error looking up console handle %p", __func__,
1847 SetLastError (ERROR_INVALID_HANDLE);
1850 fd = console_handle->fd;
1852 if(byteswritten!=NULL) {
1856 if(!(console_handle->fileaccess & GENERIC_WRITE) &&
1857 !(console_handle->fileaccess & GENERIC_ALL)) {
1858 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, console_handle->fileaccess);
1860 SetLastError (ERROR_ACCESS_DENIED);
1865 ret = write(fd, buffer, numbytes);
1866 } while (ret == -1 && errno == EINTR &&
1867 !mono_thread_info_is_interrupt_state (info));
1870 if (errno == EINTR) {
1873 _wapi_set_last_error_from_errno ();
1875 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s",
1876 __func__, handle, strerror(errno));
1881 if(byteswritten!=NULL) {
1888 static const gchar* find_typename (void)
1893 static gsize find_typesize (void)
1895 return sizeof (MonoW32HandleFind);
1898 static void pipe_close (gpointer handle, gpointer data)
1900 MonoW32HandleFile *pipe_handle = (MonoW32HandleFile*)data;
1901 gint fd = pipe_handle->fd;
1903 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing pipe handle %p fd %d", __func__, handle, fd);
1905 /* No filename with pipe handles */
1907 if (pipe_handle->share_info)
1908 file_share_release (pipe_handle->share_info);
1913 static void pipe_details (gpointer data)
1915 file_details (data);
1918 static const gchar* pipe_typename (void)
1923 static gsize pipe_typesize (void)
1925 return sizeof (MonoW32HandleFile);
1928 static gint pipe_getfiletype(void)
1930 return(FILE_TYPE_PIPE);
1934 pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1936 MonoW32HandleFile *pipe_handle;
1939 MonoThreadInfo *info = mono_thread_info_current ();
1941 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE,
1942 (gpointer *)&pipe_handle);
1944 g_warning ("%s: error looking up pipe handle %p", __func__,
1946 SetLastError (ERROR_INVALID_HANDLE);
1949 fd = pipe_handle->fd;
1951 if(bytesread!=NULL) {
1955 if(!(pipe_handle->fileaccess & GENERIC_READ) &&
1956 !(pipe_handle->fileaccess & GENERIC_ALL)) {
1957 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1958 __func__, handle, pipe_handle->fileaccess);
1960 SetLastError (ERROR_ACCESS_DENIED);
1964 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: reading up to %d bytes from pipe %p", __func__,
1968 ret=read(fd, buffer, numbytes);
1969 } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1972 if (errno == EINTR) {
1975 _wapi_set_last_error_from_errno ();
1977 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__,
1978 handle, strerror(errno));
1984 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read %d bytes from pipe %p", __func__, ret, handle);
1986 if(bytesread!=NULL) {
1994 pipe_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
1996 MonoW32HandleFile *pipe_handle;
1999 MonoThreadInfo *info = mono_thread_info_current ();
2001 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE,
2002 (gpointer *)&pipe_handle);
2004 g_warning ("%s: error looking up pipe handle %p", __func__,
2006 SetLastError (ERROR_INVALID_HANDLE);
2009 fd = pipe_handle->fd;
2011 if(byteswritten!=NULL) {
2015 if(!(pipe_handle->fileaccess & GENERIC_WRITE) &&
2016 !(pipe_handle->fileaccess & GENERIC_ALL)) {
2017 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, pipe_handle->fileaccess);
2019 SetLastError (ERROR_ACCESS_DENIED);
2023 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: writing up to %d bytes to pipe %p", __func__, numbytes,
2027 ret = write (fd, buffer, numbytes);
2028 } while (ret == -1 && errno == EINTR &&
2029 !mono_thread_info_is_interrupt_state (info));
2032 if (errno == EINTR) {
2035 _wapi_set_last_error_from_errno ();
2037 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", __func__,
2038 handle, strerror(errno));
2043 if(byteswritten!=NULL) {
2050 static gint convert_flags(guint32 fileaccess, guint32 createmode)
2054 switch(fileaccess) {
2061 case GENERIC_READ|GENERIC_WRITE:
2065 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown access type 0x%x", __func__,
2070 switch(createmode) {
2072 flags|=O_CREAT|O_EXCL;
2075 flags|=O_CREAT|O_TRUNC;
2082 case TRUNCATE_EXISTING:
2086 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown create mode 0x%x", __func__,
2095 static mode_t convert_perms(guint32 sharemode)
2099 if(sharemode&FILE_SHARE_READ) {
2102 if(sharemode&FILE_SHARE_WRITE) {
2110 static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode,
2112 FileShare **share_info)
2114 gboolean file_already_shared;
2115 guint32 file_existing_share, file_existing_access;
2117 file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info);
2119 if (file_already_shared) {
2120 /* The reference to this share info was incremented
2121 * when we looked it up, so be careful to put it back
2122 * if we conclude we can't use this file.
2124 if (file_existing_share == 0) {
2125 /* Quick and easy, no possibility to share */
2126 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess);
2128 file_share_release (*share_info);
2133 if (((file_existing_share == FILE_SHARE_READ) &&
2134 (fileaccess != GENERIC_READ)) ||
2135 ((file_existing_share == FILE_SHARE_WRITE) &&
2136 (fileaccess != GENERIC_WRITE))) {
2137 /* New access mode doesn't match up */
2138 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, fileaccess, file_existing_share);
2140 file_share_release (*share_info);
2145 if (((file_existing_access & GENERIC_READ) &&
2146 !(sharemode & FILE_SHARE_READ)) ||
2147 ((file_existing_access & GENERIC_WRITE) &&
2148 !(sharemode & FILE_SHARE_WRITE))) {
2149 /* New share mode doesn't match up */
2150 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__, sharemode, file_existing_access);
2152 file_share_release (*share_info);
2157 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__);
2165 share_allows_delete (struct stat *statbuf, FileShare **share_info)
2167 gboolean file_already_shared;
2168 guint32 file_existing_share, file_existing_access;
2170 file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, FILE_SHARE_DELETE, GENERIC_READ, &file_existing_share, &file_existing_access, share_info);
2172 if (file_already_shared) {
2173 /* The reference to this share info was incremented
2174 * when we looked it up, so be careful to put it back
2175 * if we conclude we can't use this file.
2177 if (file_existing_share == 0) {
2178 /* Quick and easy, no possibility to share */
2179 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, (*share_info)->access);
2181 file_share_release (*share_info);
2186 if (!(file_existing_share & FILE_SHARE_DELETE)) {
2187 /* New access mode doesn't match up */
2188 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, (*share_info)->access, file_existing_share);
2190 file_share_release (*share_info);
2195 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__);
2202 mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs)
2204 MonoW32HandleFile file_handle = {0};
2206 gint flags=convert_flags(fileaccess, createmode);
2207 /*mode_t perms=convert_perms(sharemode);*/
2208 /* we don't use sharemode, because that relates to sharing of
2209 * the file when the file is open and is already handled by
2210 * other code, perms instead are the on-disk permissions and
2211 * this is a sane default.
2216 MonoW32HandleType handle_type;
2217 struct stat statbuf;
2219 if (attrs & FILE_ATTRIBUTE_TEMPORARY)
2222 if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
2223 SetLastError (ERROR_ENCRYPTION_FAILED);
2224 return INVALID_HANDLE_VALUE;
2228 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2230 SetLastError (ERROR_INVALID_NAME);
2231 return(INVALID_HANDLE_VALUE);
2234 filename = mono_unicode_to_external (name);
2235 if (filename == NULL) {
2236 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2238 SetLastError (ERROR_INVALID_NAME);
2239 return(INVALID_HANDLE_VALUE);
2242 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening %s with share 0x%x and access 0x%x", __func__,
2243 filename, sharemode, fileaccess);
2245 fd = _wapi_open (filename, flags, perms);
2247 /* If we were trying to open a directory with write permissions
2248 * (e.g. O_WRONLY or O_RDWR), this call will fail with
2249 * EISDIR. However, this is a bit bogus because calls to
2250 * manipulate the directory (e.g. mono_w32file_set_times) will still work on
2251 * the directory because they use other API calls
2252 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
2253 * to open the directory again without write permission.
2255 if (fd == -1 && errno == EISDIR)
2257 /* Try again but don't try to make it writable */
2258 fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
2262 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s: %s", __func__, filename,
2264 _wapi_set_last_path_error_from_errno (NULL, filename);
2267 return(INVALID_HANDLE_VALUE);
2270 if (fd >= mono_w32handle_fd_reserve) {
2271 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__);
2273 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
2278 return(INVALID_HANDLE_VALUE);
2281 ret = fstat (fd, &statbuf);
2283 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fstat error of file %s: %s", __func__,
2284 filename, strerror (errno));
2285 _wapi_set_last_error_from_errno ();
2289 return(INVALID_HANDLE_VALUE);
2291 #ifdef __native_client__
2292 /* Workaround: Native Client currently returns the same fake inode
2293 * for all files, so do a simple hash on the filename so we don't
2294 * use the same share info for each file.
2296 statbuf.st_ino = g_str_hash(filename);
2299 if (share_allows_open (&statbuf, sharemode, fileaccess,
2300 &file_handle.share_info) == FALSE) {
2301 SetLastError (ERROR_SHARING_VIOLATION);
2305 return (INVALID_HANDLE_VALUE);
2307 if (file_handle.share_info == NULL) {
2308 /* No space, so no more files can be opened */
2309 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No space in the share table", __func__);
2311 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
2315 return(INVALID_HANDLE_VALUE);
2318 file_handle.filename = filename;
2320 file_handle.fd = fd;
2321 file_handle.fileaccess=fileaccess;
2322 file_handle.sharemode=sharemode;
2323 file_handle.attrs=attrs;
2325 #ifdef HAVE_POSIX_FADVISE
2326 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN)
2327 posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
2328 if (attrs & FILE_FLAG_RANDOM_ACCESS)
2329 posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM);
2333 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN)
2334 fcntl(fd, F_RDAHEAD, 1);
2338 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
2340 if (S_ISFIFO (statbuf.st_mode)) {
2341 handle_type = MONO_W32HANDLE_PIPE;
2342 /* maintain invariant that pipes have no filename */
2343 file_handle.filename = NULL;
2346 } else if (S_ISCHR (statbuf.st_mode)) {
2347 handle_type = MONO_W32HANDLE_CONSOLE;
2349 handle_type = MONO_W32HANDLE_FILE;
2352 handle = mono_w32handle_new_fd (handle_type, fd, &file_handle);
2353 if (handle == INVALID_HANDLE_VALUE) {
2354 g_warning ("%s: error creating file handle", __func__);
2358 SetLastError (ERROR_GEN_FAILURE);
2359 return(INVALID_HANDLE_VALUE);
2362 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle);
2367 gboolean mono_w32file_delete(const gunichar2 *name)
2371 gboolean ret = FALSE;
2374 struct stat statbuf;
2375 FileShare *shareinfo;
2379 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2381 SetLastError (ERROR_INVALID_NAME);
2385 filename=mono_unicode_to_external(name);
2386 if(filename==NULL) {
2387 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2389 SetLastError (ERROR_INVALID_NAME);
2393 attrs = mono_w32file_get_attributes (name);
2394 if (attrs == INVALID_FILE_ATTRIBUTES) {
2395 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file attributes error", __func__);
2396 /* Error set by mono_w32file_get_attributes() */
2402 /* Check to make sure sharing allows us to open the file for
2403 * writing. See bug 323389.
2405 * Do the checks that don't need an open file descriptor, for
2406 * simplicity's sake. If we really have to do the full checks
2407 * then we can implement that later.
2409 if (_wapi_stat (filename, &statbuf) < 0) {
2410 _wapi_set_last_path_error_from_errno (NULL, filename);
2415 if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
2416 &shareinfo) == FALSE) {
2417 SetLastError (ERROR_SHARING_VIOLATION);
2422 file_share_release (shareinfo);
2425 retval = _wapi_unlink (filename);
2428 _wapi_set_last_path_error_from_errno (NULL, filename);
2439 MoveFile (gunichar2 *name, gunichar2 *dest_name)
2441 gchar *utf8_name, *utf8_dest_name;
2442 gint result, errno_copy;
2443 struct stat stat_src, stat_dest;
2444 gboolean ret = FALSE;
2445 FileShare *shareinfo;
2448 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2450 SetLastError (ERROR_INVALID_NAME);
2454 utf8_name = mono_unicode_to_external (name);
2455 if (utf8_name == NULL) {
2456 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2458 SetLastError (ERROR_INVALID_NAME);
2462 if(dest_name==NULL) {
2463 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2466 SetLastError (ERROR_INVALID_NAME);
2470 utf8_dest_name = mono_unicode_to_external (dest_name);
2471 if (utf8_dest_name == NULL) {
2472 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2475 SetLastError (ERROR_INVALID_NAME);
2480 * In C# land we check for the existence of src, but not for dest.
2481 * We check it here and return the failure if dest exists and is not
2482 * the same file as src.
2484 if (_wapi_stat (utf8_name, &stat_src) < 0) {
2485 if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
2486 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
2488 g_free (utf8_dest_name);
2493 if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
2494 if (stat_dest.st_dev != stat_src.st_dev ||
2495 stat_dest.st_ino != stat_src.st_ino) {
2497 g_free (utf8_dest_name);
2498 SetLastError (ERROR_ALREADY_EXISTS);
2503 /* Check to make that we have delete sharing permission.
2504 * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
2506 * Do the checks that don't need an open file descriptor, for
2507 * simplicity's sake. If we really have to do the full checks
2508 * then we can implement that later.
2510 if (share_allows_delete (&stat_src, &shareinfo) == FALSE) {
2511 SetLastError (ERROR_SHARING_VIOLATION);
2515 file_share_release (shareinfo);
2517 result = _wapi_rename (utf8_name, utf8_dest_name);
2521 switch(errno_copy) {
2523 SetLastError (ERROR_ALREADY_EXISTS);
2527 /* Ignore here, it is dealt with below */
2531 /* We already know src exists. Must be dest that doesn't exist. */
2532 _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name);
2536 _wapi_set_last_error_from_errno ();
2541 g_free (utf8_dest_name);
2543 if (result != 0 && errno_copy == EXDEV) {
2546 if (S_ISDIR (stat_src.st_mode)) {
2547 SetLastError (ERROR_NOT_SAME_DEVICE);
2550 /* Try a copy to the new location, and delete the source */
2551 if (!mono_w32file_copy (name, dest_name, FALSE, ©_error)) {
2552 /* mono_w32file_copy will set the error */
2556 return(mono_w32file_delete (name));
2567 write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_errors)
2571 gint buf_size = st_src->st_blksize;
2572 MonoThreadInfo *info = mono_thread_info_current ();
2574 buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
2575 buf = (gchar *) g_malloc (buf_size);
2578 remain = read (src_fd, buf, buf_size);
2580 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2584 _wapi_set_last_error_from_errno ();
2594 while (remain > 0) {
2595 if ((n = write (dest_fd, wbuf, remain)) < 0) {
2596 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2600 _wapi_set_last_error_from_errno ();
2601 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write failed.", __func__);
2616 CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists)
2618 gchar *utf8_src, *utf8_dest;
2619 gint src_fd, dest_fd;
2620 struct stat st, dest_st;
2621 struct utimbuf dest_time;
2622 gboolean ret = TRUE;
2626 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2628 SetLastError (ERROR_INVALID_NAME);
2632 utf8_src = mono_unicode_to_external (name);
2633 if (utf8_src == NULL) {
2634 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of source returned NULL",
2637 SetLastError (ERROR_INVALID_PARAMETER);
2641 if(dest_name==NULL) {
2642 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: dest is NULL", __func__);
2645 SetLastError (ERROR_INVALID_NAME);
2649 utf8_dest = mono_unicode_to_external (dest_name);
2650 if (utf8_dest == NULL) {
2651 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of dest returned NULL",
2654 SetLastError (ERROR_INVALID_PARAMETER);
2661 src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2663 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2671 if (fstat (src_fd, &st) < 0) {
2672 _wapi_set_last_error_from_errno ();
2681 /* Before trying to open/create the dest, we need to report a 'file busy'
2682 * error if src and dest are actually the same file. We do the check here to take
2683 * advantage of the IOMAP capability */
2684 if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev &&
2685 st.st_ino == dest_st.st_ino) {
2691 SetLastError (ERROR_SHARING_VIOLATION);
2695 if (fail_if_exists) {
2696 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
2698 /* FIXME: it kinda sucks that this code path potentially scans
2699 * the directory twice due to the weird SetLastError()
2701 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2703 /* The file does not exist, try creating it */
2704 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
2706 /* Apparently this error is set if we
2707 * overwrite the dest file
2709 SetLastError (ERROR_ALREADY_EXISTS);
2713 _wapi_set_last_error_from_errno ();
2722 if (!write_file (src_fd, dest_fd, &st, TRUE))
2728 dest_time.modtime = st.st_mtime;
2729 dest_time.actime = st.st_atime;
2730 ret_utime = utime (utf8_dest, &dest_time);
2731 if (ret_utime == -1)
2732 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file [%s] utime failed: %s", __func__, utf8_dest, strerror(errno));
2741 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2746 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s is NULL", __func__, arg_name);
2747 SetLastError (ERROR_INVALID_NAME);
2751 utf8_ret = mono_unicode_to_external (arg);
2752 if (utf8_ret == NULL) {
2753 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of %s returned NULL",
2754 __func__, arg_name);
2755 SetLastError (ERROR_INVALID_PARAMETER);
2763 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved)
2765 gint result, backup_fd = -1,replaced_fd = -1;
2766 gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2767 struct stat stBackup;
2768 gboolean ret = FALSE;
2770 if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2772 if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2773 goto replace_cleanup;
2774 if (backupFileName != NULL) {
2775 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2776 goto replace_cleanup;
2779 if (utf8_backupFileName) {
2780 // Open the backup file for read so we can restore the file if an error occurs.
2781 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2782 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2784 goto replace_cleanup;
2787 result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2789 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2790 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2791 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2792 replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2795 if (replaced_fd == -1)
2796 goto replace_cleanup;
2798 write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2801 goto replace_cleanup;
2807 g_free (utf8_replacedFileName);
2808 g_free (utf8_replacementFileName);
2809 g_free (utf8_backupFileName);
2810 if (backup_fd != -1)
2812 if (replaced_fd != -1)
2813 close (replaced_fd);
2817 static mono_mutex_t stdhandle_mutex;
2820 _wapi_stdhandle_create (gint fd, const gchar *name)
2824 MonoW32HandleFile file_handle = {0};
2826 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating standard handle type %s, fd %d", __func__, name, fd);
2828 #if !defined(__native_client__)
2829 /* Check if fd is valid */
2831 flags = fcntl(fd, F_GETFL);
2832 } while (flags == -1 && errno == EINTR);
2835 /* Invalid fd. Not really much point checking for EBADF
2838 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl error on fd %d: %s", __func__, fd, strerror(errno));
2840 SetLastError (_wapi_get_win32_file_error (errno));
2841 return INVALID_HANDLE_VALUE;
2844 switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) {
2846 file_handle.fileaccess = GENERIC_READ;
2849 file_handle.fileaccess = GENERIC_WRITE;
2852 file_handle.fileaccess = GENERIC_READ | GENERIC_WRITE;
2855 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't figure out flags 0x%x", __func__, flags);
2856 file_handle.fileaccess = 0;
2861 * fcntl will return -1 in nacl, as there is no real file system API.
2862 * Yet, standard streams are available.
2864 file_handle.fileaccess = (fd == STDIN_FILENO) ? GENERIC_READ : GENERIC_WRITE;
2867 file_handle.fd = fd;
2868 file_handle.filename = g_strdup(name);
2869 /* some default security attributes might be needed */
2870 file_handle.security_attributes = 0;
2872 /* Apparently input handles can't be written to. (I don't
2873 * know if output or error handles can't be read from.)
2876 file_handle.fileaccess &= ~GENERIC_WRITE;
2878 file_handle.sharemode = 0;
2879 file_handle.attrs = 0;
2881 handle = mono_w32handle_new_fd (MONO_W32HANDLE_CONSOLE, fd, &file_handle);
2882 if (handle == INVALID_HANDLE_VALUE) {
2883 g_warning ("%s: error creating file handle", __func__);
2884 SetLastError (ERROR_GEN_FAILURE);
2885 return INVALID_HANDLE_VALUE;
2888 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle);
2894 STD_INPUT_HANDLE = -10,
2895 STD_OUTPUT_HANDLE = -11,
2896 STD_ERROR_HANDLE = -12,
2900 mono_w32file_get_std_handle (gint stdhandle)
2902 MonoW32HandleFile *file_handle;
2909 case STD_INPUT_HANDLE:
2914 case STD_OUTPUT_HANDLE:
2919 case STD_ERROR_HANDLE:
2925 g_assert_not_reached ();
2928 handle = GINT_TO_POINTER (fd);
2930 mono_os_mutex_lock (&stdhandle_mutex);
2932 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
2933 (gpointer *)&file_handle);
2935 /* Need to create this console handle */
2936 handle = _wapi_stdhandle_create (fd, name);
2938 if (handle == INVALID_HANDLE_VALUE) {
2939 SetLastError (ERROR_NO_MORE_FILES);
2943 /* Add a reference to this handle */
2944 mono_w32handle_ref (handle);
2948 mono_os_mutex_unlock (&stdhandle_mutex);
2954 mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
2956 MonoW32HandleType type;
2958 type = mono_w32handle_get_type (handle);
2960 if(io_ops[type].readfile==NULL) {
2961 SetLastError (ERROR_INVALID_HANDLE);
2965 return(io_ops[type].readfile (handle, buffer, numbytes, bytesread));
2969 mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
2971 MonoW32HandleType type;
2973 type = mono_w32handle_get_type (handle);
2975 if(io_ops[type].writefile==NULL) {
2976 SetLastError (ERROR_INVALID_HANDLE);
2980 return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten));
2984 mono_w32file_flush (gpointer handle)
2986 MonoW32HandleType type;
2988 type = mono_w32handle_get_type (handle);
2990 if(io_ops[type].flushfile==NULL) {
2991 SetLastError (ERROR_INVALID_HANDLE);
2995 return(io_ops[type].flushfile (handle));
2999 mono_w32file_truncate (gpointer handle)
3001 MonoW32HandleType type;
3003 type = mono_w32handle_get_type (handle);
3005 if (io_ops[type].setendoffile == NULL) {
3006 SetLastError (ERROR_INVALID_HANDLE);
3010 return(io_ops[type].setendoffile (handle));
3014 mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method)
3016 MonoW32HandleType type;
3018 type = mono_w32handle_get_type (handle);
3020 if (io_ops[type].seek == NULL) {
3021 SetLastError (ERROR_INVALID_HANDLE);
3022 return(INVALID_SET_FILE_POINTER);
3025 return(io_ops[type].seek (handle, movedistance, highmovedistance,
3030 mono_w32file_get_type(gpointer handle)
3032 MonoW32HandleType type;
3034 type = mono_w32handle_get_type (handle);
3036 if (io_ops[type].getfiletype == NULL) {
3037 SetLastError (ERROR_INVALID_HANDLE);
3038 return(FILE_TYPE_UNKNOWN);
3041 return(io_ops[type].getfiletype ());
3045 GetFileSize(gpointer handle, guint32 *highsize)
3047 MonoW32HandleType type;
3049 type = mono_w32handle_get_type (handle);
3051 if (io_ops[type].getfilesize == NULL) {
3052 SetLastError (ERROR_INVALID_HANDLE);
3053 return(INVALID_FILE_SIZE);
3056 return(io_ops[type].getfilesize (handle, highsize));
3060 mono_w32file_get_times(gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time)
3062 MonoW32HandleType type;
3064 type = mono_w32handle_get_type (handle);
3066 if (io_ops[type].getfiletime == NULL) {
3067 SetLastError (ERROR_INVALID_HANDLE);
3071 return(io_ops[type].getfiletime (handle, create_time, access_time,
3076 mono_w32file_set_times(gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time)
3078 MonoW32HandleType type;
3080 type = mono_w32handle_get_type (handle);
3082 if (io_ops[type].setfiletime == NULL) {
3083 SetLastError (ERROR_INVALID_HANDLE);
3087 return(io_ops[type].setfiletime (handle, create_time, access_time,
3091 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
3092 * January 1 1601 GMT
3095 #define TICKS_PER_MILLISECOND 10000L
3096 #define TICKS_PER_SECOND 10000000L
3097 #define TICKS_PER_MINUTE 600000000L
3098 #define TICKS_PER_HOUR 36000000000LL
3099 #define TICKS_PER_DAY 864000000000LL
3101 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
3103 static const guint16 mon_yday[2][13]={
3104 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
3105 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
3109 mono_w32file_filetime_to_systemtime(const FILETIME *file_time, SYSTEMTIME *system_time)
3111 gint64 file_ticks, totaldays, rem, y;
3114 if(system_time==NULL) {
3115 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: system_time NULL", __func__);
3117 SetLastError (ERROR_INVALID_PARAMETER);
3121 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
3122 file_time->dwLowDateTime;
3124 /* Really compares if file_ticks>=0x8000000000000000
3125 * (LLONG_MAX+1) but we're working with a signed value for the
3126 * year and day calculation to work later
3129 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file_time too big", __func__);
3131 SetLastError (ERROR_INVALID_PARAMETER);
3135 totaldays=(file_ticks / TICKS_PER_DAY);
3136 rem = file_ticks % TICKS_PER_DAY;
3137 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld rem: %lld", __func__, totaldays, rem);
3139 system_time->wHour=rem/TICKS_PER_HOUR;
3140 rem %= TICKS_PER_HOUR;
3141 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem);
3143 system_time->wMinute = rem / TICKS_PER_MINUTE;
3144 rem %= TICKS_PER_MINUTE;
3145 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Minute: %d rem: %lld", __func__, system_time->wMinute,
3148 system_time->wSecond = rem / TICKS_PER_SECOND;
3149 rem %= TICKS_PER_SECOND;
3150 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Second: %d rem: %lld", __func__, system_time->wSecond,
3153 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
3154 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Milliseconds: %d", __func__,
3155 system_time->wMilliseconds);
3157 /* January 1, 1601 was a Monday, according to Emacs calendar */
3158 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
3159 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day of week: %d", __func__, system_time->wDayOfWeek);
3161 /* This algorithm to find year and month given days from epoch
3166 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
3167 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
3169 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
3170 /* Guess a corrected year, assuming 365 days per year */
3171 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
3172 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld yg: %lld y: %lld", __func__,
3175 g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__,
3176 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
3178 /* Adjust days and y to match the guessed year. */
3179 totaldays -= ((yg - y) * 365
3180 + LEAPS_THRU_END_OF (yg - 1)
3181 - LEAPS_THRU_END_OF (y - 1));
3182 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays);
3184 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: y: %lld", __func__, y);
3187 system_time->wYear = y;
3188 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Year: %d", __func__, system_time->wYear);
3190 ip = mon_yday[isleap(y)];
3192 for(y=11; totaldays < ip[y]; --y) {
3196 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays);
3198 system_time->wMonth = y + 1;
3199 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Month: %d", __func__, system_time->wMonth);
3201 system_time->wDay = totaldays + 1;
3202 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day: %d", __func__, system_time->wDay);
3208 mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data)
3210 MonoW32HandleFind find_handle = {0};
3212 gchar *utf8_pattern = NULL, *dir_part, *entry_part;
3215 if (pattern == NULL) {
3216 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pattern is NULL", __func__);
3218 SetLastError (ERROR_PATH_NOT_FOUND);
3219 return(INVALID_HANDLE_VALUE);
3222 utf8_pattern = mono_unicode_to_external (pattern);
3223 if (utf8_pattern == NULL) {
3224 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3226 SetLastError (ERROR_INVALID_NAME);
3227 return(INVALID_HANDLE_VALUE);
3230 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for [%s]", __func__, utf8_pattern);
3232 /* Figure out which bit of the pattern is the directory */
3233 dir_part = _wapi_dirname (utf8_pattern);
3234 entry_part = _wapi_basename (utf8_pattern);
3237 /* Don't do this check for now, it breaks if directories
3238 * really do have metachars in their names (see bug 58116).
3239 * FIXME: Figure out a better solution to keep some checks...
3241 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3242 SetLastError (ERROR_INVALID_NAME);
3244 g_free (entry_part);
3245 g_free (utf8_pattern);
3246 return(INVALID_HANDLE_VALUE);
3250 /* The pattern can specify a directory or a set of files.
3252 * The pattern can have wildcard characters ? and *, but only
3253 * in the section after the last directory delimiter. (Return
3254 * ERROR_INVALID_NAME if there are wildcards in earlier path
3255 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3256 * means "match one character", "??" seems to mean "match one
3257 * or two characters", "???" seems to mean "match one, two or
3258 * three characters", etc. Windows will also try and match
3259 * the mangled "short name" of files, so 8 character patterns
3260 * with wildcards will show some surprising results.
3262 * All the written documentation I can find says that '?'
3263 * should only match one character, and doesn't mention '??',
3264 * '???' etc. I'm going to assume that the strict behaviour
3265 * (ie '???' means three and only three characters) is the
3266 * correct one, because that lets me use fnmatch(3) rather
3267 * than mess around with regexes.
3270 find_handle.namelist = NULL;
3271 result = _wapi_io_scandir (dir_part, entry_part,
3272 &find_handle.namelist);
3275 /* No files, which windows seems to call
3278 SetLastError (ERROR_FILE_NOT_FOUND);
3279 g_free (utf8_pattern);
3280 g_free (entry_part);
3282 return (INVALID_HANDLE_VALUE);
3286 _wapi_set_last_path_error_from_errno (dir_part, NULL);
3287 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: scandir error: %s", __func__, g_strerror (errno));
3288 g_free (utf8_pattern);
3289 g_free (entry_part);
3291 return (INVALID_HANDLE_VALUE);
3294 g_free (utf8_pattern);
3295 g_free (entry_part);
3297 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Got %d matches", __func__, result);
3299 find_handle.dir_part = dir_part;
3300 find_handle.num = result;
3301 find_handle.count = 0;
3303 handle = mono_w32handle_new (MONO_W32HANDLE_FIND, &find_handle);
3304 if (handle == INVALID_HANDLE_VALUE) {
3305 g_warning ("%s: error creating find handle", __func__);
3307 g_free (entry_part);
3308 g_free (utf8_pattern);
3309 SetLastError (ERROR_GEN_FAILURE);
3311 return(INVALID_HANDLE_VALUE);
3314 if (handle != INVALID_HANDLE_VALUE &&
3315 !mono_w32file_find_next (handle, find_data)) {
3316 mono_w32file_find_close (handle);
3317 SetLastError (ERROR_NO_MORE_FILES);
3318 handle = INVALID_HANDLE_VALUE;
3325 mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data)
3327 MonoW32HandleFind *find_handle;
3329 struct stat buf, linkbuf;
3332 gchar *utf8_filename, *utf8_basename;
3333 gunichar2 *utf16_basename;
3336 gboolean ret = FALSE;
3338 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND,
3339 (gpointer *)&find_handle);
3341 g_warning ("%s: error looking up find handle %p", __func__,
3343 SetLastError (ERROR_INVALID_HANDLE);
3347 mono_w32handle_lock_handle (handle);
3350 if (find_handle->count >= find_handle->num) {
3351 SetLastError (ERROR_NO_MORE_FILES);
3355 /* stat next match */
3357 filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3359 result = _wapi_stat (filename, &buf);
3360 if (result == -1 && errno == ENOENT) {
3361 /* Might be a dangling symlink */
3362 result = _wapi_lstat (filename, &buf);
3366 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: stat failed: %s", __func__, filename);
3372 #ifndef __native_client__
3373 result = _wapi_lstat (filename, &linkbuf);
3375 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lstat failed: %s", __func__, filename);
3382 utf8_filename = mono_utf8_from_external (filename);
3383 if (utf8_filename == NULL) {
3384 /* We couldn't turn this filename into utf8 (eg the
3385 * encoding of the name wasn't convertible), so just
3388 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
3395 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Found [%s]", __func__, utf8_filename);
3397 /* fill data block */
3399 if (buf.st_mtime < buf.st_ctime)
3400 create_time = buf.st_mtime;
3402 create_time = buf.st_ctime;
3404 #ifdef __native_client__
3405 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, NULL);
3407 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
3410 time_t_to_filetime (create_time, &find_data->ftCreationTime);
3411 time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3412 time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3414 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3415 find_data->nFileSizeHigh = 0;
3416 find_data->nFileSizeLow = 0;
3418 find_data->nFileSizeHigh = buf.st_size >> 32;
3419 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3422 find_data->dwReserved0 = 0;
3423 find_data->dwReserved1 = 0;
3425 utf8_basename = _wapi_basename (utf8_filename);
3426 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3428 if(utf16_basename==NULL) {
3429 g_free (utf8_basename);
3430 g_free (utf8_filename);
3435 /* utf16 is 2 * utf8 */
3438 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3440 /* Truncating a utf16 string like this might leave the last
3443 memcpy (find_data->cFileName, utf16_basename,
3444 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3446 find_data->cAlternateFileName [0] = 0; /* not used */
3448 g_free (utf8_basename);
3449 g_free (utf8_filename);
3450 g_free (utf16_basename);
3453 mono_w32handle_unlock_handle (handle);
3459 mono_w32file_find_close (gpointer handle)
3461 MonoW32HandleFind *find_handle;
3464 if (handle == NULL) {
3465 SetLastError (ERROR_INVALID_HANDLE);
3469 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND,
3470 (gpointer *)&find_handle);
3472 g_warning ("%s: error looking up find handle %p", __func__,
3474 SetLastError (ERROR_INVALID_HANDLE);
3478 mono_w32handle_lock_handle (handle);
3480 g_strfreev (find_handle->namelist);
3481 g_free (find_handle->dir_part);
3483 mono_w32handle_unlock_handle (handle);
3485 mono_w32handle_unref (handle);
3491 mono_w32file_create_directory (const gunichar2 *name)
3497 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3499 SetLastError (ERROR_INVALID_NAME);
3503 utf8_name = mono_unicode_to_external (name);
3504 if (utf8_name == NULL) {
3505 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3507 SetLastError (ERROR_INVALID_NAME);
3511 result = _wapi_mkdir (utf8_name, 0777);
3518 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3524 mono_w32file_remove_directory (const gunichar2 *name)
3530 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3532 SetLastError (ERROR_INVALID_NAME);
3536 utf8_name = mono_unicode_to_external (name);
3537 if (utf8_name == NULL) {
3538 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3540 SetLastError (ERROR_INVALID_NAME);
3544 result = _wapi_rmdir (utf8_name);
3546 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3557 mono_w32file_get_attributes (const gunichar2 *name)
3560 struct stat buf, linkbuf;
3565 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3567 SetLastError (ERROR_INVALID_NAME);
3571 utf8_name = mono_unicode_to_external (name);
3572 if (utf8_name == NULL) {
3573 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3575 SetLastError (ERROR_INVALID_PARAMETER);
3576 return (INVALID_FILE_ATTRIBUTES);
3579 result = _wapi_stat (utf8_name, &buf);
3580 if (result == -1 && errno == ENOENT) {
3581 /* Might be a dangling symlink... */
3582 result = _wapi_lstat (utf8_name, &buf);
3586 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3588 return (INVALID_FILE_ATTRIBUTES);
3591 #ifndef __native_client__
3592 result = _wapi_lstat (utf8_name, &linkbuf);
3594 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3596 return (INVALID_FILE_ATTRIBUTES);
3600 #ifdef __native_client__
3601 ret = _wapi_stat_to_file_attributes (utf8_name, &buf, NULL);
3603 ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3612 mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat)
3616 struct stat buf, linkbuf;
3620 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3622 SetLastError (ERROR_INVALID_NAME);
3626 utf8_name = mono_unicode_to_external (name);
3627 if (utf8_name == NULL) {
3628 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3630 SetLastError (ERROR_INVALID_PARAMETER);
3634 result = _wapi_stat (utf8_name, &buf);
3635 if (result == -1 && errno == ENOENT) {
3636 /* Might be a dangling symlink... */
3637 result = _wapi_lstat (utf8_name, &buf);
3641 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3646 result = _wapi_lstat (utf8_name, &linkbuf);
3648 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3653 /* fill stat block */
3655 stat->attributes = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3656 stat->creation_time = (((guint64) (buf.st_mtime < buf.st_ctime ? buf.st_mtime : buf.st_ctime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3657 stat->last_access_time = (((guint64) (buf.st_atime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3658 stat->last_write_time = (((guint64) (buf.st_mtime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3659 stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size;
3666 mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs)
3668 /* FIXME: think of something clever to do on unix */
3674 * Currently we only handle one *internal* case, with a value that is
3675 * not standard: 0x80000000, which means `set executable bit'
3679 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3681 SetLastError (ERROR_INVALID_NAME);
3685 utf8_name = mono_unicode_to_external (name);
3686 if (utf8_name == NULL) {
3687 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3689 SetLastError (ERROR_INVALID_NAME);
3693 result = _wapi_stat (utf8_name, &buf);
3694 if (result == -1 && errno == ENOENT) {
3695 /* Might be a dangling symlink... */
3696 result = _wapi_lstat (utf8_name, &buf);
3700 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3705 /* Contrary to the documentation, ms allows NORMAL to be
3706 * specified along with other attributes, so dont bother to
3707 * catch that case here.
3709 if (attrs & FILE_ATTRIBUTE_READONLY) {
3710 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP));
3712 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR);
3715 /* Ignore the other attributes for now */
3717 if (attrs & 0x80000000){
3718 mode_t exec_mask = 0;
3720 if ((buf.st_mode & S_IRUSR) != 0)
3721 exec_mask |= S_IXUSR;
3723 if ((buf.st_mode & S_IRGRP) != 0)
3724 exec_mask |= S_IXGRP;
3726 if ((buf.st_mode & S_IROTH) != 0)
3727 exec_mask |= S_IXOTH;
3729 result = chmod (utf8_name, buf.st_mode | exec_mask);
3731 /* Don't bother to reset executable (might need to change this
3741 mono_w32file_get_cwd (guint32 length, gunichar2 *buffer)
3743 gunichar2 *utf16_path;
3747 #ifdef __native_client__
3748 gchar *path = g_get_current_dir ();
3749 if (length < strlen(path) + 1 || path == NULL)
3751 memcpy (buffer, path, strlen(path) + 1);
3753 if (getcwd ((gchar*)buffer, length) == NULL) {
3754 if (errno == ERANGE) { /*buffer length is not big enough */
3755 gchar *path = g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/
3758 utf16_path = mono_unicode_from_external (path, &bytes);
3759 g_free (utf16_path);
3763 _wapi_set_last_error_from_errno ();
3768 utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3769 count = (bytes/2)+1;
3770 g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3772 /* Add the terminator */
3773 memset (buffer, '\0', bytes+2);
3774 memcpy (buffer, utf16_path, bytes);
3776 g_free (utf16_path);
3782 mono_w32file_set_cwd (const gunichar2 *path)
3788 SetLastError (ERROR_INVALID_PARAMETER);
3792 utf8_path = mono_unicode_to_external (path);
3793 if (_wapi_chdir (utf8_path) != 0) {
3794 _wapi_set_last_error_from_errno ();
3805 mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size)
3807 MonoW32HandleFile pipe_read_handle = {0};
3808 MonoW32HandleFile pipe_write_handle = {0};
3809 gpointer read_handle;
3810 gpointer write_handle;
3814 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating pipe", __func__);
3818 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error creating pipe: %s", __func__,
3821 _wapi_set_last_error_from_errno ();
3825 if (filedes[0] >= mono_w32handle_fd_reserve ||
3826 filedes[1] >= mono_w32handle_fd_reserve) {
3827 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__);
3829 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3837 /* filedes[0] is open for reading, filedes[1] for writing */
3839 pipe_read_handle.fd = filedes [0];
3840 pipe_read_handle.fileaccess = GENERIC_READ;
3841 read_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[0],
3843 if (read_handle == INVALID_HANDLE_VALUE) {
3844 g_warning ("%s: error creating pipe read handle", __func__);
3847 SetLastError (ERROR_GEN_FAILURE);
3852 pipe_write_handle.fd = filedes [1];
3853 pipe_write_handle.fileaccess = GENERIC_WRITE;
3854 write_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[1],
3855 &pipe_write_handle);
3856 if (write_handle == INVALID_HANDLE_VALUE) {
3857 g_warning ("%s: error creating pipe write handle", __func__);
3858 mono_w32handle_unref (read_handle);
3862 SetLastError (ERROR_GEN_FAILURE);
3867 *readpipe = read_handle;
3868 *writepipe = write_handle;
3870 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning pipe: read handle %p, write handle %p",
3871 __func__, read_handle, write_handle);
3876 #ifdef HAVE_GETFSSTAT
3877 /* Darwin has getfsstat */
3879 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3881 struct statfs *stats;
3884 glong length, total = 0;
3886 n = getfsstat (NULL, 0, MNT_NOWAIT);
3889 size = n * sizeof (struct statfs);
3890 stats = (struct statfs *) g_malloc (size);
3893 if (getfsstat (stats, size, MNT_NOWAIT) == -1){
3897 for (i = 0; i < n; i++){
3898 dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL);
3899 if (total + length < len){
3900 memcpy (buf + total, dir, sizeof (gunichar2) * length);
3901 buf [total+length] = 0;
3904 total += length + 1;
3913 /* In-place octal sequence replacement */
3915 unescape_octal (gchar *str)
3924 while (*rptr != '\0') {
3925 if (*rptr == '\\') {
3928 c = (*(rptr++) - '0') << 6;
3929 c += (*(rptr++) - '0') << 3;
3930 c += *(rptr++) - '0';
3932 } else if (wptr != rptr) {
3940 static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf);
3943 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
3944 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
3945 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
3950 guint32 buffer_index;
3951 guint32 mountpoint_index;
3952 guint32 field_number;
3953 guint32 allocated_size;
3954 guint32 fsname_index;
3955 guint32 fstype_index;
3956 gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1];
3957 gchar *mountpoint_allocated;
3958 gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER];
3959 gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3960 gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3963 gboolean check_mount_source;
3964 } LinuxMountInfoParseState;
3966 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3967 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3968 static void append_to_mountpoint (LinuxMountInfoParseState *state);
3969 static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3972 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3976 LinuxMountInfoParseState state;
3977 gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL;
3979 memset (buf, 0, len * sizeof (gunichar2));
3980 fd = open ("/proc/self/mountinfo", O_RDONLY);
3982 parser = GetLogicalDriveStrings_MountInfo;
3984 fd = open ("/proc/mounts", O_RDONLY);
3986 parser = GetLogicalDriveStrings_Mounts;
3990 ret = GetLogicalDriveStrings_Mtab (len, buf);
3994 memset (&state, 0, sizeof (LinuxMountInfoParseState));
3995 state.field_number = 1;
3996 state.delimiter = ' ';
3998 while ((state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER)) > 0) {
3999 state.buffer_index = 0;
4001 while ((*parser)(len, buf, &state)) {
4002 if (state.buffer [state.buffer_index] == '\n') {
4003 gboolean quit = add_drive_string (len, buf, &state);
4004 state.field_number = 1;
4005 state.buffer_index++;
4006 if (state.mountpoint_allocated) {
4007 g_free (state.mountpoint_allocated);
4008 state.mountpoint_allocated = NULL;
4025 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4029 if (state->field_number == 1)
4030 state->check_mount_source = TRUE;
4032 while (state->buffer_index < (guint32)state->nbytes) {
4033 if (state->buffer [state->buffer_index] == state->delimiter) {
4034 state->field_number++;
4035 switch (state->field_number) {
4037 state->mountpoint_index = 0;
4041 if (state->mountpoint_allocated)
4042 state->mountpoint_allocated [state->mountpoint_index] = 0;
4044 state->mountpoint [state->mountpoint_index] = 0;
4048 ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index);
4050 state->buffer_index = (ptr - (gchar*)state->buffer) - 1;
4052 state->buffer_index = state->nbytes;
4055 state->buffer_index++;
4057 } else if (state->buffer [state->buffer_index] == '\n')
4060 switch (state->field_number) {
4062 if (state->check_mount_source) {
4063 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4064 /* We can ignore the rest, it's a device
4066 state->check_mount_source = FALSE;
4067 state->fsname [state->fsname_index++] = '/';
4070 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4071 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4076 append_to_mountpoint (state);
4080 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4081 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4085 state->buffer_index++;
4091 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4093 while (state->buffer_index < (guint32)state->nbytes) {
4094 if (state->buffer [state->buffer_index] == state->delimiter) {
4095 state->field_number++;
4096 switch (state->field_number) {
4098 state->mountpoint_index = 0;
4102 if (state->mountpoint_allocated)
4103 state->mountpoint_allocated [state->mountpoint_index] = 0;
4105 state->mountpoint [state->mountpoint_index] = 0;
4109 state->delimiter = '-';
4113 state->delimiter = ' ';
4117 state->check_mount_source = TRUE;
4120 state->buffer_index++;
4122 } else if (state->buffer [state->buffer_index] == '\n')
4125 switch (state->field_number) {
4127 append_to_mountpoint (state);
4131 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4132 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4136 if (state->check_mount_source) {
4137 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4138 /* We can ignore the rest, it's a device
4140 state->check_mount_source = FALSE;
4141 state->fsname [state->fsname_index++] = '/';
4144 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4145 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4150 state->buffer_index++;
4157 append_to_mountpoint (LinuxMountInfoParseState *state)
4159 gchar ch = state->buffer [state->buffer_index];
4160 if (state->mountpoint_allocated) {
4161 if (state->mountpoint_index >= state->allocated_size) {
4162 guint32 newsize = (state->allocated_size << 1) + 1;
4163 gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar));
4165 memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index);
4166 g_free (state->mountpoint_allocated);
4167 state->mountpoint_allocated = newbuf;
4168 state->allocated_size = newsize;
4170 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4172 if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) {
4173 state->allocated_size = (state->mountpoint_index << 1) + 1;
4174 state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar));
4175 memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index);
4176 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4178 state->mountpoint [state->mountpoint_index++] = ch;
4183 add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4185 gboolean quit = FALSE;
4186 gboolean ignore_entry;
4188 if (state->fsname_index == 1 && state->fsname [0] == '/')
4189 ignore_entry = FALSE;
4190 else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 ||
4191 memcmp ("aufs", state->fstype, state->fstype_index) == 0) {
4192 /* Don't ignore overlayfs and aufs - these might be used on Docker
4193 * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */
4194 ignore_entry = FALSE;
4195 } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) {
4196 ignore_entry = TRUE;
4197 } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) {
4198 /* Ignore GNOME's gvfs */
4199 if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0)
4200 ignore_entry = TRUE;
4202 ignore_entry = FALSE;
4203 } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0)
4204 ignore_entry = FALSE;
4206 ignore_entry = TRUE;
4208 if (!ignore_entry) {
4211 gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint;
4213 unescape_octal (mountpoint);
4214 dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL);
4215 if (state->total + length + 1 > len) {
4217 state->total = len * 2;
4220 memcpy (buf + state->total, dir, sizeof (gunichar2) * length);
4221 state->total += length;
4225 state->fsname_index = 0;
4226 state->fstype_index = 0;
4232 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
4234 return GetLogicalDriveStrings_Mtab (len, buf);
4238 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf)
4241 gunichar2 *ptr, *dir;
4242 glong length, total = 0;
4246 memset (buf, 0, sizeof (gunichar2) * (len + 1));
4251 /* Sigh, mntent and friends don't work well.
4252 * It stops on the first line that doesn't begin with a '/'.
4253 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4254 fp = fopen ("/etc/mtab", "rt");
4256 fp = fopen ("/etc/mnttab", "rt");
4262 while (fgets (buffer, 512, fp) != NULL) {
4266 splitted = g_strsplit (buffer, " ", 0);
4267 if (!*splitted || !*(splitted + 1)) {
4268 g_strfreev (splitted);
4272 unescape_octal (*(splitted + 1));
4273 dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
4274 g_strfreev (splitted);
4275 if (total + length + 1 > len) {
4278 return len * 2; /* guess */
4281 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4283 total += length + 1;
4288 /* Commented out, does not work with my mtab!!! - Gonz */
4289 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4293 gunichar2 *ptr, *dir;
4294 glong len, total = 0;
4297 fp = setmntent ("/etc/mtab", "rt");
4299 fp = setmntent ("/etc/mnttab", "rt");
4305 while ((mnt = getmntent (fp)) != NULL) {
4306 g_print ("GOT %s\n", mnt->mnt_dir);
4307 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4308 if (total + len + 1 > len) {
4309 return len * 2; /* guess */
4312 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4324 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
4326 mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_bytes_avail, guint64 *total_number_of_bytes, guint64 *total_number_of_free_bytes)
4329 struct statvfs fsstat;
4330 #elif defined(HAVE_STATFS)
4331 struct statfs fsstat;
4333 gboolean isreadonly;
4334 gchar *utf8_path_name;
4336 unsigned long block_size;
4338 if (path_name == NULL) {
4339 utf8_path_name = g_strdup (g_get_current_dir());
4340 if (utf8_path_name == NULL) {
4341 SetLastError (ERROR_DIRECTORY);
4346 utf8_path_name = mono_unicode_to_external (path_name);
4347 if (utf8_path_name == NULL) {
4348 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
4350 SetLastError (ERROR_INVALID_NAME);
4357 ret = statvfs (utf8_path_name, &fsstat);
4358 isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
4359 block_size = fsstat.f_frsize;
4360 #elif defined(HAVE_STATFS)
4361 ret = statfs (utf8_path_name, &fsstat);
4362 #if defined (MNT_RDONLY)
4363 isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
4364 #elif defined (MS_RDONLY)
4365 isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY);
4367 block_size = fsstat.f_bsize;
4369 } while(ret == -1 && errno == EINTR);
4371 g_free(utf8_path_name);
4374 _wapi_set_last_error_from_errno ();
4375 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: statvfs failed: %s", __func__, strerror (errno));
4379 /* total number of free bytes for non-root */
4380 if (free_bytes_avail != NULL) {
4382 *free_bytes_avail = 0;
4385 *free_bytes_avail = block_size * (guint64)fsstat.f_bavail;
4389 /* total number of bytes available for non-root */
4390 if (total_number_of_bytes != NULL) {
4391 *total_number_of_bytes = block_size * (guint64)fsstat.f_blocks;
4394 /* total number of bytes available for root */
4395 if (total_number_of_free_bytes != NULL) {
4397 *total_number_of_free_bytes = 0;
4400 *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree;
4408 mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_bytes_avail, guint64 *total_number_of_bytes, guint64 *total_number_of_free_bytes)
4410 if (free_bytes_avail != NULL) {
4411 *free_bytes_avail = (guint64) -1;
4414 if (total_number_of_bytes != NULL) {
4415 *total_number_of_bytes = (guint64) -1;
4418 if (total_number_of_free_bytes != NULL) {
4419 *total_number_of_free_bytes = (guint64) -1;
4427 * General Unix support
4432 const long fstypeid;
4434 const gchar* fstype;
4437 static _wapi_drive_type _wapi_drive_types[] = {
4439 { DRIVE_REMOTE, "afp" },
4440 { DRIVE_REMOTE, "autofs" },
4441 { DRIVE_CDROM, "cddafs" },
4442 { DRIVE_CDROM, "cd9660" },
4443 { DRIVE_RAMDISK, "devfs" },
4444 { DRIVE_FIXED, "exfat" },
4445 { DRIVE_RAMDISK, "fdesc" },
4446 { DRIVE_REMOTE, "ftp" },
4447 { DRIVE_FIXED, "hfs" },
4448 { DRIVE_FIXED, "msdos" },
4449 { DRIVE_REMOTE, "nfs" },
4450 { DRIVE_FIXED, "ntfs" },
4451 { DRIVE_REMOTE, "smbfs" },
4452 { DRIVE_FIXED, "udf" },
4453 { DRIVE_REMOTE, "webdav" },
4454 { DRIVE_UNKNOWN, NULL }
4456 { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"},
4457 { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"},
4458 { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"},
4459 { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"},
4460 { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"},
4461 { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" },
4462 { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"},
4463 { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"},
4464 { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"},
4465 { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"},
4466 { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"},
4467 { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"},
4468 { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"},
4469 { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"},
4470 { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"},
4471 { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"},
4472 { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"},
4473 { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"},
4474 { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"},
4475 { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"},
4476 { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"},
4477 { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"},
4478 { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"},
4479 { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"},
4480 { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"},
4481 { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"},
4482 { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"},
4483 { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"},
4484 { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"},
4485 { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"},
4486 { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"},
4487 { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"},
4488 { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"},
4489 { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"},
4490 { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"},
4491 { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"},
4492 { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"},
4493 { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"},
4494 { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"},
4495 { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"},
4496 { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"},
4497 { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"},
4498 { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"},
4499 { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"},
4500 { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"},
4501 { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"},
4502 { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"},
4503 { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"},
4504 { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"},
4505 { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"},
4506 { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"},
4507 { DRIVE_FIXED, UFS_MAGIC, "ufs"},
4508 { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"},
4509 { DRIVE_FIXED, UFS2_MAGIC, "ufs2"},
4510 { DRIVE_FIXED, UFS_CIGAM, "ufs"},
4511 { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"},
4512 { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"},
4513 { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"},
4514 { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"},
4515 { DRIVE_FIXED, V9FS_MAGIC, "9p"},
4516 { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"},
4517 { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"},
4518 { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"},
4519 { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"},
4520 { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"},
4521 { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"},
4522 { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"},
4523 { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"},
4524 { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"},
4525 { DRIVE_FIXED, OMFS_MAGIC, "omfs"},
4526 { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"},
4527 { DRIVE_UNKNOWN, 0, NULL}
4529 { DRIVE_RAMDISK, "ramfs" },
4530 { DRIVE_RAMDISK, "tmpfs" },
4531 { DRIVE_RAMDISK, "proc" },
4532 { DRIVE_RAMDISK, "sysfs" },
4533 { DRIVE_RAMDISK, "debugfs" },
4534 { DRIVE_RAMDISK, "devpts" },
4535 { DRIVE_RAMDISK, "securityfs" },
4536 { DRIVE_CDROM, "iso9660" },
4537 { DRIVE_FIXED, "ext2" },
4538 { DRIVE_FIXED, "ext3" },
4539 { DRIVE_FIXED, "ext4" },
4540 { DRIVE_FIXED, "sysv" },
4541 { DRIVE_FIXED, "reiserfs" },
4542 { DRIVE_FIXED, "ufs" },
4543 { DRIVE_FIXED, "vfat" },
4544 { DRIVE_FIXED, "msdos" },
4545 { DRIVE_FIXED, "udf" },
4546 { DRIVE_FIXED, "hfs" },
4547 { DRIVE_FIXED, "hpfs" },
4548 { DRIVE_FIXED, "qnx4" },
4549 { DRIVE_FIXED, "ntfs" },
4550 { DRIVE_FIXED, "ntfs-3g" },
4551 { DRIVE_REMOTE, "smbfs" },
4552 { DRIVE_REMOTE, "fuse" },
4553 { DRIVE_REMOTE, "nfs" },
4554 { DRIVE_REMOTE, "nfs4" },
4555 { DRIVE_REMOTE, "cifs" },
4556 { DRIVE_REMOTE, "ncpfs" },
4557 { DRIVE_REMOTE, "coda" },
4558 { DRIVE_REMOTE, "afs" },
4559 { DRIVE_UNKNOWN, NULL }
4564 static guint32 _wapi_get_drive_type(long f_type)
4566 _wapi_drive_type *current;
4568 current = &_wapi_drive_types[0];
4569 while (current->drive_type != DRIVE_UNKNOWN) {
4570 if (current->fstypeid == f_type)
4571 return current->drive_type;
4575 return DRIVE_UNKNOWN;
4578 static guint32 _wapi_get_drive_type(const gchar* fstype)
4580 _wapi_drive_type *current;
4582 current = &_wapi_drive_types[0];
4583 while (current->drive_type != DRIVE_UNKNOWN) {
4584 if (strcmp (current->fstype, fstype) == 0)
4590 return current->drive_type;
4594 #if defined (PLATFORM_MACOSX) || defined (__linux__)
4596 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4600 if (statfs (utf8_root_path_name, &buf) == -1)
4601 return DRIVE_UNKNOWN;
4603 return _wapi_get_drive_type (buf.f_fstypename);
4605 return _wapi_get_drive_type (buf.f_type);
4610 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4617 fp = fopen ("/etc/mtab", "rt");
4619 fp = fopen ("/etc/mnttab", "rt");
4621 return(DRIVE_UNKNOWN);
4624 drive_type = DRIVE_NO_ROOT_DIR;
4625 while (fgets (buffer, 512, fp) != NULL) {
4626 splitted = g_strsplit (buffer, " ", 0);
4627 if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
4628 g_strfreev (splitted);
4632 /* compare given root_path_name with the one from mtab,
4633 if length of utf8_root_path_name is zero it must be the root dir */
4634 if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
4635 (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
4636 drive_type = _wapi_get_drive_type (*(splitted + 2));
4637 /* it is possible this path might be mounted again with
4638 a known type...keep looking */
4639 if (drive_type != DRIVE_UNKNOWN) {
4640 g_strfreev (splitted);
4645 g_strfreev (splitted);
4654 mono_w32file_get_drive_type(const gunichar2 *root_path_name)
4656 gchar *utf8_root_path_name;
4659 if (root_path_name == NULL) {
4660 utf8_root_path_name = g_strdup (g_get_current_dir());
4661 if (utf8_root_path_name == NULL) {
4662 return(DRIVE_NO_ROOT_DIR);
4666 utf8_root_path_name = mono_unicode_to_external (root_path_name);
4667 if (utf8_root_path_name == NULL) {
4668 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
4669 return(DRIVE_NO_ROOT_DIR);
4672 /* strip trailing slash for compare below */
4673 if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) {
4674 utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
4677 drive_type = GetDriveTypeFromPath (utf8_root_path_name);
4678 g_free (utf8_root_path_name);
4680 return (drive_type);
4683 #if defined (PLATFORM_MACOSX) || defined (__linux__) || defined(PLATFORM_BSD) || defined(__native_client__) || defined(__FreeBSD_kernel__)
4685 get_fstypename (gchar *utfpath)
4687 #if defined (PLATFORM_MACOSX) || defined (__linux__)
4690 _wapi_drive_type *current;
4692 if (statfs (utfpath, &stat) == -1)
4695 return g_strdup (stat.f_fstypename);
4697 current = &_wapi_drive_types[0];
4698 while (current->drive_type != DRIVE_UNKNOWN) {
4699 if (stat.f_type == current->fstypeid)
4700 return g_strdup (current->fstype);
4710 /* Linux has struct statfs which has a different layout */
4712 mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize)
4716 gboolean status = FALSE;
4719 // We only support getting the file system type
4720 if (fsbuffer == NULL)
4723 utfpath = mono_unicode_to_external (path);
4724 if ((fstypename = get_fstypename (utfpath)) != NULL){
4725 gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL);
4726 if (ret != NULL && len < fsbuffersize){
4727 memcpy (fsbuffer, ret, len * sizeof (gunichar2));
4733 g_free (fstypename);
4741 LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4743 MonoW32HandleFile *file_handle;
4744 off_t offset, length;
4746 if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, (gpointer *)&file_handle)) {
4747 g_warning ("%s: error looking up file handle %p", __func__, handle);
4748 SetLastError (ERROR_INVALID_HANDLE);
4752 if (!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) {
4753 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
4754 SetLastError (ERROR_ACCESS_DENIED);
4758 #ifdef HAVE_LARGE_FILE_SUPPORT
4759 offset = ((gint64)offset_high << 32) | offset_low;
4760 length = ((gint64)length_high << 32) | length_low;
4762 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %lld, length %lld", __func__, handle, offset, length);
4764 if (offset_high > 0 || length_high > 0) {
4765 SetLastError (ERROR_INVALID_PARAMETER);
4768 offset = offset_low;
4769 length = length_low;
4771 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %ld, length %ld", __func__, handle, offset, length);
4774 return _wapi_lock_file_region (GPOINTER_TO_UINT(handle), offset, length);
4778 UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4780 MonoW32HandleFile *file_handle;
4781 off_t offset, length;
4783 if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, (gpointer *)&file_handle)) {
4784 g_warning ("%s: error looking up file handle %p", __func__, handle);
4785 SetLastError (ERROR_INVALID_HANDLE);
4789 if (!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) {
4790 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
4791 SetLastError (ERROR_ACCESS_DENIED);
4795 #ifdef HAVE_LARGE_FILE_SUPPORT
4796 offset = ((gint64)offset_high << 32) | offset_low;
4797 length = ((gint64)length_high << 32) | length_low;
4799 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %lld, length %lld", __func__, handle, offset, length);
4801 offset = offset_low;
4802 length = length_low;
4804 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %ld, length %ld", __func__, handle, offset, length);
4807 return _wapi_unlock_file_region (GPOINTER_TO_UINT(handle), offset, length);
4811 mono_w32file_init (void)
4813 mono_os_mutex_init (&stdhandle_mutex);
4814 mono_os_mutex_init (&file_share_mutex);
4816 mono_w32handle_register_ops (MONO_W32HANDLE_FILE, &_wapi_file_ops);
4817 mono_w32handle_register_ops (MONO_W32HANDLE_CONSOLE, &_wapi_console_ops);
4818 mono_w32handle_register_ops (MONO_W32HANDLE_FIND, &_wapi_find_ops);
4819 mono_w32handle_register_ops (MONO_W32HANDLE_PIPE, &_wapi_pipe_ops);
4821 /* mono_w32handle_register_capabilities (MONO_W32HANDLE_FILE, */
4822 /* MONO_W32HANDLE_CAP_WAIT); */
4823 /* mono_w32handle_register_capabilities (MONO_W32HANDLE_CONSOLE, */
4824 /* MONO_W32HANDLE_CAP_WAIT); */
4826 if (g_getenv ("MONO_STRICT_IO_EMULATION"))
4827 lock_while_writing = TRUE;
4831 mono_w32file_cleanup (void)
4833 mono_os_mutex_destroy (&file_share_mutex);
4835 if (file_share_table)
4836 g_hash_table_destroy (file_share_table);
4840 mono_w32file_move (gunichar2 *path, gunichar2 *dest, gint32 *error)
4846 result = MoveFile (path, dest);
4848 *error = GetLastError ();
4856 mono_w32file_copy (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error)
4862 result = CopyFile (path, dest, !overwrite);
4864 *error = GetLastError ();
4872 mono_w32file_replace (gunichar2 *destinationFileName, gunichar2 *sourceFileName, gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error)
4878 result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL);
4880 *error = GetLastError ();
4888 mono_w32file_get_file_size (gpointer handle, gint32 *error)
4895 length = GetFileSize (handle, &length_hi);
4896 if(length==INVALID_FILE_SIZE) {
4897 *error=GetLastError ();
4902 return length | ((gint64)length_hi << 32);
4906 mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error)
4912 result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
4914 *error = GetLastError ();
4922 mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error)
4928 result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
4930 *error = GetLastError ();
4938 mono_w32file_get_console_input (void)
4943 handle = mono_w32file_get_std_handle (STD_INPUT_HANDLE);
4950 mono_w32file_get_console_output (void)
4955 handle = mono_w32file_get_std_handle (STD_OUTPUT_HANDLE);
4962 mono_w32file_get_console_error (void)
4967 handle = mono_w32file_get_std_handle (STD_ERROR_HANDLE);