14 #ifdef HAVE_SYS_STATVFS_H
15 #include <sys/statvfs.h>
17 #if defined(HAVE_SYS_STATFS_H)
18 #include <sys/statfs.h>
20 #if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
21 #include <sys/param.h>
22 #include <sys/mount.h>
24 #include <sys/types.h>
28 #include <sys/ioctl.h>
30 #include <mono/utils/linux_magic.h>
38 #include "w32file-internals.h"
40 #include "w32file-unix-glob.h"
41 #include "w32handle.h"
43 #include "utils/mono-io-portability.h"
44 #include "utils/mono-logger-internals.h"
45 #include "utils/mono-os-mutex.h"
46 #include "utils/mono-threads.h"
47 #include "utils/mono-threads-api.h"
48 #include "utils/strenc.h"
59 /* Currently used for both FILE, CONSOLE and PIPE handle types.
60 * This may have to change in future. */
63 FileShare *share_info; /* Pointer into shared mem */
65 guint32 security_attributes;
79 * If SHM is disabled, this will point to a hash of FileShare structures, otherwise
80 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
83 static GHashTable *file_share_table;
84 static MonoCoopMutex file_share_mutex;
87 time_t_to_filetime (time_t timeval, FILETIME *filetime)
91 ticks = ((guint64)timeval * 10000000) + 116444736000000000ULL;
92 filetime->dwLowDateTime = ticks & 0xFFFFFFFF;
93 filetime->dwHighDateTime = ticks >> 32;
97 file_share_release (FileShare *share_info)
99 /* Prevent new entries racing with us */
100 mono_coop_mutex_lock (&file_share_mutex);
102 g_assert (share_info->handle_refs > 0);
103 share_info->handle_refs -= 1;
105 if (share_info->handle_refs == 0)
106 g_hash_table_remove (file_share_table, share_info);
108 mono_coop_mutex_unlock (&file_share_mutex);
112 file_share_equal (gconstpointer ka, gconstpointer kb)
114 const FileShare *s1 = (const FileShare *)ka;
115 const FileShare *s2 = (const FileShare *)kb;
117 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
121 file_share_hash (gconstpointer data)
123 const FileShare *s = (const FileShare *)data;
129 file_share_get (guint64 device, guint64 inode, guint32 new_sharemode, guint32 new_access,
130 guint32 *old_sharemode, guint32 *old_access, FileShare **share_info)
132 FileShare *file_share;
133 gboolean exists = FALSE;
135 /* Prevent new entries racing with us */
136 mono_coop_mutex_lock (&file_share_mutex);
141 * Instead of allocating a 4MB array, we use a hash table to keep track of this
142 * info. This is needed even if SHM is disabled, to track sharing inside
143 * the current process.
145 if (!file_share_table)
146 file_share_table = g_hash_table_new_full (file_share_hash, file_share_equal, NULL, g_free);
151 file_share = (FileShare *)g_hash_table_lookup (file_share_table, &tmp);
153 *old_sharemode = file_share->sharemode;
154 *old_access = file_share->access;
155 *share_info = file_share;
157 g_assert (file_share->handle_refs > 0);
158 file_share->handle_refs += 1;
162 file_share = g_new0 (FileShare, 1);
164 file_share->device = device;
165 file_share->inode = inode;
166 file_share->sharemode = new_sharemode;
167 file_share->access = new_access;
168 file_share->handle_refs = 1;
169 *share_info = file_share;
171 g_hash_table_insert (file_share_table, file_share, file_share);
174 mono_coop_mutex_unlock (&file_share_mutex);
180 _wapi_open (const gchar *pathname, gint flags, mode_t mode)
183 gchar *located_filename;
185 if (flags & O_CREAT) {
186 located_filename = mono_portability_find_file (pathname, FALSE);
187 if (located_filename == NULL) {
189 fd = open (pathname, flags, mode);
193 fd = open (located_filename, flags, mode);
195 g_free (located_filename);
199 fd = open (pathname, flags, mode);
201 if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
202 gint saved_errno = errno;
203 located_filename = mono_portability_find_file (pathname, TRUE);
205 if (located_filename == NULL) {
211 fd = open (located_filename, flags, mode);
213 g_free (located_filename);
221 _wapi_access (const gchar *pathname, gint mode)
226 ret = access (pathname, mode);
228 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
229 gint saved_errno = errno;
230 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
232 if (located_filename == NULL) {
238 ret = access (located_filename, mode);
240 g_free (located_filename);
247 _wapi_chmod (const gchar *pathname, mode_t mode)
252 ret = chmod (pathname, mode);
254 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
255 gint saved_errno = errno;
256 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
258 if (located_filename == NULL) {
264 ret = chmod (located_filename, mode);
266 g_free (located_filename);
273 _wapi_utime (const gchar *filename, const struct utimbuf *buf)
278 ret = utime (filename, buf);
280 if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) {
281 gint saved_errno = errno;
282 gchar *located_filename = mono_portability_find_file (filename, TRUE);
284 if (located_filename == NULL) {
290 ret = utime (located_filename, buf);
292 g_free (located_filename);
299 _wapi_unlink (const gchar *pathname)
304 ret = unlink (pathname);
306 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && IS_PORTABILITY_SET) {
307 gint saved_errno = errno;
308 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
310 if (located_filename == NULL) {
316 ret = unlink (located_filename);
318 g_free (located_filename);
325 _wapi_rename (const gchar *oldpath, const gchar *newpath)
328 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
330 if (located_newpath == NULL) {
332 ret = rename (oldpath, newpath);
336 ret = rename (oldpath, located_newpath);
339 if (ret == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && IS_PORTABILITY_SET) {
340 gint saved_errno = errno;
341 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
343 if (located_oldpath == NULL) {
344 g_free (located_oldpath);
345 g_free (located_newpath);
352 ret = rename (located_oldpath, located_newpath);
354 g_free (located_oldpath);
356 g_free (located_newpath);
363 _wapi_stat (const gchar *path, struct stat *buf)
368 ret = stat (path, buf);
370 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
371 gint saved_errno = errno;
372 gchar *located_filename = mono_portability_find_file (path, TRUE);
374 if (located_filename == NULL) {
380 ret = stat (located_filename, buf);
382 g_free (located_filename);
389 _wapi_lstat (const gchar *path, struct stat *buf)
394 ret = lstat (path, buf);
396 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
397 gint saved_errno = errno;
398 gchar *located_filename = mono_portability_find_file (path, TRUE);
400 if (located_filename == NULL) {
405 ret = lstat (located_filename, buf);
406 g_free (located_filename);
413 _wapi_mkdir (const gchar *pathname, mode_t mode)
416 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
418 if (located_filename == NULL) {
420 ret = mkdir (pathname, mode);
424 ret = mkdir (located_filename, mode);
426 g_free (located_filename);
433 _wapi_rmdir (const gchar *pathname)
438 ret = rmdir (pathname);
440 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
441 gint saved_errno = errno;
442 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
444 if (located_filename == NULL) {
450 ret = rmdir (located_filename);
452 g_free (located_filename);
459 _wapi_chdir (const gchar *path)
466 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
467 gint saved_errno = errno;
468 gchar *located_filename = mono_portability_find_file (path, TRUE);
470 if (located_filename == NULL) {
476 ret = chdir (located_filename);
478 g_free (located_filename);
485 _wapi_basename (const gchar *filename)
487 gchar *new_filename = g_strdup (filename), *ret;
489 if (IS_PORTABILITY_SET) {
490 g_strdelimit (new_filename, "\\", '/');
493 if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
494 gint len = strlen (new_filename);
496 g_memmove (new_filename, new_filename + 2, len - 2);
497 new_filename[len - 2] = '\0';
500 ret = g_path_get_basename (new_filename);
501 g_free (new_filename);
507 _wapi_dirname (const gchar *filename)
509 gchar *new_filename = g_strdup (filename), *ret;
511 if (IS_PORTABILITY_SET) {
512 g_strdelimit (new_filename, "\\", '/');
515 if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
516 gint len = strlen (new_filename);
518 g_memmove (new_filename, new_filename + 2, len - 2);
519 new_filename[len - 2] = '\0';
522 ret = g_path_get_dirname (new_filename);
523 g_free (new_filename);
529 _wapi_g_dir_open (const gchar *path, guint flags, GError **error)
534 ret = g_dir_open (path, flags, error);
536 if (ret == NULL && ((*error)->code == G_FILE_ERROR_NOENT || (*error)->code == G_FILE_ERROR_NOTDIR || (*error)->code == G_FILE_ERROR_NAMETOOLONG) && IS_PORTABILITY_SET) {
537 gchar *located_filename = mono_portability_find_file (path, TRUE);
538 GError *tmp_error = NULL;
540 if (located_filename == NULL) {
545 ret = g_dir_open (located_filename, flags, &tmp_error);
547 g_free (located_filename);
548 if (tmp_error == NULL) {
549 g_clear_error (error);
557 get_errno_from_g_file_error (gint error)
561 case G_FILE_ERROR_ACCES: return EACCES;
564 case G_FILE_ERROR_NAMETOOLONG: return ENAMETOOLONG;
567 case G_FILE_ERROR_NOENT: return ENOENT;
570 case G_FILE_ERROR_NOTDIR: return ENOTDIR;
573 case G_FILE_ERROR_NXIO: return ENXIO;
576 case G_FILE_ERROR_NODEV: return ENODEV;
579 case G_FILE_ERROR_ROFS: return EROFS;
582 case G_FILE_ERROR_TXTBSY: return ETXTBSY;
585 case G_FILE_ERROR_FAULT: return EFAULT;
588 case G_FILE_ERROR_LOOP: return ELOOP;
591 case G_FILE_ERROR_NOSPC: return ENOSPC;
594 case G_FILE_ERROR_NOMEM: return ENOMEM;
597 case G_FILE_ERROR_MFILE: return EMFILE;
600 case G_FILE_ERROR_NFILE: return ENFILE;
603 case G_FILE_ERROR_BADF: return EBADF;
606 case G_FILE_ERROR_INVAL: return EINVAL;
609 case G_FILE_ERROR_PIPE: return EPIPE;
612 case G_FILE_ERROR_AGAIN: return EAGAIN;
615 case G_FILE_ERROR_INTR: return EINTR;
618 case G_FILE_ERROR_IO: return EIO;
621 case G_FILE_ERROR_PERM: return EPERM;
623 case G_FILE_ERROR_FAILED: return ERROR_INVALID_PARAMETER;
625 g_assert_not_reached ();
630 file_compare (gconstpointer a, gconstpointer b)
632 gchar *astr = *(gchar **) a;
633 gchar *bstr = *(gchar **) b;
635 return strcmp (astr, bstr);
638 /* scandir using glib */
640 _wapi_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
642 GError *error = NULL;
646 mono_w32file_unix_glob_t glob_buf;
649 dir = _wapi_g_dir_open (dirname, 0, &error);
651 /* g_dir_open returns ENOENT on directories on which we don't
652 * have read/x permission */
653 gint errnum = get_errno_from_g_file_error (error->code);
654 g_error_free (error);
655 if (errnum == ENOENT &&
656 !_wapi_access (dirname, F_OK) &&
657 _wapi_access (dirname, R_OK|X_OK)) {
665 if (IS_PORTABILITY_CASE) {
666 flags = W32FILE_UNIX_GLOB_IGNORECASE;
669 result = mono_w32file_unix_glob (dir, pattern, flags, &glob_buf);
670 if (g_str_has_suffix (pattern, ".*")) {
671 /* Special-case the patterns ending in '.*', as
672 * windows also matches entries with no extension with
675 * TODO: should this be a MONO_IOMAP option?
677 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
683 result2 = mono_w32file_unix_glob (dir, pattern2, flags | W32FILE_UNIX_GLOB_APPEND | W32FILE_UNIX_GLOB_UNIQUE, &glob_buf);
695 if (glob_buf.gl_pathc == 0) {
697 } else if (result != 0) {
701 names = g_ptr_array_new ();
702 for (i = 0; i < glob_buf.gl_pathc; i++) {
703 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
706 mono_w32file_unix_globfree (&glob_buf);
710 g_ptr_array_sort (names, file_compare);
711 g_ptr_array_set_size (names, result + 1);
713 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
715 g_ptr_array_free (names, TRUE);
722 _wapi_lock_file_region (gint fd, off_t offset, off_t length)
724 struct flock lock_data;
727 if (offset < 0 || length < 0) {
728 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
732 lock_data.l_type = F_WRLCK;
733 lock_data.l_whence = SEEK_SET;
734 lock_data.l_start = offset;
735 lock_data.l_len = length;
738 ret = fcntl (fd, F_SETLK, &lock_data);
739 } while(ret == -1 && errno == EINTR);
741 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret);
745 * if locks are not available (NFS for example),
750 || errno == EOPNOTSUPP
759 mono_w32error_set_last (ERROR_LOCK_VIOLATION);
767 _wapi_unlock_file_region (gint fd, off_t offset, off_t length)
769 struct flock lock_data;
772 lock_data.l_type = F_UNLCK;
773 lock_data.l_whence = SEEK_SET;
774 lock_data.l_start = offset;
775 lock_data.l_len = length;
778 ret = fcntl (fd, F_SETLK, &lock_data);
779 } while(ret == -1 && errno == EINTR);
781 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret);
785 * if locks are not available (NFS for example),
790 || errno == EOPNOTSUPP
799 mono_w32error_set_last (ERROR_LOCK_VIOLATION);
806 static void file_close (gpointer handle, gpointer data);
807 static void file_details (gpointer data);
808 static const gchar* file_typename (void);
809 static gsize file_typesize (void);
810 static gint file_getfiletype(void);
811 static gboolean file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
812 static gboolean file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
813 static gboolean file_flush(gpointer handle);
814 static guint32 file_seek(gpointer handle, gint32 movedistance,
815 gint32 *highmovedistance, gint method);
816 static gboolean file_setendoffile(gpointer handle);
817 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
818 static gboolean file_getfiletime(gpointer handle, FILETIME *create_time,
819 FILETIME *access_time,
820 FILETIME *write_time);
821 static gboolean file_setfiletime(gpointer handle,
822 const FILETIME *create_time,
823 const FILETIME *access_time,
824 const FILETIME *write_time);
825 static guint32 GetDriveTypeFromPath (const gchar *utf8_root_path_name);
827 /* File handle is only signalled for overlapped IO */
828 static MonoW32HandleOps _wapi_file_ops = {
829 file_close, /* close */
833 NULL, /* special_wait */
835 file_details, /* details */
836 file_typename, /* typename */
837 file_typesize, /* typesize */
840 static void console_close (gpointer handle, gpointer data);
841 static void console_details (gpointer data);
842 static const gchar* console_typename (void);
843 static gsize console_typesize (void);
844 static gint console_getfiletype(void);
845 static gboolean console_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
846 static gboolean console_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
848 /* Console is mostly the same as file, except it can block waiting for
851 static MonoW32HandleOps _wapi_console_ops = {
852 console_close, /* close */
856 NULL, /* special_wait */
858 console_details, /* details */
859 console_typename, /* typename */
860 console_typesize, /* typesize */
863 static const gchar* find_typename (void);
864 static gsize find_typesize (void);
866 static MonoW32HandleOps _wapi_find_ops = {
871 NULL, /* special_wait */
874 find_typename, /* typename */
875 find_typesize, /* typesize */
878 static void pipe_close (gpointer handle, gpointer data);
879 static void pipe_details (gpointer data);
880 static const gchar* pipe_typename (void);
881 static gsize pipe_typesize (void);
882 static gint pipe_getfiletype (void);
883 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
884 static gboolean pipe_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
888 static MonoW32HandleOps _wapi_pipe_ops = {
889 pipe_close, /* close */
893 NULL, /* special_wait */
895 pipe_details, /* details */
896 pipe_typename, /* typename */
897 pipe_typesize, /* typesize */
900 static const struct {
901 /* File, console and pipe handles */
902 gint (*getfiletype)(void);
904 /* File, console and pipe handles */
905 gboolean (*readfile)(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
906 gboolean (*writefile)(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
907 gboolean (*flushfile)(gpointer handle);
910 guint32 (*seek)(gpointer handle, gint32 movedistance,
911 gint32 *highmovedistance, gint method);
912 gboolean (*setendoffile)(gpointer handle);
913 guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
914 gboolean (*getfiletime)(gpointer handle, FILETIME *create_time,
915 FILETIME *access_time,
916 FILETIME *write_time);
917 gboolean (*setfiletime)(gpointer handle,
918 const FILETIME *create_time,
919 const FILETIME *access_time,
920 const FILETIME *write_time);
921 } io_ops[MONO_W32HANDLE_COUNT]={
922 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
925 file_read, file_write,
926 file_flush, file_seek,
932 {console_getfiletype,
935 NULL, NULL, NULL, NULL, NULL, NULL},
937 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
939 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
941 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
943 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
944 /* socket (will need at least read and write) */
945 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
947 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
949 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
954 NULL, NULL, NULL, NULL, NULL, NULL},
957 static gboolean lock_while_writing = FALSE;
959 /* Some utility functions.
963 * Check if a file is writable by the current user.
965 * This is is a best effort kind of thing. It assumes a reasonable sane set
966 * of permissions by the underlying OS.
968 * We generally assume that basic unix permission bits are authoritative. Which might not
969 * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc)
971 * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file.
973 * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent
974 * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous
975 * and should not be used with a dynamic runtime.
978 is_file_writable (struct stat *st, const gchar *path)
981 // OS X Finder "locked" or `ls -lO` "uchg".
982 // This only covers one of several cases where an OS X file could be unwritable through special flags.
983 if (st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE))
987 /* Is it globally writable? */
988 if (st->st_mode & S_IWOTH)
991 /* Am I the owner? */
992 if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR))
995 /* Am I in the same group? */
996 if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP))
999 /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid
1000 * but it's the only sane option we have on unix.
1002 return access (path, W_OK) == 0;
1006 static guint32 _wapi_stat_to_file_attributes (const gchar *pathname,
1013 /* FIXME: this could definitely be better, but there seems to
1014 * be no pattern to the attributes that are set
1017 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
1018 if (S_ISSOCK (buf->st_mode))
1019 buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */
1021 filename = _wapi_basename (pathname);
1023 if (S_ISDIR (buf->st_mode)) {
1024 attrs = FILE_ATTRIBUTE_DIRECTORY;
1025 if (!is_file_writable (buf, pathname)) {
1026 attrs |= FILE_ATTRIBUTE_READONLY;
1028 if (filename[0] == '.') {
1029 attrs |= FILE_ATTRIBUTE_HIDDEN;
1032 if (!is_file_writable (buf, pathname)) {
1033 attrs = FILE_ATTRIBUTE_READONLY;
1035 if (filename[0] == '.') {
1036 attrs |= FILE_ATTRIBUTE_HIDDEN;
1038 } else if (filename[0] == '.') {
1039 attrs = FILE_ATTRIBUTE_HIDDEN;
1041 attrs = FILE_ATTRIBUTE_NORMAL;
1046 if (S_ISLNK (lbuf->st_mode)) {
1047 attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
1057 _wapi_set_last_error_from_errno (void)
1059 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
1062 static void _wapi_set_last_path_error_from_errno (const gchar *dir,
1065 if (errno == ENOENT) {
1066 /* Check the path - if it's a missing directory then
1067 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
1073 dirname = _wapi_dirname (path);
1075 dirname = g_strdup (dir);
1078 if (_wapi_access (dirname, F_OK) == 0) {
1079 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
1081 mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
1086 _wapi_set_last_error_from_errno ();
1092 static void file_close (gpointer handle, gpointer data)
1094 /* FIXME: after mono_w32handle_close is coop-aware, change this to MONO_REQ_GC_UNSAFE_MODE and leave just the switch to SAFE around close() below */
1095 MONO_ENTER_GC_UNSAFE;
1096 MonoW32HandleFile *file_handle = (MonoW32HandleFile *)data;
1097 gint fd = file_handle->fd;
1099 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing file handle %p [%s]", __func__, handle,
1100 file_handle->filename);
1102 if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE)
1103 _wapi_unlink (file_handle->filename);
1105 g_free (file_handle->filename);
1107 if (file_handle->share_info)
1108 file_share_release (file_handle->share_info);
1113 MONO_EXIT_GC_UNSAFE;
1116 static void file_details (gpointer data)
1118 MonoW32HandleFile *file = (MonoW32HandleFile *)data;
1120 g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u",
1122 file->fileaccess&GENERIC_READ?'R':'.',
1123 file->fileaccess&GENERIC_WRITE?'W':'.',
1124 file->fileaccess&GENERIC_EXECUTE?'X':'.',
1125 file->sharemode&FILE_SHARE_READ?'R':'.',
1126 file->sharemode&FILE_SHARE_WRITE?'W':'.',
1127 file->sharemode&FILE_SHARE_DELETE?'D':'.',
1131 static const gchar* file_typename (void)
1136 static gsize file_typesize (void)
1138 return sizeof (MonoW32HandleFile);
1141 static gint file_getfiletype(void)
1143 return(FILE_TYPE_DISK);
1147 file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1149 MonoW32HandleFile *file_handle;
1152 MonoThreadInfo *info = mono_thread_info_current ();
1154 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1155 (gpointer *)&file_handle);
1157 g_warning ("%s: error looking up file handle %p", __func__,
1159 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1163 fd = file_handle->fd;
1164 if(bytesread!=NULL) {
1168 if(!(file_handle->fileaccess & GENERIC_READ) &&
1169 !(file_handle->fileaccess & GENERIC_ALL)) {
1170 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1171 __func__, handle, file_handle->fileaccess);
1173 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1179 ret = read (fd, buffer, numbytes);
1181 } while (ret == -1 && errno == EINTR &&
1182 !mono_thread_info_is_interrupt_state (info));
1187 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__,
1188 handle, strerror(err));
1189 mono_w32error_set_last (mono_w32error_unix_to_win32 (err));
1193 if (bytesread != NULL) {
1201 file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
1203 MonoW32HandleFile *file_handle;
1206 off_t current_pos = 0;
1207 MonoThreadInfo *info = mono_thread_info_current ();
1209 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1210 (gpointer *)&file_handle);
1212 g_warning ("%s: error looking up file handle %p", __func__,
1214 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1218 fd = file_handle->fd;
1220 if(byteswritten!=NULL) {
1224 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1225 !(file_handle->fileaccess & GENERIC_ALL)) {
1226 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);
1228 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1232 if (lock_while_writing) {
1233 /* Need to lock the region we're about to write to,
1234 * because we only do advisory locking on POSIX
1238 current_pos = lseek (fd, (off_t)0, SEEK_CUR);
1240 if (current_pos == -1) {
1241 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__,
1242 handle, strerror (errno));
1243 _wapi_set_last_error_from_errno ();
1247 if (_wapi_lock_file_region (fd, current_pos,
1248 numbytes) == FALSE) {
1249 /* The error has already been set */
1256 ret = write (fd, buffer, numbytes);
1258 } while (ret == -1 && errno == EINTR &&
1259 !mono_thread_info_is_interrupt_state (info));
1261 if (lock_while_writing) {
1262 _wapi_unlock_file_region (fd, current_pos, numbytes);
1266 if (errno == EINTR) {
1269 _wapi_set_last_error_from_errno ();
1271 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s",
1272 __func__, handle, strerror(errno));
1277 if (byteswritten != NULL) {
1278 *byteswritten = ret;
1283 static gboolean file_flush(gpointer handle)
1285 MonoW32HandleFile *file_handle;
1289 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1290 (gpointer *)&file_handle);
1292 g_warning ("%s: error looking up file handle %p", __func__,
1294 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1298 fd = file_handle->fd;
1300 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1301 !(file_handle->fileaccess & GENERIC_ALL)) {
1302 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);
1304 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1312 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fsync of handle %p error: %s", __func__, handle,
1315 _wapi_set_last_error_from_errno ();
1322 static guint32 file_seek(gpointer handle, gint32 movedistance,
1323 gint32 *highmovedistance, gint method)
1325 MonoW32HandleFile *file_handle;
1327 gint64 offset, newpos;
1331 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1332 (gpointer *)&file_handle);
1334 g_warning ("%s: error looking up file handle %p", __func__,
1336 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1337 return(INVALID_SET_FILE_POINTER);
1340 fd = file_handle->fd;
1342 if(!(file_handle->fileaccess & GENERIC_READ) &&
1343 !(file_handle->fileaccess & GENERIC_WRITE) &&
1344 !(file_handle->fileaccess & GENERIC_ALL)) {
1345 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);
1347 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1348 return(INVALID_SET_FILE_POINTER);
1362 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: invalid seek type %d", __func__, method);
1364 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1365 return(INVALID_SET_FILE_POINTER);
1368 #ifdef HAVE_LARGE_FILE_SUPPORT
1369 if(highmovedistance==NULL) {
1370 offset=movedistance;
1371 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld (low %d)", __func__,
1372 offset, movedistance);
1374 offset=((gint64) *highmovedistance << 32) | (guint32)movedistance;
1376 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);
1379 offset=movedistance;
1382 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: moving handle %p by %lld bytes from %d", __func__,
1383 handle, (long long)offset, whence);
1385 #ifdef PLATFORM_ANDROID
1386 /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */
1388 newpos=lseek64(fd, offset, whence);
1392 newpos=lseek(fd, offset, whence);
1396 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek on handle %p returned error %s",
1397 __func__, handle, strerror(errno));
1399 _wapi_set_last_error_from_errno ();
1400 return(INVALID_SET_FILE_POINTER);
1403 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek returns %lld", __func__, newpos);
1405 #ifdef HAVE_LARGE_FILE_SUPPORT
1406 ret=newpos & 0xFFFFFFFF;
1407 if(highmovedistance!=NULL) {
1408 *highmovedistance=newpos>>32;
1412 if(highmovedistance!=NULL) {
1413 /* Accurate, but potentially dodgy :-) */
1414 *highmovedistance=0;
1418 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: move of handle %p returning %d/%d", __func__,
1419 handle, ret, highmovedistance==NULL?0:*highmovedistance);
1424 static gboolean file_setendoffile(gpointer handle)
1426 MonoW32HandleFile *file_handle;
1428 struct stat statbuf;
1431 MonoThreadInfo *info = mono_thread_info_current ();
1433 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1434 (gpointer *)&file_handle);
1436 g_warning ("%s: error looking up file handle %p", __func__,
1438 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1441 fd = file_handle->fd;
1443 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1444 !(file_handle->fileaccess & GENERIC_ALL)) {
1445 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);
1447 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1451 /* Find the current file position, and the file length. If
1452 * the file position is greater than the length, write to
1453 * extend the file with a hole. If the file position is less
1454 * than the length, truncate the file.
1458 ret=fstat(fd, &statbuf);
1461 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__,
1462 handle, strerror(errno));
1464 _wapi_set_last_error_from_errno ();
1469 pos=lseek(fd, (off_t)0, SEEK_CUR);
1472 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__,
1473 handle, strerror(errno));
1475 _wapi_set_last_error_from_errno ();
1479 #ifdef FTRUNCATE_DOESNT_EXTEND
1480 off_t size = statbuf.st_size;
1481 /* I haven't bothered to write the configure.ac stuff for this
1482 * because I don't know if any platform needs it. I'm leaving
1483 * this code just in case though
1486 /* Extend the file. Use write() here, because some
1487 * manuals say that ftruncate() behaviour is undefined
1488 * when the file needs extending. The POSIX spec says
1489 * that on XSI-conformant systems it extends, so if
1490 * every system we care about conforms, then we can
1495 ret = write (fd, "", 1);
1497 } while (ret == -1 && errno == EINTR &&
1498 !mono_thread_info_is_interrupt_state (info));
1501 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p extend write failed: %s", __func__, handle, strerror(errno));
1503 _wapi_set_last_error_from_errno ();
1507 /* And put the file position back after the write */
1509 ret = lseek (fd, pos, SEEK_SET);
1512 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p second lseek failed: %s",
1513 __func__, handle, strerror(errno));
1515 _wapi_set_last_error_from_errno ();
1521 /* always truncate, because the extend write() adds an extra
1522 * byte to the end of the file
1526 ret=ftruncate(fd, pos);
1529 while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1531 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ftruncate failed: %s", __func__,
1532 handle, strerror(errno));
1534 _wapi_set_last_error_from_errno ();
1541 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
1543 MonoW32HandleFile *file_handle;
1545 struct stat statbuf;
1550 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1551 (gpointer *)&file_handle);
1553 g_warning ("%s: error looking up file handle %p", __func__,
1555 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1556 return(INVALID_FILE_SIZE);
1558 fd = file_handle->fd;
1560 if(!(file_handle->fileaccess & GENERIC_READ) &&
1561 !(file_handle->fileaccess & GENERIC_WRITE) &&
1562 !(file_handle->fileaccess & GENERIC_ALL)) {
1563 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);
1565 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1566 return(INVALID_FILE_SIZE);
1569 /* If the file has a size with the low bits 0xFFFFFFFF the
1570 * caller can't tell if this is an error, so clear the error
1573 mono_w32error_set_last (ERROR_SUCCESS);
1576 ret = fstat(fd, &statbuf);
1579 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__,
1580 handle, strerror(errno));
1582 _wapi_set_last_error_from_errno ();
1583 return(INVALID_FILE_SIZE);
1586 /* fstat indicates block devices as zero-length, so go a different path */
1588 if (S_ISBLK(statbuf.st_mode)) {
1592 res = ioctl (fd, BLKGETSIZE64, &bigsize);
1595 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ioctl BLKGETSIZE64 failed: %s",
1596 __func__, handle, strerror(errno));
1598 _wapi_set_last_error_from_errno ();
1599 return(INVALID_FILE_SIZE);
1602 size = bigsize & 0xFFFFFFFF;
1603 if (highsize != NULL) {
1604 *highsize = bigsize>>32;
1607 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning block device size %d/%d",
1608 __func__, size, *highsize);
1614 #ifdef HAVE_LARGE_FILE_SUPPORT
1615 size = statbuf.st_size & 0xFFFFFFFF;
1616 if (highsize != NULL) {
1617 *highsize = statbuf.st_size>>32;
1620 if (highsize != NULL) {
1621 /* Accurate, but potentially dodgy :-) */
1624 size = statbuf.st_size;
1627 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning size %d/%d", __func__, size, *highsize);
1632 static gboolean file_getfiletime(gpointer handle, FILETIME *create_time,
1633 FILETIME *access_time,
1634 FILETIME *write_time)
1636 MonoW32HandleFile *file_handle;
1638 struct stat statbuf;
1639 guint64 create_ticks, access_ticks, write_ticks;
1642 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1643 (gpointer *)&file_handle);
1645 g_warning ("%s: error looking up file handle %p", __func__,
1647 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1650 fd = file_handle->fd;
1652 if(!(file_handle->fileaccess & GENERIC_READ) &&
1653 !(file_handle->fileaccess & GENERIC_ALL)) {
1654 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1655 __func__, handle, file_handle->fileaccess);
1657 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1662 ret=fstat(fd, &statbuf);
1665 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle,
1668 _wapi_set_last_error_from_errno ();
1672 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: atime: %ld ctime: %ld mtime: %ld", __func__,
1673 statbuf.st_atime, statbuf.st_ctime,
1676 /* Try and guess a meaningful create time by using the older
1679 /* The magic constant comes from msdn documentation
1680 * "Converting a time_t Value to a File Time"
1682 if(statbuf.st_atime < statbuf.st_ctime) {
1683 create_ticks=((guint64)statbuf.st_atime*10000000)
1684 + 116444736000000000ULL;
1686 create_ticks=((guint64)statbuf.st_ctime*10000000)
1687 + 116444736000000000ULL;
1690 access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
1691 write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
1693 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: aticks: %llu cticks: %llu wticks: %llu", __func__,
1694 access_ticks, create_ticks, write_ticks);
1696 if(create_time!=NULL) {
1697 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
1698 create_time->dwHighDateTime = create_ticks >> 32;
1701 if(access_time!=NULL) {
1702 access_time->dwLowDateTime = access_ticks & 0xFFFFFFFF;
1703 access_time->dwHighDateTime = access_ticks >> 32;
1706 if(write_time!=NULL) {
1707 write_time->dwLowDateTime = write_ticks & 0xFFFFFFFF;
1708 write_time->dwHighDateTime = write_ticks >> 32;
1714 static gboolean file_setfiletime(gpointer handle,
1715 const FILETIME *create_time G_GNUC_UNUSED,
1716 const FILETIME *access_time,
1717 const FILETIME *write_time)
1719 MonoW32HandleFile *file_handle;
1721 struct utimbuf utbuf;
1722 struct stat statbuf;
1723 guint64 access_ticks, write_ticks;
1726 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1727 (gpointer *)&file_handle);
1729 g_warning ("%s: error looking up file handle %p", __func__,
1731 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1734 fd = file_handle->fd;
1736 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1737 !(file_handle->fileaccess & GENERIC_ALL)) {
1738 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);
1740 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1744 if(file_handle->filename == NULL) {
1745 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p unknown filename", __func__, handle);
1747 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1751 /* Get the current times, so we can put the same times back in
1752 * the event that one of the FileTime structs is NULL
1755 ret=fstat (fd, &statbuf);
1758 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle,
1761 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1765 if(access_time!=NULL) {
1766 access_ticks=((guint64)access_time->dwHighDateTime << 32) +
1767 access_time->dwLowDateTime;
1768 /* This is (time_t)0. We can actually go to INT_MIN,
1769 * but this will do for now.
1771 if (access_ticks < 116444736000000000ULL) {
1772 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set access time too early",
1774 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1778 if (sizeof (utbuf.actime) == 4 && ((access_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) {
1779 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",
1781 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1785 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1787 utbuf.actime=statbuf.st_atime;
1790 if(write_time!=NULL) {
1791 write_ticks=((guint64)write_time->dwHighDateTime << 32) +
1792 write_time->dwLowDateTime;
1793 /* This is (time_t)0. We can actually go to INT_MIN,
1794 * but this will do for now.
1796 if (write_ticks < 116444736000000000ULL) {
1797 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time too early",
1799 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1802 if (sizeof (utbuf.modtime) == 4 && ((write_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) {
1803 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",
1805 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1809 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1811 utbuf.modtime=statbuf.st_mtime;
1814 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting handle %p access %ld write %ld", __func__,
1815 handle, utbuf.actime, utbuf.modtime);
1817 ret = _wapi_utime (file_handle->filename, &utbuf);
1819 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p [%s] utime failed: %s", __func__,
1820 handle, file_handle->filename, strerror(errno));
1822 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1829 static void console_close (gpointer handle, gpointer data)
1831 /* FIXME: after mono_w32handle_close is coop-aware, change this to MONO_REQ_GC_UNSAFE_MODE and leave just the switch to SAFE around close() below */
1832 MONO_ENTER_GC_UNSAFE;
1833 MonoW32HandleFile *console_handle = (MonoW32HandleFile *)data;
1834 gint fd = console_handle->fd;
1836 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing console handle %p", __func__, handle);
1838 g_free (console_handle->filename);
1841 if (console_handle->share_info)
1842 file_share_release (console_handle->share_info);
1847 MONO_EXIT_GC_UNSAFE;
1850 static void console_details (gpointer data)
1852 file_details (data);
1855 static const gchar* console_typename (void)
1860 static gsize console_typesize (void)
1862 return sizeof (MonoW32HandleFile);
1865 static gint console_getfiletype(void)
1867 return(FILE_TYPE_CHAR);
1871 console_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1873 MonoW32HandleFile *console_handle;
1876 MonoThreadInfo *info = mono_thread_info_current ();
1878 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
1879 (gpointer *)&console_handle);
1881 g_warning ("%s: error looking up console handle %p", __func__,
1883 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1886 fd = console_handle->fd;
1888 if(bytesread!=NULL) {
1892 if(!(console_handle->fileaccess & GENERIC_READ) &&
1893 !(console_handle->fileaccess & GENERIC_ALL)) {
1894 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1895 __func__, handle, console_handle->fileaccess);
1897 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1903 ret=read(fd, buffer, numbytes);
1905 } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1908 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, handle,
1911 _wapi_set_last_error_from_errno ();
1915 if(bytesread!=NULL) {
1923 console_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
1925 MonoW32HandleFile *console_handle;
1928 MonoThreadInfo *info = mono_thread_info_current ();
1930 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
1931 (gpointer *)&console_handle);
1933 g_warning ("%s: error looking up console handle %p", __func__,
1935 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1938 fd = console_handle->fd;
1940 if(byteswritten!=NULL) {
1944 if(!(console_handle->fileaccess & GENERIC_WRITE) &&
1945 !(console_handle->fileaccess & GENERIC_ALL)) {
1946 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);
1948 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1954 ret = write(fd, buffer, numbytes);
1956 } while (ret == -1 && errno == EINTR &&
1957 !mono_thread_info_is_interrupt_state (info));
1960 if (errno == EINTR) {
1963 _wapi_set_last_error_from_errno ();
1965 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s",
1966 __func__, handle, strerror(errno));
1971 if(byteswritten!=NULL) {
1978 static const gchar* find_typename (void)
1983 static gsize find_typesize (void)
1985 return sizeof (MonoW32HandleFind);
1988 static void pipe_close (gpointer handle, gpointer data)
1990 /* FIXME: after mono_w32handle_close is coop-aware, change this to MONO_REQ_GC_UNSAFE_MODE and leave just the switch to SAFE around close() below */
1991 MONO_ENTER_GC_UNSAFE;
1992 MonoW32HandleFile *pipe_handle = (MonoW32HandleFile*)data;
1993 gint fd = pipe_handle->fd;
1995 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing pipe handle %p fd %d", __func__, handle, fd);
1997 /* No filename with pipe handles */
1999 if (pipe_handle->share_info)
2000 file_share_release (pipe_handle->share_info);
2005 MONO_EXIT_GC_UNSAFE;
2008 static void pipe_details (gpointer data)
2010 file_details (data);
2013 static const gchar* pipe_typename (void)
2018 static gsize pipe_typesize (void)
2020 return sizeof (MonoW32HandleFile);
2023 static gint pipe_getfiletype(void)
2025 return(FILE_TYPE_PIPE);
2029 pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
2031 MonoW32HandleFile *pipe_handle;
2034 MonoThreadInfo *info = mono_thread_info_current ();
2036 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE,
2037 (gpointer *)&pipe_handle);
2039 g_warning ("%s: error looking up pipe handle %p", __func__,
2041 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2044 fd = pipe_handle->fd;
2046 if(bytesread!=NULL) {
2050 if(!(pipe_handle->fileaccess & GENERIC_READ) &&
2051 !(pipe_handle->fileaccess & GENERIC_ALL)) {
2052 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
2053 __func__, handle, pipe_handle->fileaccess);
2055 mono_w32error_set_last (ERROR_ACCESS_DENIED);
2059 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: reading up to %d bytes from pipe %p", __func__,
2064 ret=read(fd, buffer, numbytes);
2066 } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
2069 if (errno == EINTR) {
2072 _wapi_set_last_error_from_errno ();
2074 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__,
2075 handle, strerror(errno));
2081 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read %d bytes from pipe %p", __func__, ret, handle);
2083 if(bytesread!=NULL) {
2091 pipe_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
2093 MonoW32HandleFile *pipe_handle;
2096 MonoThreadInfo *info = mono_thread_info_current ();
2098 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE,
2099 (gpointer *)&pipe_handle);
2101 g_warning ("%s: error looking up pipe handle %p", __func__,
2103 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2106 fd = pipe_handle->fd;
2108 if(byteswritten!=NULL) {
2112 if(!(pipe_handle->fileaccess & GENERIC_WRITE) &&
2113 !(pipe_handle->fileaccess & GENERIC_ALL)) {
2114 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);
2116 mono_w32error_set_last (ERROR_ACCESS_DENIED);
2120 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: writing up to %d bytes to pipe %p", __func__, numbytes,
2125 ret = write (fd, buffer, numbytes);
2127 } while (ret == -1 && errno == EINTR &&
2128 !mono_thread_info_is_interrupt_state (info));
2131 if (errno == EINTR) {
2134 _wapi_set_last_error_from_errno ();
2136 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", __func__,
2137 handle, strerror(errno));
2142 if(byteswritten!=NULL) {
2149 static gint convert_flags(guint32 fileaccess, guint32 createmode)
2153 switch(fileaccess) {
2160 case GENERIC_READ|GENERIC_WRITE:
2164 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown access type 0x%x", __func__,
2169 switch(createmode) {
2171 flags|=O_CREAT|O_EXCL;
2174 flags|=O_CREAT|O_TRUNC;
2181 case TRUNCATE_EXISTING:
2185 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown create mode 0x%x", __func__,
2194 static mode_t convert_perms(guint32 sharemode)
2198 if(sharemode&FILE_SHARE_READ) {
2201 if(sharemode&FILE_SHARE_WRITE) {
2209 static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode,
2211 FileShare **share_info)
2213 gboolean file_already_shared;
2214 guint32 file_existing_share, file_existing_access;
2216 file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info);
2218 if (file_already_shared) {
2219 /* The reference to this share info was incremented
2220 * when we looked it up, so be careful to put it back
2221 * if we conclude we can't use this file.
2223 if (file_existing_share == 0) {
2224 /* Quick and easy, no possibility to share */
2225 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);
2227 file_share_release (*share_info);
2232 if (((file_existing_share == FILE_SHARE_READ) &&
2233 (fileaccess != GENERIC_READ)) ||
2234 ((file_existing_share == FILE_SHARE_WRITE) &&
2235 (fileaccess != GENERIC_WRITE))) {
2236 /* New access mode doesn't match up */
2237 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);
2239 file_share_release (*share_info);
2244 if (((file_existing_access & GENERIC_READ) &&
2245 !(sharemode & FILE_SHARE_READ)) ||
2246 ((file_existing_access & GENERIC_WRITE) &&
2247 !(sharemode & FILE_SHARE_WRITE))) {
2248 /* New share mode doesn't match up */
2249 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);
2251 file_share_release (*share_info);
2256 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__);
2264 share_allows_delete (struct stat *statbuf, FileShare **share_info)
2266 gboolean file_already_shared;
2267 guint32 file_existing_share, file_existing_access;
2269 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);
2271 if (file_already_shared) {
2272 /* The reference to this share info was incremented
2273 * when we looked it up, so be careful to put it back
2274 * if we conclude we can't use this file.
2276 if (file_existing_share == 0) {
2277 /* Quick and easy, no possibility to share */
2278 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);
2280 file_share_release (*share_info);
2285 if (!(file_existing_share & FILE_SHARE_DELETE)) {
2286 /* New access mode doesn't match up */
2287 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);
2289 file_share_release (*share_info);
2294 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__);
2301 mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs)
2303 MonoW32HandleFile file_handle = {0};
2305 gint flags=convert_flags(fileaccess, createmode);
2306 /*mode_t perms=convert_perms(sharemode);*/
2307 /* we don't use sharemode, because that relates to sharing of
2308 * the file when the file is open and is already handled by
2309 * other code, perms instead are the on-disk permissions and
2310 * this is a sane default.
2315 MonoW32HandleType handle_type;
2316 struct stat statbuf;
2318 if (attrs & FILE_ATTRIBUTE_TEMPORARY)
2321 if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
2322 mono_w32error_set_last (ERROR_ENCRYPTION_FAILED);
2323 return INVALID_HANDLE_VALUE;
2327 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2329 mono_w32error_set_last (ERROR_INVALID_NAME);
2330 return(INVALID_HANDLE_VALUE);
2333 filename = mono_unicode_to_external (name);
2334 if (filename == NULL) {
2335 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2337 mono_w32error_set_last (ERROR_INVALID_NAME);
2338 return(INVALID_HANDLE_VALUE);
2341 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening %s with share 0x%x and access 0x%x", __func__,
2342 filename, sharemode, fileaccess);
2344 fd = _wapi_open (filename, flags, perms);
2346 /* If we were trying to open a directory with write permissions
2347 * (e.g. O_WRONLY or O_RDWR), this call will fail with
2348 * EISDIR. However, this is a bit bogus because calls to
2349 * manipulate the directory (e.g. mono_w32file_set_times) will still work on
2350 * the directory because they use other API calls
2351 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
2352 * to open the directory again without write permission.
2354 if (fd == -1 && errno == EISDIR)
2356 /* Try again but don't try to make it writable */
2357 fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
2361 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s: %s", __func__, filename,
2363 _wapi_set_last_path_error_from_errno (NULL, filename);
2366 return(INVALID_HANDLE_VALUE);
2369 if (fd >= mono_w32handle_fd_reserve) {
2370 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__);
2372 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
2379 return(INVALID_HANDLE_VALUE);
2383 ret = fstat (fd, &statbuf);
2386 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fstat error of file %s: %s", __func__,
2387 filename, strerror (errno));
2388 _wapi_set_last_error_from_errno ();
2394 return(INVALID_HANDLE_VALUE);
2397 if (share_allows_open (&statbuf, sharemode, fileaccess,
2398 &file_handle.share_info) == FALSE) {
2399 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2405 return (INVALID_HANDLE_VALUE);
2407 if (file_handle.share_info == NULL) {
2408 /* No space, so no more files can be opened */
2409 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No space in the share table", __func__);
2411 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
2417 return(INVALID_HANDLE_VALUE);
2420 file_handle.filename = filename;
2422 file_handle.fd = fd;
2423 file_handle.fileaccess=fileaccess;
2424 file_handle.sharemode=sharemode;
2425 file_handle.attrs=attrs;
2427 #ifdef HAVE_POSIX_FADVISE
2428 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) {
2430 posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
2433 if (attrs & FILE_FLAG_RANDOM_ACCESS) {
2435 posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM);
2441 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) {
2443 fcntl(fd, F_RDAHEAD, 1);
2449 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
2451 if (S_ISFIFO (statbuf.st_mode)) {
2452 handle_type = MONO_W32HANDLE_PIPE;
2453 /* maintain invariant that pipes have no filename */
2454 file_handle.filename = NULL;
2457 } else if (S_ISCHR (statbuf.st_mode)) {
2458 handle_type = MONO_W32HANDLE_CONSOLE;
2460 handle_type = MONO_W32HANDLE_FILE;
2463 MONO_ENTER_GC_SAFE; /* FIXME: mono_w32handle_new_fd should be updated with coop transitions */
2464 handle = mono_w32handle_new_fd (handle_type, fd, &file_handle);
2466 if (handle == INVALID_HANDLE_VALUE) {
2467 g_warning ("%s: error creating file handle", __func__);
2473 mono_w32error_set_last (ERROR_GEN_FAILURE);
2474 return(INVALID_HANDLE_VALUE);
2477 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle);
2483 mono_w32file_close (gpointer handle)
2487 /* FIXME: we transition here and not in file_close, pipe_close,
2488 * console_close because w32handle_close is not coop aware yet, but it
2489 * calls back into w32file. */
2490 res = mono_w32handle_close (handle);
2495 gboolean mono_w32file_delete(const gunichar2 *name)
2499 gboolean ret = FALSE;
2501 struct stat statbuf;
2502 FileShare *shareinfo;
2506 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2508 mono_w32error_set_last (ERROR_INVALID_NAME);
2512 filename=mono_unicode_to_external(name);
2513 if(filename==NULL) {
2514 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2516 mono_w32error_set_last (ERROR_INVALID_NAME);
2521 /* Check to make sure sharing allows us to open the file for
2522 * writing. See bug 323389.
2524 * Do the checks that don't need an open file descriptor, for
2525 * simplicity's sake. If we really have to do the full checks
2526 * then we can implement that later.
2528 if (_wapi_stat (filename, &statbuf) < 0) {
2529 _wapi_set_last_path_error_from_errno (NULL, filename);
2534 if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
2535 &shareinfo) == FALSE) {
2536 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2541 file_share_release (shareinfo);
2544 retval = _wapi_unlink (filename);
2547 /* On linux, calling unlink on an non-existing file in a read-only mount will fail with EROFS.
2548 The expected behavior is for this function to return FALSE and not trigger an exception.
2549 To work around this behavior, we stat the file on failure.
2551 if (errno == EROFS) {
2553 if (mono_w32file_get_attributes_ex (name, &stat)) //The file exists, so must be due the RO file system
2556 _wapi_set_last_path_error_from_errno (NULL, filename);
2567 MoveFile (gunichar2 *name, gunichar2 *dest_name)
2569 gchar *utf8_name, *utf8_dest_name;
2570 gint result, errno_copy;
2571 struct stat stat_src, stat_dest;
2572 gboolean ret = FALSE;
2573 FileShare *shareinfo;
2576 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2578 mono_w32error_set_last (ERROR_INVALID_NAME);
2582 utf8_name = mono_unicode_to_external (name);
2583 if (utf8_name == NULL) {
2584 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2586 mono_w32error_set_last (ERROR_INVALID_NAME);
2590 if(dest_name==NULL) {
2591 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2594 mono_w32error_set_last (ERROR_INVALID_NAME);
2598 utf8_dest_name = mono_unicode_to_external (dest_name);
2599 if (utf8_dest_name == NULL) {
2600 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2603 mono_w32error_set_last (ERROR_INVALID_NAME);
2608 * In C# land we check for the existence of src, but not for dest.
2609 * We check it here and return the failure if dest exists and is not
2610 * the same file as src.
2612 if (_wapi_stat (utf8_name, &stat_src) < 0) {
2613 if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
2614 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
2616 g_free (utf8_dest_name);
2621 if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
2622 if (stat_dest.st_dev != stat_src.st_dev ||
2623 stat_dest.st_ino != stat_src.st_ino) {
2625 g_free (utf8_dest_name);
2626 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2631 /* Check to make that we have delete sharing permission.
2632 * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
2634 * Do the checks that don't need an open file descriptor, for
2635 * simplicity's sake. If we really have to do the full checks
2636 * then we can implement that later.
2638 if (share_allows_delete (&stat_src, &shareinfo) == FALSE) {
2639 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2643 file_share_release (shareinfo);
2645 result = _wapi_rename (utf8_name, utf8_dest_name);
2649 switch(errno_copy) {
2651 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2655 /* Ignore here, it is dealt with below */
2659 /* We already know src exists. Must be dest that doesn't exist. */
2660 _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name);
2664 _wapi_set_last_error_from_errno ();
2669 g_free (utf8_dest_name);
2671 if (result != 0 && errno_copy == EXDEV) {
2674 if (S_ISDIR (stat_src.st_mode)) {
2675 mono_w32error_set_last (ERROR_NOT_SAME_DEVICE);
2678 /* Try a copy to the new location, and delete the source */
2679 if (!mono_w32file_copy (name, dest_name, FALSE, ©_error)) {
2680 /* mono_w32file_copy will set the error */
2684 return(mono_w32file_delete (name));
2695 write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_errors)
2699 gint buf_size = st_src->st_blksize;
2700 MonoThreadInfo *info = mono_thread_info_current ();
2702 buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
2703 buf = (gchar *) g_malloc (buf_size);
2707 remain = read (src_fd, buf, buf_size);
2710 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2714 _wapi_set_last_error_from_errno ();
2724 while (remain > 0) {
2726 n = write (dest_fd, wbuf, remain);
2729 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2733 _wapi_set_last_error_from_errno ();
2734 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write failed.", __func__);
2749 CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists)
2751 gchar *utf8_src, *utf8_dest;
2752 gint src_fd, dest_fd;
2753 struct stat st, dest_st;
2754 struct utimbuf dest_time;
2755 gboolean ret = TRUE;
2760 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2762 mono_w32error_set_last (ERROR_INVALID_NAME);
2766 utf8_src = mono_unicode_to_external (name);
2767 if (utf8_src == NULL) {
2768 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of source returned NULL",
2771 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2775 if(dest_name==NULL) {
2776 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: dest is NULL", __func__);
2779 mono_w32error_set_last (ERROR_INVALID_NAME);
2783 utf8_dest = mono_unicode_to_external (dest_name);
2784 if (utf8_dest == NULL) {
2785 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of dest returned NULL",
2788 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2795 src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2797 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2806 syscall_res = fstat (src_fd, &st);
2808 if (syscall_res < 0) {
2809 _wapi_set_last_error_from_errno ();
2820 /* Before trying to open/create the dest, we need to report a 'file busy'
2821 * error if src and dest are actually the same file. We do the check here to take
2822 * advantage of the IOMAP capability */
2823 if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev &&
2824 st.st_ino == dest_st.st_ino) {
2832 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2836 if (fail_if_exists) {
2837 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
2839 /* FIXME: it kinda sucks that this code path potentially scans
2840 * the directory twice due to the weird mono_w32error_set_last()
2842 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2844 /* The file does not exist, try creating it */
2845 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
2847 /* Apparently this error is set if we
2848 * overwrite the dest file
2850 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2854 _wapi_set_last_error_from_errno ();
2865 if (!write_file (src_fd, dest_fd, &st, TRUE))
2871 dest_time.modtime = st.st_mtime;
2872 dest_time.actime = st.st_atime;
2874 ret_utime = utime (utf8_dest, &dest_time);
2876 if (ret_utime == -1)
2877 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file [%s] utime failed: %s", __func__, utf8_dest, strerror(errno));
2886 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2891 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s is NULL", __func__, arg_name);
2892 mono_w32error_set_last (ERROR_INVALID_NAME);
2896 utf8_ret = mono_unicode_to_external (arg);
2897 if (utf8_ret == NULL) {
2898 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of %s returned NULL",
2899 __func__, arg_name);
2900 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2908 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved)
2910 gint result, backup_fd = -1,replaced_fd = -1;
2911 gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2912 struct stat stBackup;
2913 gboolean ret = FALSE;
2915 if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2917 if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2918 goto replace_cleanup;
2919 if (backupFileName != NULL) {
2920 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2921 goto replace_cleanup;
2924 if (utf8_backupFileName) {
2925 // Open the backup file for read so we can restore the file if an error occurs.
2926 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2927 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2929 goto replace_cleanup;
2932 result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2934 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2935 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2936 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2937 replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2940 if (replaced_fd == -1)
2941 goto replace_cleanup;
2943 write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2946 goto replace_cleanup;
2952 g_free (utf8_replacedFileName);
2953 g_free (utf8_replacementFileName);
2954 g_free (utf8_backupFileName);
2955 if (backup_fd != -1) {
2960 if (replaced_fd != -1) {
2962 close (replaced_fd);
2968 static MonoCoopMutex stdhandle_mutex;
2971 _wapi_stdhandle_create (gint fd, const gchar *name)
2975 MonoW32HandleFile file_handle = {0};
2977 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating standard handle type %s, fd %d", __func__, name, fd);
2979 /* Check if fd is valid */
2981 flags = fcntl(fd, F_GETFL);
2982 } while (flags == -1 && errno == EINTR);
2985 /* Invalid fd. Not really much point checking for EBADF
2988 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl error on fd %d: %s", __func__, fd, strerror(errno));
2990 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
2991 return INVALID_HANDLE_VALUE;
2994 switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) {
2996 file_handle.fileaccess = GENERIC_READ;
2999 file_handle.fileaccess = GENERIC_WRITE;
3002 file_handle.fileaccess = GENERIC_READ | GENERIC_WRITE;
3005 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't figure out flags 0x%x", __func__, flags);
3006 file_handle.fileaccess = 0;
3010 file_handle.fd = fd;
3011 file_handle.filename = g_strdup(name);
3012 /* some default security attributes might be needed */
3013 file_handle.security_attributes = 0;
3015 /* Apparently input handles can't be written to. (I don't
3016 * know if output or error handles can't be read from.)
3019 file_handle.fileaccess &= ~GENERIC_WRITE;
3021 file_handle.sharemode = 0;
3022 file_handle.attrs = 0;
3024 handle = mono_w32handle_new_fd (MONO_W32HANDLE_CONSOLE, fd, &file_handle);
3025 if (handle == INVALID_HANDLE_VALUE) {
3026 g_warning ("%s: error creating file handle", __func__);
3027 mono_w32error_set_last (ERROR_GEN_FAILURE);
3028 return INVALID_HANDLE_VALUE;
3031 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle);
3037 STD_INPUT_HANDLE = -10,
3038 STD_OUTPUT_HANDLE = -11,
3039 STD_ERROR_HANDLE = -12,
3043 mono_w32file_get_std_handle (gint stdhandle)
3045 MonoW32HandleFile *file_handle;
3052 case STD_INPUT_HANDLE:
3057 case STD_OUTPUT_HANDLE:
3062 case STD_ERROR_HANDLE:
3068 g_assert_not_reached ();
3071 handle = GINT_TO_POINTER (fd);
3073 mono_coop_mutex_lock (&stdhandle_mutex);
3075 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
3076 (gpointer *)&file_handle);
3078 /* Need to create this console handle */
3079 handle = _wapi_stdhandle_create (fd, name);
3081 if (handle == INVALID_HANDLE_VALUE) {
3082 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3088 mono_coop_mutex_unlock (&stdhandle_mutex);
3094 mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
3096 MonoW32HandleType type;
3098 type = mono_w32handle_get_type (handle);
3100 if(io_ops[type].readfile==NULL) {
3101 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3105 return(io_ops[type].readfile (handle, buffer, numbytes, bytesread));
3109 mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
3111 MonoW32HandleType type;
3113 type = mono_w32handle_get_type (handle);
3115 if(io_ops[type].writefile==NULL) {
3116 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3120 return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten));
3124 mono_w32file_flush (gpointer handle)
3126 MonoW32HandleType type;
3128 type = mono_w32handle_get_type (handle);
3130 if(io_ops[type].flushfile==NULL) {
3131 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3135 return(io_ops[type].flushfile (handle));
3139 mono_w32file_truncate (gpointer handle)
3141 MonoW32HandleType type;
3143 type = mono_w32handle_get_type (handle);
3145 if (io_ops[type].setendoffile == NULL) {
3146 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3150 return(io_ops[type].setendoffile (handle));
3154 mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method)
3156 MonoW32HandleType type;
3158 type = mono_w32handle_get_type (handle);
3160 if (io_ops[type].seek == NULL) {
3161 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3162 return(INVALID_SET_FILE_POINTER);
3165 return(io_ops[type].seek (handle, movedistance, highmovedistance,
3170 mono_w32file_get_type(gpointer handle)
3172 MonoW32HandleType type;
3174 type = mono_w32handle_get_type (handle);
3176 if (io_ops[type].getfiletype == NULL) {
3177 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3178 return(FILE_TYPE_UNKNOWN);
3181 return(io_ops[type].getfiletype ());
3185 GetFileSize(gpointer handle, guint32 *highsize)
3187 MonoW32HandleType type;
3189 type = mono_w32handle_get_type (handle);
3191 if (io_ops[type].getfilesize == NULL) {
3192 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3193 return(INVALID_FILE_SIZE);
3196 return(io_ops[type].getfilesize (handle, highsize));
3200 mono_w32file_get_times(gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time)
3202 MonoW32HandleType type;
3204 type = mono_w32handle_get_type (handle);
3206 if (io_ops[type].getfiletime == NULL) {
3207 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3211 return(io_ops[type].getfiletime (handle, create_time, access_time,
3216 mono_w32file_set_times(gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time)
3218 MonoW32HandleType type;
3220 type = mono_w32handle_get_type (handle);
3222 if (io_ops[type].setfiletime == NULL) {
3223 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3227 return(io_ops[type].setfiletime (handle, create_time, access_time,
3231 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
3232 * January 1 1601 GMT
3235 #define TICKS_PER_MILLISECOND 10000L
3236 #define TICKS_PER_SECOND 10000000L
3237 #define TICKS_PER_MINUTE 600000000L
3238 #define TICKS_PER_HOUR 36000000000LL
3239 #define TICKS_PER_DAY 864000000000LL
3241 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
3243 static const guint16 mon_yday[2][13]={
3244 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
3245 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
3249 mono_w32file_filetime_to_systemtime(const FILETIME *file_time, SYSTEMTIME *system_time)
3251 gint64 file_ticks, totaldays, rem, y;
3254 if(system_time==NULL) {
3255 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: system_time NULL", __func__);
3257 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3261 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
3262 file_time->dwLowDateTime;
3264 /* Really compares if file_ticks>=0x8000000000000000
3265 * (LLONG_MAX+1) but we're working with a signed value for the
3266 * year and day calculation to work later
3269 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file_time too big", __func__);
3271 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3275 totaldays=(file_ticks / TICKS_PER_DAY);
3276 rem = file_ticks % TICKS_PER_DAY;
3277 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld rem: %lld", __func__, totaldays, rem);
3279 system_time->wHour=rem/TICKS_PER_HOUR;
3280 rem %= TICKS_PER_HOUR;
3281 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem);
3283 system_time->wMinute = rem / TICKS_PER_MINUTE;
3284 rem %= TICKS_PER_MINUTE;
3285 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Minute: %d rem: %lld", __func__, system_time->wMinute,
3288 system_time->wSecond = rem / TICKS_PER_SECOND;
3289 rem %= TICKS_PER_SECOND;
3290 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Second: %d rem: %lld", __func__, system_time->wSecond,
3293 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
3294 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Milliseconds: %d", __func__,
3295 system_time->wMilliseconds);
3297 /* January 1, 1601 was a Monday, according to Emacs calendar */
3298 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
3299 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day of week: %d", __func__, system_time->wDayOfWeek);
3301 /* This algorithm to find year and month given days from epoch
3306 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
3307 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
3309 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
3310 /* Guess a corrected year, assuming 365 days per year */
3311 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
3312 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld yg: %lld y: %lld", __func__,
3315 g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__,
3316 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
3318 /* Adjust days and y to match the guessed year. */
3319 totaldays -= ((yg - y) * 365
3320 + LEAPS_THRU_END_OF (yg - 1)
3321 - LEAPS_THRU_END_OF (y - 1));
3322 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays);
3324 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: y: %lld", __func__, y);
3327 system_time->wYear = y;
3328 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Year: %d", __func__, system_time->wYear);
3330 ip = mon_yday[isleap(y)];
3332 for(y=11; totaldays < ip[y]; --y) {
3336 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays);
3338 system_time->wMonth = y + 1;
3339 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Month: %d", __func__, system_time->wMonth);
3341 system_time->wDay = totaldays + 1;
3342 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day: %d", __func__, system_time->wDay);
3348 mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data)
3350 MonoW32HandleFind find_handle = {0};
3352 gchar *utf8_pattern = NULL, *dir_part, *entry_part;
3355 if (pattern == NULL) {
3356 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pattern is NULL", __func__);
3358 mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
3359 return(INVALID_HANDLE_VALUE);
3362 utf8_pattern = mono_unicode_to_external (pattern);
3363 if (utf8_pattern == NULL) {
3364 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3366 mono_w32error_set_last (ERROR_INVALID_NAME);
3367 return(INVALID_HANDLE_VALUE);
3370 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for [%s]", __func__, utf8_pattern);
3372 /* Figure out which bit of the pattern is the directory */
3373 dir_part = _wapi_dirname (utf8_pattern);
3374 entry_part = _wapi_basename (utf8_pattern);
3377 /* Don't do this check for now, it breaks if directories
3378 * really do have metachars in their names (see bug 58116).
3379 * FIXME: Figure out a better solution to keep some checks...
3381 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3382 mono_w32error_set_last (ERROR_INVALID_NAME);
3384 g_free (entry_part);
3385 g_free (utf8_pattern);
3386 return(INVALID_HANDLE_VALUE);
3390 /* The pattern can specify a directory or a set of files.
3392 * The pattern can have wildcard characters ? and *, but only
3393 * in the section after the last directory delimiter. (Return
3394 * ERROR_INVALID_NAME if there are wildcards in earlier path
3395 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3396 * means "match one character", "??" seems to mean "match one
3397 * or two characters", "???" seems to mean "match one, two or
3398 * three characters", etc. Windows will also try and match
3399 * the mangled "short name" of files, so 8 character patterns
3400 * with wildcards will show some surprising results.
3402 * All the written documentation I can find says that '?'
3403 * should only match one character, and doesn't mention '??',
3404 * '???' etc. I'm going to assume that the strict behaviour
3405 * (ie '???' means three and only three characters) is the
3406 * correct one, because that lets me use fnmatch(3) rather
3407 * than mess around with regexes.
3410 find_handle.namelist = NULL;
3411 result = _wapi_io_scandir (dir_part, entry_part,
3412 &find_handle.namelist);
3415 /* No files, which windows seems to call
3418 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
3419 g_free (utf8_pattern);
3420 g_free (entry_part);
3422 return (INVALID_HANDLE_VALUE);
3426 _wapi_set_last_path_error_from_errno (dir_part, NULL);
3427 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: scandir error: %s", __func__, g_strerror (errno));
3428 g_free (utf8_pattern);
3429 g_free (entry_part);
3431 return (INVALID_HANDLE_VALUE);
3434 g_free (utf8_pattern);
3435 g_free (entry_part);
3437 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Got %d matches", __func__, result);
3439 find_handle.dir_part = dir_part;
3440 find_handle.num = result;
3441 find_handle.count = 0;
3443 handle = mono_w32handle_new (MONO_W32HANDLE_FIND, &find_handle);
3444 if (handle == INVALID_HANDLE_VALUE) {
3445 g_warning ("%s: error creating find handle", __func__);
3447 g_free (entry_part);
3448 g_free (utf8_pattern);
3449 mono_w32error_set_last (ERROR_GEN_FAILURE);
3451 return(INVALID_HANDLE_VALUE);
3454 if (handle != INVALID_HANDLE_VALUE &&
3455 !mono_w32file_find_next (handle, find_data)) {
3456 mono_w32file_find_close (handle);
3457 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3458 handle = INVALID_HANDLE_VALUE;
3465 mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data)
3467 MonoW32HandleFind *find_handle;
3469 struct stat buf, linkbuf;
3472 gchar *utf8_filename, *utf8_basename;
3473 gunichar2 *utf16_basename;
3476 gboolean ret = FALSE;
3478 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND,
3479 (gpointer *)&find_handle);
3481 g_warning ("%s: error looking up find handle %p", __func__,
3483 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3487 mono_w32handle_lock_handle (handle);
3490 if (find_handle->count >= find_handle->num) {
3491 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3495 /* stat next match */
3497 filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3499 result = _wapi_stat (filename, &buf);
3500 if (result == -1 && errno == ENOENT) {
3501 /* Might be a dangling symlink */
3502 result = _wapi_lstat (filename, &buf);
3506 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: stat failed: %s", __func__, filename);
3512 result = _wapi_lstat (filename, &linkbuf);
3514 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lstat failed: %s", __func__, filename);
3520 utf8_filename = mono_utf8_from_external (filename);
3521 if (utf8_filename == NULL) {
3522 /* We couldn't turn this filename into utf8 (eg the
3523 * encoding of the name wasn't convertible), so just
3526 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
3533 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Found [%s]", __func__, utf8_filename);
3535 /* fill data block */
3537 if (buf.st_mtime < buf.st_ctime)
3538 create_time = buf.st_mtime;
3540 create_time = buf.st_ctime;
3542 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
3544 time_t_to_filetime (create_time, &find_data->ftCreationTime);
3545 time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3546 time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3548 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3549 find_data->nFileSizeHigh = 0;
3550 find_data->nFileSizeLow = 0;
3552 find_data->nFileSizeHigh = buf.st_size >> 32;
3553 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3556 find_data->dwReserved0 = 0;
3557 find_data->dwReserved1 = 0;
3559 utf8_basename = _wapi_basename (utf8_filename);
3560 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3562 if(utf16_basename==NULL) {
3563 g_free (utf8_basename);
3564 g_free (utf8_filename);
3569 /* utf16 is 2 * utf8 */
3572 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3574 /* Truncating a utf16 string like this might leave the last
3577 memcpy (find_data->cFileName, utf16_basename,
3578 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3580 find_data->cAlternateFileName [0] = 0; /* not used */
3582 g_free (utf8_basename);
3583 g_free (utf8_filename);
3584 g_free (utf16_basename);
3587 mono_w32handle_unlock_handle (handle);
3593 mono_w32file_find_close (gpointer handle)
3595 MonoW32HandleFind *find_handle;
3598 if (handle == NULL) {
3599 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3603 ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND,
3604 (gpointer *)&find_handle);
3606 g_warning ("%s: error looking up find handle %p", __func__,
3608 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3612 mono_w32handle_lock_handle (handle);
3614 g_strfreev (find_handle->namelist);
3615 g_free (find_handle->dir_part);
3617 mono_w32handle_unlock_handle (handle);
3620 mono_w32handle_unref (handle);
3627 mono_w32file_create_directory (const gunichar2 *name)
3633 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3635 mono_w32error_set_last (ERROR_INVALID_NAME);
3639 utf8_name = mono_unicode_to_external (name);
3640 if (utf8_name == NULL) {
3641 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3643 mono_w32error_set_last (ERROR_INVALID_NAME);
3647 result = _wapi_mkdir (utf8_name, 0777);
3654 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3660 mono_w32file_remove_directory (const gunichar2 *name)
3666 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3668 mono_w32error_set_last (ERROR_INVALID_NAME);
3672 utf8_name = mono_unicode_to_external (name);
3673 if (utf8_name == NULL) {
3674 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3676 mono_w32error_set_last (ERROR_INVALID_NAME);
3680 result = _wapi_rmdir (utf8_name);
3682 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3693 mono_w32file_get_attributes (const gunichar2 *name)
3696 struct stat buf, linkbuf;
3701 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3703 mono_w32error_set_last (ERROR_INVALID_NAME);
3707 utf8_name = mono_unicode_to_external (name);
3708 if (utf8_name == NULL) {
3709 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3711 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3712 return (INVALID_FILE_ATTRIBUTES);
3715 result = _wapi_stat (utf8_name, &buf);
3716 if (result == -1 && (errno == ENOENT || errno == ELOOP)) {
3717 /* Might be a dangling symlink... */
3718 result = _wapi_lstat (utf8_name, &buf);
3722 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3724 return (INVALID_FILE_ATTRIBUTES);
3727 result = _wapi_lstat (utf8_name, &linkbuf);
3729 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3731 return (INVALID_FILE_ATTRIBUTES);
3734 ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3742 mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat)
3746 struct stat buf, linkbuf;
3750 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3752 mono_w32error_set_last (ERROR_INVALID_NAME);
3756 utf8_name = mono_unicode_to_external (name);
3757 if (utf8_name == NULL) {
3758 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3760 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3764 result = _wapi_stat (utf8_name, &buf);
3765 if (result == -1 && errno == ENOENT) {
3766 /* Might be a dangling symlink... */
3767 result = _wapi_lstat (utf8_name, &buf);
3771 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3776 result = _wapi_lstat (utf8_name, &linkbuf);
3778 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3783 /* fill stat block */
3785 stat->attributes = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3786 stat->creation_time = (((guint64) (buf.st_mtime < buf.st_ctime ? buf.st_mtime : buf.st_ctime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3787 stat->last_access_time = (((guint64) (buf.st_atime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3788 stat->last_write_time = (((guint64) (buf.st_mtime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3789 stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size;
3796 mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs)
3798 /* FIXME: think of something clever to do on unix */
3804 * Currently we only handle one *internal* case, with a value that is
3805 * not standard: 0x80000000, which means `set executable bit'
3809 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3811 mono_w32error_set_last (ERROR_INVALID_NAME);
3815 utf8_name = mono_unicode_to_external (name);
3816 if (utf8_name == NULL) {
3817 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3819 mono_w32error_set_last (ERROR_INVALID_NAME);
3823 result = _wapi_stat (utf8_name, &buf);
3824 if (result == -1 && errno == ENOENT) {
3825 /* Might be a dangling symlink... */
3826 result = _wapi_lstat (utf8_name, &buf);
3830 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3835 /* Contrary to the documentation, ms allows NORMAL to be
3836 * specified along with other attributes, so dont bother to
3837 * catch that case here.
3839 if (attrs & FILE_ATTRIBUTE_READONLY) {
3840 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP));
3842 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR);
3845 /* Ignore the other attributes for now */
3847 if (attrs & 0x80000000){
3848 mode_t exec_mask = 0;
3850 if ((buf.st_mode & S_IRUSR) != 0)
3851 exec_mask |= S_IXUSR;
3853 if ((buf.st_mode & S_IRGRP) != 0)
3854 exec_mask |= S_IXGRP;
3856 if ((buf.st_mode & S_IROTH) != 0)
3857 exec_mask |= S_IXOTH;
3860 result = chmod (utf8_name, buf.st_mode | exec_mask);
3863 /* Don't bother to reset executable (might need to change this
3873 mono_w32file_get_cwd (guint32 length, gunichar2 *buffer)
3875 gunichar2 *utf16_path;
3879 if (getcwd ((gchar*)buffer, length) == NULL) {
3880 if (errno == ERANGE) { /*buffer length is not big enough */
3881 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*/
3884 utf16_path = mono_unicode_from_external (path, &bytes);
3885 g_free (utf16_path);
3889 _wapi_set_last_error_from_errno ();
3893 utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3894 count = (bytes/2)+1;
3895 g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3897 /* Add the terminator */
3898 memset (buffer, '\0', bytes+2);
3899 memcpy (buffer, utf16_path, bytes);
3901 g_free (utf16_path);
3907 mono_w32file_set_cwd (const gunichar2 *path)
3913 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3917 utf8_path = mono_unicode_to_external (path);
3918 if (_wapi_chdir (utf8_path) != 0) {
3919 _wapi_set_last_error_from_errno ();
3930 mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size)
3932 MonoW32HandleFile pipe_read_handle = {0};
3933 MonoW32HandleFile pipe_write_handle = {0};
3934 gpointer read_handle;
3935 gpointer write_handle;
3939 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating pipe", __func__);
3945 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error creating pipe: %s", __func__,
3948 _wapi_set_last_error_from_errno ();
3952 if (filedes[0] >= mono_w32handle_fd_reserve ||
3953 filedes[1] >= mono_w32handle_fd_reserve) {
3954 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__);
3956 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
3966 /* filedes[0] is open for reading, filedes[1] for writing */
3968 pipe_read_handle.fd = filedes [0];
3969 pipe_read_handle.fileaccess = GENERIC_READ;
3970 read_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[0],
3972 if (read_handle == INVALID_HANDLE_VALUE) {
3973 g_warning ("%s: error creating pipe read handle", __func__);
3978 mono_w32error_set_last (ERROR_GEN_FAILURE);
3983 pipe_write_handle.fd = filedes [1];
3984 pipe_write_handle.fileaccess = GENERIC_WRITE;
3985 write_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[1],
3986 &pipe_write_handle);
3987 if (write_handle == INVALID_HANDLE_VALUE) {
3988 g_warning ("%s: error creating pipe write handle", __func__);
3991 mono_w32handle_unref (read_handle);
3996 mono_w32error_set_last (ERROR_GEN_FAILURE);
4001 *readpipe = read_handle;
4002 *writepipe = write_handle;
4004 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning pipe: read handle %p, write handle %p",
4005 __func__, read_handle, write_handle);
4010 #ifdef HAVE_GETFSSTAT
4011 /* Darwin has getfsstat */
4013 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
4015 struct statfs *stats;
4018 glong length, total = 0;
4022 n = getfsstat (NULL, 0, MNT_NOWAIT);
4026 size = n * sizeof (struct statfs);
4027 stats = (struct statfs *) g_malloc (size);
4031 syscall_res = getfsstat (stats, size, MNT_NOWAIT);
4033 if (syscall_res == -1){
4037 for (i = 0; i < n; i++){
4038 dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL);
4039 if (total + length < len){
4040 memcpy (buf + total, dir, sizeof (gunichar2) * length);
4041 buf [total+length] = 0;
4044 total += length + 1;
4053 /* In-place octal sequence replacement */
4055 unescape_octal (gchar *str)
4064 while (*rptr != '\0') {
4065 if (*rptr == '\\') {
4068 c = (*(rptr++) - '0') << 6;
4069 c += (*(rptr++) - '0') << 3;
4070 c += *(rptr++) - '0';
4072 } else if (wptr != rptr) {
4080 static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf);
4083 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
4084 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
4085 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
4090 guint32 buffer_index;
4091 guint32 mountpoint_index;
4092 guint32 field_number;
4093 guint32 allocated_size;
4094 guint32 fsname_index;
4095 guint32 fstype_index;
4096 gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1];
4097 gchar *mountpoint_allocated;
4098 gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER];
4099 gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
4100 gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
4103 gboolean check_mount_source;
4104 } LinuxMountInfoParseState;
4106 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
4107 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
4108 static void append_to_mountpoint (LinuxMountInfoParseState *state);
4109 static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
4112 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
4116 LinuxMountInfoParseState state;
4117 gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL;
4119 memset (buf, 0, len * sizeof (gunichar2));
4121 fd = open ("/proc/self/mountinfo", O_RDONLY);
4124 parser = GetLogicalDriveStrings_MountInfo;
4127 fd = open ("/proc/mounts", O_RDONLY);
4130 parser = GetLogicalDriveStrings_Mounts;
4134 ret = GetLogicalDriveStrings_Mtab (len, buf);
4138 memset (&state, 0, sizeof (LinuxMountInfoParseState));
4139 state.field_number = 1;
4140 state.delimiter = ' ';
4144 state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER);
4146 if (!(state.nbytes > 0))
4148 state.buffer_index = 0;
4150 while ((*parser)(len, buf, &state)) {
4151 if (state.buffer [state.buffer_index] == '\n') {
4152 gboolean quit = add_drive_string (len, buf, &state);
4153 state.field_number = 1;
4154 state.buffer_index++;
4155 if (state.mountpoint_allocated) {
4156 g_free (state.mountpoint_allocated);
4157 state.mountpoint_allocated = NULL;
4177 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4181 if (state->field_number == 1)
4182 state->check_mount_source = TRUE;
4184 while (state->buffer_index < (guint32)state->nbytes) {
4185 if (state->buffer [state->buffer_index] == state->delimiter) {
4186 state->field_number++;
4187 switch (state->field_number) {
4189 state->mountpoint_index = 0;
4193 if (state->mountpoint_allocated)
4194 state->mountpoint_allocated [state->mountpoint_index] = 0;
4196 state->mountpoint [state->mountpoint_index] = 0;
4200 ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index);
4202 state->buffer_index = (ptr - (gchar*)state->buffer) - 1;
4204 state->buffer_index = state->nbytes;
4207 state->buffer_index++;
4209 } else if (state->buffer [state->buffer_index] == '\n')
4212 switch (state->field_number) {
4214 if (state->check_mount_source) {
4215 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4216 /* We can ignore the rest, it's a device
4218 state->check_mount_source = FALSE;
4219 state->fsname [state->fsname_index++] = '/';
4222 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4223 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4228 append_to_mountpoint (state);
4232 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4233 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4237 state->buffer_index++;
4243 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4245 while (state->buffer_index < (guint32)state->nbytes) {
4246 if (state->buffer [state->buffer_index] == state->delimiter) {
4247 state->field_number++;
4248 switch (state->field_number) {
4250 state->mountpoint_index = 0;
4254 if (state->mountpoint_allocated)
4255 state->mountpoint_allocated [state->mountpoint_index] = 0;
4257 state->mountpoint [state->mountpoint_index] = 0;
4261 state->delimiter = '-';
4265 state->delimiter = ' ';
4269 state->check_mount_source = TRUE;
4272 state->buffer_index++;
4274 } else if (state->buffer [state->buffer_index] == '\n')
4277 switch (state->field_number) {
4279 append_to_mountpoint (state);
4283 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4284 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4288 if (state->check_mount_source) {
4289 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4290 /* We can ignore the rest, it's a device
4292 state->check_mount_source = FALSE;
4293 state->fsname [state->fsname_index++] = '/';
4296 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4297 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4302 state->buffer_index++;
4309 append_to_mountpoint (LinuxMountInfoParseState *state)
4311 gchar ch = state->buffer [state->buffer_index];
4312 if (state->mountpoint_allocated) {
4313 if (state->mountpoint_index >= state->allocated_size) {
4314 guint32 newsize = (state->allocated_size << 1) + 1;
4315 gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar));
4317 memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index);
4318 g_free (state->mountpoint_allocated);
4319 state->mountpoint_allocated = newbuf;
4320 state->allocated_size = newsize;
4322 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4324 if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) {
4325 state->allocated_size = (state->mountpoint_index << 1) + 1;
4326 state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar));
4327 memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index);
4328 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4330 state->mountpoint [state->mountpoint_index++] = ch;
4335 add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4337 gboolean quit = FALSE;
4338 gboolean ignore_entry;
4340 if (state->fsname_index == 1 && state->fsname [0] == '/')
4341 ignore_entry = FALSE;
4342 else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 ||
4343 memcmp ("aufs", state->fstype, state->fstype_index) == 0) {
4344 /* Don't ignore overlayfs and aufs - these might be used on Docker
4345 * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */
4346 ignore_entry = FALSE;
4347 } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) {
4348 ignore_entry = TRUE;
4349 } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) {
4350 /* Ignore GNOME's gvfs */
4351 if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0)
4352 ignore_entry = TRUE;
4354 ignore_entry = FALSE;
4355 } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0)
4356 ignore_entry = FALSE;
4358 ignore_entry = TRUE;
4360 if (!ignore_entry) {
4363 gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint;
4365 unescape_octal (mountpoint);
4366 dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL);
4367 if (state->total + length + 1 > len) {
4369 state->total = len * 2;
4372 memcpy (buf + state->total, dir, sizeof (gunichar2) * length);
4373 state->total += length;
4377 state->fsname_index = 0;
4378 state->fstype_index = 0;
4384 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
4386 return GetLogicalDriveStrings_Mtab (len, buf);
4390 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf)
4393 gunichar2 *ptr, *dir;
4394 glong length, total = 0;
4398 memset (buf, 0, sizeof (gunichar2) * (len + 1));
4403 /* Sigh, mntent and friends don't work well.
4404 * It stops on the first line that doesn't begin with a '/'.
4405 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4407 fp = fopen ("/etc/mtab", "rt");
4411 fp = fopen ("/etc/mnttab", "rt");
4421 fgets_res = fgets (buffer, 512, fp);
4428 splitted = g_strsplit (buffer, " ", 0);
4429 if (!*splitted || !*(splitted + 1)) {
4430 g_strfreev (splitted);
4434 unescape_octal (*(splitted + 1));
4435 dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
4436 g_strfreev (splitted);
4437 if (total + length + 1 > len) {
4442 return len * 2; /* guess */
4445 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4447 total += length + 1;
4454 /* Commented out, does not work with my mtab!!! - Gonz */
4455 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4459 gunichar2 *ptr, *dir;
4460 glong len, total = 0;
4464 fp = setmntent ("/etc/mtab", "rt");
4468 fp = setmntent ("/etc/mnttab", "rt");
4477 mnt = getmntent (fp);
4481 g_print ("GOT %s\n", mnt->mnt_dir);
4482 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4483 if (total + len + 1 > len) {
4487 return len * 2; /* guess */
4490 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4504 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
4506 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)
4509 struct statvfs fsstat;
4510 #elif defined(HAVE_STATFS)
4511 struct statfs fsstat;
4513 gboolean isreadonly;
4514 gchar *utf8_path_name;
4516 unsigned long block_size;
4518 if (path_name == NULL) {
4519 utf8_path_name = g_strdup (g_get_current_dir());
4520 if (utf8_path_name == NULL) {
4521 mono_w32error_set_last (ERROR_DIRECTORY);
4526 utf8_path_name = mono_unicode_to_external (path_name);
4527 if (utf8_path_name == NULL) {
4528 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
4530 mono_w32error_set_last (ERROR_INVALID_NAME);
4538 ret = statvfs (utf8_path_name, &fsstat);
4540 isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
4541 block_size = fsstat.f_frsize;
4542 #elif defined(HAVE_STATFS)
4544 ret = statfs (utf8_path_name, &fsstat);
4546 #if defined (MNT_RDONLY)
4547 isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
4548 #elif defined (MS_RDONLY)
4549 isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY);
4551 block_size = fsstat.f_bsize;
4553 } while(ret == -1 && errno == EINTR);
4555 g_free(utf8_path_name);
4558 _wapi_set_last_error_from_errno ();
4559 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: statvfs failed: %s", __func__, strerror (errno));
4563 /* total number of free bytes for non-root */
4564 if (free_bytes_avail != NULL) {
4566 *free_bytes_avail = 0;
4569 *free_bytes_avail = block_size * (guint64)fsstat.f_bavail;
4573 /* total number of bytes available for non-root */
4574 if (total_number_of_bytes != NULL) {
4575 *total_number_of_bytes = block_size * (guint64)fsstat.f_blocks;
4578 /* total number of bytes available for root */
4579 if (total_number_of_free_bytes != NULL) {
4581 *total_number_of_free_bytes = 0;
4584 *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree;
4592 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)
4594 if (free_bytes_avail != NULL) {
4595 *free_bytes_avail = (guint64) -1;
4598 if (total_number_of_bytes != NULL) {
4599 *total_number_of_bytes = (guint64) -1;
4602 if (total_number_of_free_bytes != NULL) {
4603 *total_number_of_free_bytes = (guint64) -1;
4611 * General Unix support
4616 const long fstypeid;
4618 const gchar* fstype;
4621 static _wapi_drive_type _wapi_drive_types[] = {
4623 { DRIVE_REMOTE, "afp" },
4624 { DRIVE_REMOTE, "autofs" },
4625 { DRIVE_CDROM, "cddafs" },
4626 { DRIVE_CDROM, "cd9660" },
4627 { DRIVE_RAMDISK, "devfs" },
4628 { DRIVE_FIXED, "exfat" },
4629 { DRIVE_RAMDISK, "fdesc" },
4630 { DRIVE_REMOTE, "ftp" },
4631 { DRIVE_FIXED, "hfs" },
4632 { DRIVE_FIXED, "msdos" },
4633 { DRIVE_REMOTE, "nfs" },
4634 { DRIVE_FIXED, "ntfs" },
4635 { DRIVE_REMOTE, "smbfs" },
4636 { DRIVE_FIXED, "udf" },
4637 { DRIVE_REMOTE, "webdav" },
4638 { DRIVE_UNKNOWN, NULL }
4640 { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"},
4641 { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"},
4642 { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"},
4643 { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"},
4644 { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"},
4645 { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" },
4646 { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"},
4647 { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"},
4648 { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"},
4649 { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"},
4650 { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"},
4651 { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"},
4652 { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"},
4653 { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"},
4654 { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"},
4655 { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"},
4656 { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"},
4657 { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"},
4658 { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"},
4659 { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"},
4660 { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"},
4661 { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"},
4662 { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"},
4663 { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"},
4664 { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"},
4665 { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"},
4666 { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"},
4667 { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"},
4668 { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"},
4669 { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"},
4670 { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"},
4671 { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"},
4672 { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"},
4673 { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"},
4674 { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"},
4675 { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"},
4676 { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"},
4677 { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"},
4678 { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"},
4679 { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"},
4680 { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"},
4681 { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"},
4682 { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"},
4683 { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"},
4684 { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"},
4685 { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"},
4686 { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"},
4687 { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"},
4688 { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"},
4689 { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"},
4690 { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"},
4691 { DRIVE_FIXED, UFS_MAGIC, "ufs"},
4692 { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"},
4693 { DRIVE_FIXED, UFS2_MAGIC, "ufs2"},
4694 { DRIVE_FIXED, UFS_CIGAM, "ufs"},
4695 { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"},
4696 { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"},
4697 { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"},
4698 { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"},
4699 { DRIVE_FIXED, V9FS_MAGIC, "9p"},
4700 { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"},
4701 { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"},
4702 { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"},
4703 { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"},
4704 { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"},
4705 { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"},
4706 { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"},
4707 { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"},
4708 { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"},
4709 { DRIVE_FIXED, OMFS_MAGIC, "omfs"},
4710 { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"},
4711 { DRIVE_UNKNOWN, 0, NULL}
4713 { DRIVE_RAMDISK, "ramfs" },
4714 { DRIVE_RAMDISK, "tmpfs" },
4715 { DRIVE_RAMDISK, "proc" },
4716 { DRIVE_RAMDISK, "sysfs" },
4717 { DRIVE_RAMDISK, "debugfs" },
4718 { DRIVE_RAMDISK, "devpts" },
4719 { DRIVE_RAMDISK, "securityfs" },
4720 { DRIVE_CDROM, "iso9660" },
4721 { DRIVE_FIXED, "ext2" },
4722 { DRIVE_FIXED, "ext3" },
4723 { DRIVE_FIXED, "ext4" },
4724 { DRIVE_FIXED, "sysv" },
4725 { DRIVE_FIXED, "reiserfs" },
4726 { DRIVE_FIXED, "ufs" },
4727 { DRIVE_FIXED, "vfat" },
4728 { DRIVE_FIXED, "msdos" },
4729 { DRIVE_FIXED, "udf" },
4730 { DRIVE_FIXED, "hfs" },
4731 { DRIVE_FIXED, "hpfs" },
4732 { DRIVE_FIXED, "qnx4" },
4733 { DRIVE_FIXED, "ntfs" },
4734 { DRIVE_FIXED, "ntfs-3g" },
4735 { DRIVE_REMOTE, "smbfs" },
4736 { DRIVE_REMOTE, "fuse" },
4737 { DRIVE_REMOTE, "nfs" },
4738 { DRIVE_REMOTE, "nfs4" },
4739 { DRIVE_REMOTE, "cifs" },
4740 { DRIVE_REMOTE, "ncpfs" },
4741 { DRIVE_REMOTE, "coda" },
4742 { DRIVE_REMOTE, "afs" },
4743 { DRIVE_UNKNOWN, NULL }
4748 static guint32 _wapi_get_drive_type(long f_type)
4750 _wapi_drive_type *current;
4752 current = &_wapi_drive_types[0];
4753 while (current->drive_type != DRIVE_UNKNOWN) {
4754 if (current->fstypeid == f_type)
4755 return current->drive_type;
4759 return DRIVE_UNKNOWN;
4762 static guint32 _wapi_get_drive_type(const gchar* fstype)
4764 _wapi_drive_type *current;
4766 current = &_wapi_drive_types[0];
4767 while (current->drive_type != DRIVE_UNKNOWN) {
4768 if (strcmp (current->fstype, fstype) == 0)
4774 return current->drive_type;
4778 #if defined (PLATFORM_MACOSX) || defined (__linux__)
4780 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4786 res = statfs (utf8_root_path_name, &buf);
4789 return DRIVE_UNKNOWN;
4791 return _wapi_get_drive_type (buf.f_fstypename);
4793 return _wapi_get_drive_type (buf.f_type);
4798 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4806 fp = fopen ("/etc/mtab", "rt");
4810 fp = fopen ("/etc/mnttab", "rt");
4813 return(DRIVE_UNKNOWN);
4816 drive_type = DRIVE_NO_ROOT_DIR;
4820 fgets_res = fgets (buffer, 512, fp);
4822 if (fgets_res == NULL)
4824 splitted = g_strsplit (buffer, " ", 0);
4825 if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
4826 g_strfreev (splitted);
4830 /* compare given root_path_name with the one from mtab,
4831 if length of utf8_root_path_name is zero it must be the root dir */
4832 if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
4833 (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
4834 drive_type = _wapi_get_drive_type (*(splitted + 2));
4835 /* it is possible this path might be mounted again with
4836 a known type...keep looking */
4837 if (drive_type != DRIVE_UNKNOWN) {
4838 g_strfreev (splitted);
4843 g_strfreev (splitted);
4854 mono_w32file_get_drive_type(const gunichar2 *root_path_name)
4856 gchar *utf8_root_path_name;
4859 if (root_path_name == NULL) {
4860 utf8_root_path_name = g_strdup (g_get_current_dir());
4861 if (utf8_root_path_name == NULL) {
4862 return(DRIVE_NO_ROOT_DIR);
4866 utf8_root_path_name = mono_unicode_to_external (root_path_name);
4867 if (utf8_root_path_name == NULL) {
4868 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
4869 return(DRIVE_NO_ROOT_DIR);
4872 /* strip trailing slash for compare below */
4873 if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) {
4874 utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
4877 drive_type = GetDriveTypeFromPath (utf8_root_path_name);
4878 g_free (utf8_root_path_name);
4880 return (drive_type);
4883 #if defined (PLATFORM_MACOSX) || defined (__linux__) || defined(PLATFORM_BSD) || defined(__FreeBSD_kernel__) || defined(__HAIKU__)
4885 get_fstypename (gchar *utfpath)
4887 #if defined (PLATFORM_MACOSX) || defined (__linux__)
4890 _wapi_drive_type *current;
4894 statfs_res = statfs (utfpath, &stat);
4896 if (statfs_res == -1)
4899 return g_strdup (stat.f_fstypename);
4901 current = &_wapi_drive_types[0];
4902 while (current->drive_type != DRIVE_UNKNOWN) {
4903 if (stat.f_type == current->fstypeid)
4904 return g_strdup (current->fstype);
4914 /* Linux has struct statfs which has a different layout */
4916 mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize)
4920 gboolean status = FALSE;
4923 // We only support getting the file system type
4924 if (fsbuffer == NULL)
4927 utfpath = mono_unicode_to_external (path);
4928 if ((fstypename = get_fstypename (utfpath)) != NULL){
4929 gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL);
4930 if (ret != NULL && len < fsbuffersize){
4931 memcpy (fsbuffer, ret, len * sizeof (gunichar2));
4937 g_free (fstypename);
4945 LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4947 MonoW32HandleFile *file_handle;
4948 off_t offset, length;
4950 if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, (gpointer *)&file_handle)) {
4951 g_warning ("%s: error looking up file handle %p", __func__, handle);
4952 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4956 if (!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) {
4957 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);
4958 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4962 #ifdef HAVE_LARGE_FILE_SUPPORT
4963 offset = ((gint64)offset_high << 32) | offset_low;
4964 length = ((gint64)length_high << 32) | length_low;
4966 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %lld, length %lld", __func__, handle, offset, length);
4968 if (offset_high > 0 || length_high > 0) {
4969 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
4972 offset = offset_low;
4973 length = length_low;
4975 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %ld, length %ld", __func__, handle, offset, length);
4978 return _wapi_lock_file_region (GPOINTER_TO_UINT(handle), offset, length);
4982 UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4984 MonoW32HandleFile *file_handle;
4985 off_t offset, length;
4987 if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, (gpointer *)&file_handle)) {
4988 g_warning ("%s: error looking up file handle %p", __func__, handle);
4989 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4993 if (!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) {
4994 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);
4995 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4999 #ifdef HAVE_LARGE_FILE_SUPPORT
5000 offset = ((gint64)offset_high << 32) | offset_low;
5001 length = ((gint64)length_high << 32) | length_low;
5003 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %lld, length %lld", __func__, handle, offset, length);
5005 offset = offset_low;
5006 length = length_low;
5008 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %ld, length %ld", __func__, handle, offset, length);
5011 return _wapi_unlock_file_region (GPOINTER_TO_UINT(handle), offset, length);
5015 mono_w32file_init (void)
5017 mono_coop_mutex_init (&stdhandle_mutex);
5018 mono_coop_mutex_init (&file_share_mutex);
5020 mono_w32handle_register_ops (MONO_W32HANDLE_FILE, &_wapi_file_ops);
5021 mono_w32handle_register_ops (MONO_W32HANDLE_CONSOLE, &_wapi_console_ops);
5022 mono_w32handle_register_ops (MONO_W32HANDLE_FIND, &_wapi_find_ops);
5023 mono_w32handle_register_ops (MONO_W32HANDLE_PIPE, &_wapi_pipe_ops);
5025 /* mono_w32handle_register_capabilities (MONO_W32HANDLE_FILE, */
5026 /* MONO_W32HANDLE_CAP_WAIT); */
5027 /* mono_w32handle_register_capabilities (MONO_W32HANDLE_CONSOLE, */
5028 /* MONO_W32HANDLE_CAP_WAIT); */
5030 if (g_hasenv ("MONO_STRICT_IO_EMULATION"))
5031 lock_while_writing = TRUE;
5035 mono_w32file_cleanup (void)
5037 mono_coop_mutex_destroy (&file_share_mutex);
5039 if (file_share_table)
5040 g_hash_table_destroy (file_share_table);
5044 mono_w32file_move (gunichar2 *path, gunichar2 *dest, gint32 *error)
5048 result = MoveFile (path, dest);
5050 *error = mono_w32error_get_last ();
5055 mono_w32file_copy (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error)
5059 result = CopyFile (path, dest, !overwrite);
5061 *error = mono_w32error_get_last ();
5067 mono_w32file_replace (gunichar2 *destinationFileName, gunichar2 *sourceFileName, gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error)
5071 result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL);
5073 *error = mono_w32error_get_last ();
5078 mono_w32file_get_file_size (gpointer handle, gint32 *error)
5083 length = GetFileSize (handle, &length_hi);
5084 if(length==INVALID_FILE_SIZE) {
5085 *error=mono_w32error_get_last ();
5088 return length | ((gint64)length_hi << 32);
5092 mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error)
5096 result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
5098 *error = mono_w32error_get_last ();
5103 mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error)
5107 result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
5109 *error = mono_w32error_get_last ();
5114 mono_w32file_get_console_input (void)
5116 return mono_w32file_get_std_handle (STD_INPUT_HANDLE);
5120 mono_w32file_get_console_output (void)
5122 return mono_w32file_get_std_handle (STD_OUTPUT_HANDLE);
5126 mono_w32file_get_console_error (void)
5128 return mono_w32file_get_std_handle (STD_ERROR_HANDLE);