Fix infrequent hangs in test-runner. (#16793)
[mono-project.git] / mono / metadata / w32file-unix.c
blob33f66a5c99aa2df3b410ee31b50d825d51e1b4b7
1 /**
2 * \file
3 */
5 #include <config.h>
6 #include <glib.h>
8 #include <stdlib.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #ifdef HAVE_SYS_STATVFS_H
15 #include <sys/statvfs.h>
16 #endif
17 #if defined(HAVE_SYS_STATFS_H)
18 #include <sys/statfs.h>
19 #endif
20 #if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
21 #include <sys/param.h>
22 #include <sys/mount.h>
23 #endif
24 #include <sys/types.h>
25 #include <stdio.h>
26 #ifdef HAVE_UTIME_H
27 #include <utime.h>
28 #endif
29 #ifdef __linux__
30 #include <sys/ioctl.h>
31 #include <linux/fs.h>
32 #include <mono/utils/linux_magic.h>
33 #endif
34 #ifdef _AIX
35 #include <sys/mntctl.h>
36 #include <sys/vmount.h>
37 #endif
38 #include <sys/time.h>
39 #ifdef HAVE_DIRENT_H
40 # include <dirent.h>
41 #endif
42 #if HOST_DARWIN
43 #include <dlfcn.h>
44 #endif
46 #include "w32file.h"
47 #include "w32file-internals.h"
48 #include "w32file-unix-glob.h"
49 #include "w32error.h"
50 #include "fdhandle.h"
51 #include "utils/mono-error-internals.h"
52 #include "utils/mono-io-portability.h"
53 #include "utils/mono-logger-internals.h"
54 #include "utils/mono-os-mutex.h"
55 #include "utils/mono-threads.h"
56 #include "utils/mono-threads-api.h"
57 #include "utils/strenc-internals.h"
58 #include "utils/strenc.h"
59 #include "utils/refcount.h"
60 #include "icall-decl.h"
61 #include "utils/mono-errno.h"
63 #define NANOSECONDS_PER_MICROSECOND 1000LL
64 #define TICKS_PER_MICROSECOND 10L
65 #define TICKS_PER_MILLISECOND 10000L
66 #define TICKS_PER_SECOND 10000000LL
67 #define TICKS_PER_MINUTE 600000000LL
68 #define TICKS_PER_HOUR 36000000000LL
69 #define TICKS_PER_DAY 864000000000LL
71 // Constants to convert Unix times to the API expected by .NET and Windows
72 #define CONVERT_BASE 116444736000000000ULL
74 #define INVALID_HANDLE_VALUE ((gpointer)-1)
76 typedef struct {
77 guint64 device;
78 guint64 inode;
79 guint32 sharemode;
80 guint32 access;
81 guint32 handle_refs;
82 guint32 timestamp;
83 } FileShare;
85 /* Currently used for both FILE, CONSOLE and PIPE handle types.
86 * This may have to change in future. */
87 typedef struct {
88 MonoFDHandle fdhandle;
89 gchar *filename;
90 FileShare *share_info; /* Pointer into shared mem */
91 guint32 security_attributes;
92 guint32 fileaccess;
93 guint32 sharemode;
94 guint32 attrs;
95 } FileHandle;
97 typedef struct {
98 MonoRefCount ref;
99 MonoCoopMutex mutex;
100 gchar **namelist;
101 gchar *dir_part;
102 gint num;
103 gsize count;
104 } FindHandle;
107 * If SHM is disabled, this will point to a hash of FileShare structures, otherwise
108 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
109 * 4MB array.
111 static GHashTable *file_share_table;
112 static MonoCoopMutex file_share_mutex;
114 static GHashTable *finds;
115 static MonoCoopMutex finds_mutex;
117 #if HOST_DARWIN
118 typedef int (*clonefile_fn) (const char *from, const char *to, int flags);
119 static MonoDl *libc_handle;
120 static clonefile_fn clonefile_ptr;
121 #endif
123 static void
124 time_t_to_filetime (time_t timeval, FILETIME *filetime)
126 guint64 ticks;
128 ticks = ((guint64)timeval * 10000000) + CONVERT_BASE;
129 filetime->dwLowDateTime = ticks & 0xFFFFFFFF;
130 filetime->dwHighDateTime = ticks >> 32;
133 static FileHandle*
134 file_data_create (MonoFDType type, gint fd)
136 FileHandle *filehandle;
138 filehandle = g_new0 (FileHandle, 1);
139 mono_fdhandle_init ((MonoFDHandle*) filehandle, type, fd);
141 return filehandle;
144 static gint
145 _wapi_unlink (const gchar *pathname);
147 static void
148 file_share_release (FileShare *share_info);
150 static void
151 file_data_close (MonoFDHandle *fdhandle)
153 FileHandle* filehandle;
155 filehandle = (FileHandle*) fdhandle;
156 g_assert (filehandle);
158 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: closing fd %d", __func__, ((MonoFDHandle*) filehandle)->fd);
160 if (((MonoFDHandle*) filehandle)->type == MONO_FDTYPE_FILE && (filehandle->attrs & FILE_FLAG_DELETE_ON_CLOSE)) {
161 _wapi_unlink (filehandle->filename);
164 if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_CONSOLE || ((MonoFDHandle*) filehandle)->fd > 2) {
165 if (filehandle->share_info) {
166 file_share_release (filehandle->share_info);
167 filehandle->share_info = NULL;
170 MONO_ENTER_GC_SAFE;
171 close (((MonoFDHandle*) filehandle)->fd);
172 MONO_EXIT_GC_SAFE;
176 static void
177 file_data_destroy (MonoFDHandle *fdhandle)
179 FileHandle *filehandle;
181 filehandle = (FileHandle*) fdhandle;
182 g_assert (filehandle);
184 if (filehandle->filename)
185 g_free (filehandle->filename);
187 g_free (filehandle);
190 static void
191 file_share_release (FileShare *share_info)
193 /* Prevent new entries racing with us */
194 mono_coop_mutex_lock (&file_share_mutex);
196 g_assert (share_info->handle_refs > 0);
197 share_info->handle_refs -= 1;
199 if (share_info->handle_refs == 0) {
200 g_hash_table_remove (file_share_table, share_info);
201 // g_free (share_info);
204 mono_coop_mutex_unlock (&file_share_mutex);
207 static gint
208 file_share_equal (gconstpointer ka, gconstpointer kb)
210 const FileShare *s1 = (const FileShare *)ka;
211 const FileShare *s2 = (const FileShare *)kb;
213 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
216 static guint
217 file_share_hash (gconstpointer data)
219 const FileShare *s = (const FileShare *)data;
221 return s->inode;
224 static gboolean
225 file_share_get (guint64 device, guint64 inode, guint32 new_sharemode, guint32 new_access,
226 guint32 *old_sharemode, guint32 *old_access, FileShare **share_info)
228 FileShare *file_share;
229 gboolean exists = FALSE;
231 /* Prevent new entries racing with us */
232 mono_coop_mutex_lock (&file_share_mutex);
234 FileShare tmp;
237 * Instead of allocating a 4MB array, we use a hash table to keep track of this
238 * info. This is needed even if SHM is disabled, to track sharing inside
239 * the current process.
241 if (!file_share_table)
242 file_share_table = g_hash_table_new_full (file_share_hash, file_share_equal, NULL, g_free);
244 tmp.device = device;
245 tmp.inode = inode;
247 file_share = (FileShare *)g_hash_table_lookup (file_share_table, &tmp);
248 if (file_share) {
249 *old_sharemode = file_share->sharemode;
250 *old_access = file_share->access;
251 *share_info = file_share;
253 g_assert (file_share->handle_refs > 0);
254 file_share->handle_refs += 1;
256 exists = TRUE;
257 } else {
258 file_share = g_new0 (FileShare, 1);
260 file_share->device = device;
261 file_share->inode = inode;
262 file_share->sharemode = new_sharemode;
263 file_share->access = new_access;
264 file_share->handle_refs = 1;
265 *share_info = file_share;
267 g_hash_table_insert (file_share_table, file_share, file_share);
270 mono_coop_mutex_unlock (&file_share_mutex);
272 return(exists);
275 static gint
276 _wapi_open (const gchar *pathname, gint flags, mode_t mode)
278 gint fd;
279 gchar *located_filename;
281 if (flags & O_CREAT) {
282 located_filename = mono_portability_find_file (pathname, FALSE);
283 if (located_filename == NULL) {
284 MONO_ENTER_GC_SAFE;
285 fd = open (pathname, flags, mode);
286 MONO_EXIT_GC_SAFE;
287 } else {
288 MONO_ENTER_GC_SAFE;
289 fd = open (located_filename, flags, mode);
290 MONO_EXIT_GC_SAFE;
291 g_free (located_filename);
293 } else {
294 MONO_ENTER_GC_SAFE;
295 fd = open (pathname, flags, mode);
296 MONO_EXIT_GC_SAFE;
297 if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
298 gint saved_errno = errno;
299 located_filename = mono_portability_find_file (pathname, TRUE);
301 if (located_filename == NULL) {
302 mono_set_errno (saved_errno);
303 return -1;
306 MONO_ENTER_GC_SAFE;
307 fd = open (located_filename, flags, mode);
308 MONO_EXIT_GC_SAFE;
309 g_free (located_filename);
313 return(fd);
316 static gint
317 _wapi_access (const gchar *pathname, gint mode)
319 gint ret;
321 MONO_ENTER_GC_SAFE;
322 ret = access (pathname, mode);
323 MONO_EXIT_GC_SAFE;
324 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
325 gint saved_errno = errno;
326 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
328 if (located_filename == NULL) {
329 mono_set_errno (saved_errno);
330 return -1;
333 MONO_ENTER_GC_SAFE;
334 ret = access (located_filename, mode);
335 MONO_EXIT_GC_SAFE;
336 g_free (located_filename);
339 return ret;
342 static gint
343 _wapi_chmod (const gchar *pathname, mode_t mode)
345 gint ret;
347 MONO_ENTER_GC_SAFE;
348 #if defined(HAVE_CHMOD)
349 ret = chmod (pathname, mode);
350 #else
351 ret = -1;
352 #endif
353 MONO_EXIT_GC_SAFE;
354 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
355 gint saved_errno = errno;
356 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
358 if (located_filename == NULL) {
359 mono_set_errno (saved_errno);
360 return -1;
363 #if defined(HAVE_CHMOD)
364 MONO_ENTER_GC_SAFE;
365 ret = chmod (located_filename, mode);
366 MONO_EXIT_GC_SAFE;
367 #else
368 ret = -1;
369 #endif
370 g_free (located_filename);
373 return ret;
376 #ifndef HAVE_STRUCT_TIMEVAL
377 static gint
378 _wapi_utime (const gchar *filename, const struct utimbuf *buf)
380 gint ret = -1;
382 #ifdef HAVE_UTIME
383 MONO_ENTER_GC_SAFE;
384 ret = utime (filename, buf);
385 MONO_EXIT_GC_SAFE;
386 if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) {
387 gint saved_errno = errno;
388 gchar *located_filename = mono_portability_find_file (filename, TRUE);
390 if (located_filename == NULL) {
391 mono_set_errno (saved_errno);
392 return -1;
395 MONO_ENTER_GC_SAFE;
396 ret = utime (located_filename, buf);
397 MONO_EXIT_GC_SAFE;
398 g_free (located_filename);
400 #endif
402 return ret;
405 #else
406 static gint
407 _wapi_utimes (const gchar *filename, const struct timeval times[2])
409 gint ret = -1;
411 #ifdef HAVE_UTIMES
412 MONO_ENTER_GC_SAFE;
413 ret = utimes (filename, times);
414 MONO_EXIT_GC_SAFE;
415 if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) {
416 gint saved_errno = errno;
417 gchar *located_filename = mono_portability_find_file (filename, TRUE);
419 if (located_filename == NULL) {
420 mono_set_errno (saved_errno);
421 return -1;
424 MONO_ENTER_GC_SAFE;
425 ret = utimes (located_filename, times);
426 MONO_EXIT_GC_SAFE;
427 g_free (located_filename);
429 #endif
431 return ret;
433 #endif
435 static gint
436 _wapi_unlink (const gchar *pathname)
438 gint ret;
440 MONO_ENTER_GC_SAFE;
441 ret = unlink (pathname);
442 MONO_EXIT_GC_SAFE;
443 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && IS_PORTABILITY_SET) {
444 gint saved_errno = errno;
445 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
447 if (located_filename == NULL) {
448 mono_set_errno (saved_errno);
449 return -1;
452 MONO_ENTER_GC_SAFE;
453 ret = unlink (located_filename);
454 MONO_EXIT_GC_SAFE;
455 g_free (located_filename);
458 return ret;
461 static gint
462 _wapi_rename (const gchar *oldpath, const gchar *newpath)
464 gint ret;
465 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
467 if (located_newpath == NULL) {
468 MONO_ENTER_GC_SAFE;
469 ret = rename (oldpath, newpath);
470 MONO_EXIT_GC_SAFE;
471 } else {
472 MONO_ENTER_GC_SAFE;
473 ret = rename (oldpath, located_newpath);
474 MONO_EXIT_GC_SAFE;
476 if (ret == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && IS_PORTABILITY_SET) {
477 gint saved_errno = errno;
478 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
480 if (located_oldpath == NULL) {
481 g_free (located_oldpath);
482 g_free (located_newpath);
484 mono_set_errno (saved_errno);
485 return -1;
488 MONO_ENTER_GC_SAFE;
489 ret = rename (located_oldpath, located_newpath);
490 MONO_EXIT_GC_SAFE;
491 g_free (located_oldpath);
493 g_free (located_newpath);
496 return ret;
499 static gint
500 _wapi_stat (const gchar *path, struct stat *buf)
502 gint ret;
504 MONO_ENTER_GC_SAFE;
505 ret = stat (path, buf);
506 MONO_EXIT_GC_SAFE;
507 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
508 gint saved_errno = errno;
509 gchar *located_filename = mono_portability_find_file (path, TRUE);
511 if (located_filename == NULL) {
512 mono_set_errno (saved_errno);
513 return -1;
516 MONO_ENTER_GC_SAFE;
517 ret = stat (located_filename, buf);
518 MONO_EXIT_GC_SAFE;
519 g_free (located_filename);
522 return ret;
525 static gint
526 _wapi_lstat (const gchar *path, struct stat *buf)
528 gint ret;
530 #ifdef HAVE_LSTAT
531 MONO_ENTER_GC_SAFE;
532 ret = lstat (path, buf);
533 MONO_EXIT_GC_SAFE;
534 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
535 gint saved_errno = errno;
536 gchar *located_filename = mono_portability_find_file (path, TRUE);
538 if (located_filename == NULL) {
539 mono_set_errno (saved_errno);
540 return -1;
543 ret = lstat (located_filename, buf);
544 g_free (located_filename);
546 #else
547 ret = -1;
548 #endif
550 return ret;
553 static gint
554 _wapi_mkdir (const gchar *pathname, mode_t mode)
556 gint ret;
557 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
559 if (located_filename == NULL) {
560 MONO_ENTER_GC_SAFE;
561 ret = mkdir (pathname, mode);
562 MONO_EXIT_GC_SAFE;
563 } else {
564 MONO_ENTER_GC_SAFE;
565 ret = mkdir (located_filename, mode);
566 MONO_EXIT_GC_SAFE;
567 g_free (located_filename);
570 return ret;
573 static gint
574 _wapi_rmdir (const gchar *pathname)
576 gint ret;
578 MONO_ENTER_GC_SAFE;
579 ret = rmdir (pathname);
580 MONO_EXIT_GC_SAFE;
581 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
582 gint saved_errno = errno;
583 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
585 if (located_filename == NULL) {
586 mono_set_errno (saved_errno);
587 return -1;
590 MONO_ENTER_GC_SAFE;
591 ret = rmdir (located_filename);
592 MONO_EXIT_GC_SAFE;
593 g_free (located_filename);
596 return ret;
599 static gint
600 _wapi_chdir (const gchar *path)
602 gint ret;
604 MONO_ENTER_GC_SAFE;
605 ret = chdir (path);
606 MONO_EXIT_GC_SAFE;
607 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
608 gint saved_errno = errno;
609 gchar *located_filename = mono_portability_find_file (path, TRUE);
611 if (located_filename == NULL) {
612 mono_set_errno (saved_errno);
613 return -1;
616 MONO_ENTER_GC_SAFE;
617 ret = chdir (located_filename);
618 MONO_EXIT_GC_SAFE;
619 g_free (located_filename);
622 return ret;
625 static gchar*
626 _wapi_basename (const gchar *filename)
628 gchar *new_filename = g_strdup (filename), *ret;
630 if (IS_PORTABILITY_SET)
631 g_strdelimit (new_filename, '\\', '/');
633 if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
634 gint len = strlen (new_filename);
636 g_memmove (new_filename, new_filename + 2, len - 2);
637 new_filename[len - 2] = '\0';
640 ret = g_path_get_basename (new_filename);
641 g_free (new_filename);
643 return ret;
646 static gchar*
647 _wapi_dirname (const gchar *filename)
649 gchar *new_filename = g_strdup (filename), *ret;
651 if (IS_PORTABILITY_SET)
652 g_strdelimit (new_filename, '\\', '/');
654 if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
655 gint len = strlen (new_filename);
657 g_memmove (new_filename, new_filename + 2, len - 2);
658 new_filename[len - 2] = '\0';
661 ret = g_path_get_dirname (new_filename);
662 g_free (new_filename);
664 return ret;
667 static GDir*
668 _wapi_g_dir_open (const gchar *path, guint flags, GError **gerror)
670 GDir *ret;
672 MONO_ENTER_GC_SAFE;
673 ret = g_dir_open (path, flags, gerror);
674 MONO_EXIT_GC_SAFE;
675 if (ret == NULL && ((*gerror)->code == G_FILE_ERROR_NOENT || (*gerror)->code == G_FILE_ERROR_NOTDIR || (*gerror)->code == G_FILE_ERROR_NAMETOOLONG) && IS_PORTABILITY_SET) {
676 gchar *located_filename = mono_portability_find_file (path, TRUE);
677 GError *tmp_error = NULL;
679 if (located_filename == NULL) {
680 return(NULL);
683 MONO_ENTER_GC_SAFE;
684 ret = g_dir_open (located_filename, flags, &tmp_error);
685 MONO_EXIT_GC_SAFE;
686 g_free (located_filename);
687 if (tmp_error == NULL) {
688 g_clear_error (gerror);
692 return ret;
695 static gint
696 get_errno_from_g_file_error (gint error)
698 switch (error) {
699 #ifdef EACCES
700 case G_FILE_ERROR_ACCES: return EACCES;
701 #endif
702 #ifdef ENAMETOOLONG
703 case G_FILE_ERROR_NAMETOOLONG: return ENAMETOOLONG;
704 #endif
705 #ifdef ENOENT
706 case G_FILE_ERROR_NOENT: return ENOENT;
707 #endif
708 #ifdef ENOTDIR
709 case G_FILE_ERROR_NOTDIR: return ENOTDIR;
710 #endif
711 #ifdef ENXIO
712 case G_FILE_ERROR_NXIO: return ENXIO;
713 #endif
714 #ifdef ENODEV
715 case G_FILE_ERROR_NODEV: return ENODEV;
716 #endif
717 #ifdef EROFS
718 case G_FILE_ERROR_ROFS: return EROFS;
719 #endif
720 #ifdef ETXTBSY
721 case G_FILE_ERROR_TXTBSY: return ETXTBSY;
722 #endif
723 #ifdef EFAULT
724 case G_FILE_ERROR_FAULT: return EFAULT;
725 #endif
726 #ifdef ELOOP
727 case G_FILE_ERROR_LOOP: return ELOOP;
728 #endif
729 #ifdef ENOSPC
730 case G_FILE_ERROR_NOSPC: return ENOSPC;
731 #endif
732 #ifdef ENOMEM
733 case G_FILE_ERROR_NOMEM: return ENOMEM;
734 #endif
735 #ifdef EMFILE
736 case G_FILE_ERROR_MFILE: return EMFILE;
737 #endif
738 #ifdef ENFILE
739 case G_FILE_ERROR_NFILE: return ENFILE;
740 #endif
741 #ifdef EBADF
742 case G_FILE_ERROR_BADF: return EBADF;
743 #endif
744 #ifdef EINVAL
745 case G_FILE_ERROR_INVAL: return EINVAL;
746 #endif
747 #ifdef EPIPE
748 case G_FILE_ERROR_PIPE: return EPIPE;
749 #endif
750 #ifdef EAGAIN
751 case G_FILE_ERROR_AGAIN: return EAGAIN;
752 #endif
753 #ifdef EINTR
754 case G_FILE_ERROR_INTR: return EINTR;
755 #endif
756 #ifdef EIO
757 case G_FILE_ERROR_IO: return EIO;
758 #endif
759 #ifdef EPERM
760 case G_FILE_ERROR_PERM: return EPERM;
761 #endif
762 case G_FILE_ERROR_FAILED: return ERROR_INVALID_PARAMETER;
763 default:
764 g_assert_not_reached ();
768 static gint
769 file_compare (gconstpointer a, gconstpointer b)
771 gchar *astr = *(gchar **) a;
772 gchar *bstr = *(gchar **) b;
774 return strcmp (astr, bstr);
777 /* scandir using glib */
778 static gint
779 _wapi_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
781 GError *gerror = NULL;
782 GDir *dir;
783 GPtrArray *names;
784 gint result;
785 mono_w32file_unix_glob_t glob_buf;
786 gint flags = 0, i;
788 dir = _wapi_g_dir_open (dirname, 0, &gerror);
789 if (dir == NULL) {
790 /* g_dir_open returns ENOENT on directories on which we don't
791 * have read/x permission */
792 gint errnum = get_errno_from_g_file_error (gerror->code);
793 g_error_free (gerror);
794 if (errnum == ENOENT &&
795 !_wapi_access (dirname, F_OK) &&
796 _wapi_access (dirname, R_OK|X_OK)) {
797 errnum = EACCES;
800 mono_set_errno (errnum);
801 return -1;
804 if (IS_PORTABILITY_CASE) {
805 flags = W32FILE_UNIX_GLOB_IGNORECASE;
808 result = mono_w32file_unix_glob (dir, pattern, flags, &glob_buf);
809 if (g_str_has_suffix (pattern, ".*")) {
810 /* Special-case the patterns ending in '.*', as
811 * windows also matches entries with no extension with
812 * this pattern.
814 * TODO: should this be a MONO_IOMAP option?
816 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
817 gint result2;
819 MONO_ENTER_GC_SAFE;
820 g_dir_rewind (dir);
821 MONO_EXIT_GC_SAFE;
822 result2 = mono_w32file_unix_glob (dir, pattern2, flags | W32FILE_UNIX_GLOB_APPEND | W32FILE_UNIX_GLOB_UNIQUE, &glob_buf);
824 g_free (pattern2);
826 if (result != 0) {
827 result = result2;
831 MONO_ENTER_GC_SAFE;
832 g_dir_close (dir);
833 MONO_EXIT_GC_SAFE;
834 if (glob_buf.gl_pathc == 0) {
835 return(0);
836 } else if (result != 0) {
837 return -1;
840 names = g_ptr_array_new ();
841 for (i = 0; i < glob_buf.gl_pathc; i++) {
842 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
845 mono_w32file_unix_globfree (&glob_buf);
847 result = names->len;
848 if (result > 0) {
849 g_ptr_array_sort (names, file_compare);
850 g_ptr_array_set_size (names, result + 1);
852 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
853 } else {
854 g_ptr_array_free (names, TRUE);
857 return result;
860 static gboolean
861 _wapi_lock_file_region (gint fd, off_t offset, off_t length)
863 struct flock lock_data;
864 gint ret;
866 if (offset < 0 || length < 0) {
867 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
868 return FALSE;
871 lock_data.l_type = F_WRLCK;
872 lock_data.l_whence = SEEK_SET;
873 lock_data.l_start = offset;
874 lock_data.l_len = length;
876 do {
877 ret = fcntl (fd, F_SETLK, &lock_data);
878 } while(ret == -1 && errno == EINTR);
880 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl returns %d", __func__, ret);
882 if (ret == -1) {
884 * if locks are not available (NFS for example),
885 * ignore the error
887 if (errno == ENOLCK
888 #ifdef EOPNOTSUPP
889 || errno == EOPNOTSUPP
890 #endif
891 #ifdef ENOTSUP
892 || errno == ENOTSUP
893 #endif
895 return TRUE;
898 mono_w32error_set_last (ERROR_LOCK_VIOLATION);
899 return FALSE;
902 return TRUE;
905 static gboolean
906 _wapi_unlock_file_region (gint fd, off_t offset, off_t length)
908 struct flock lock_data;
909 gint ret;
911 lock_data.l_type = F_UNLCK;
912 lock_data.l_whence = SEEK_SET;
913 lock_data.l_start = offset;
914 lock_data.l_len = length;
916 do {
917 ret = fcntl (fd, F_SETLK, &lock_data);
918 } while(ret == -1 && errno == EINTR);
920 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl returns %d", __func__, ret);
922 if (ret == -1) {
924 * if locks are not available (NFS for example),
925 * ignore the error
927 if (errno == ENOLCK
928 #ifdef EOPNOTSUPP
929 || errno == EOPNOTSUPP
930 #endif
931 #ifdef ENOTSUP
932 || errno == ENOTSUP
933 #endif
935 return TRUE;
938 mono_w32error_set_last (ERROR_LOCK_VIOLATION);
939 return FALSE;
942 return TRUE;
945 static gboolean lock_while_writing = FALSE;
947 /* Some utility functions.
951 * Check if a file is writable by the current user.
953 * This is is a best effort kind of thing. It assumes a reasonable sane set
954 * of permissions by the underlying OS.
956 * We generally assume that basic unix permission bits are authoritative. Which might not
957 * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc)
959 * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file.
961 * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent
962 * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous
963 * and should not be used with a dynamic runtime.
965 static gboolean
966 is_file_writable (struct stat *st, const gchar *path)
968 gboolean ret;
969 gchar *located_path;
971 #if __APPLE__
972 // OS X Finder "locked" or `ls -lO` "uchg".
973 // This only covers one of several cases where an OS X file could be unwritable through special flags.
974 if (st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE))
975 return 0;
976 #endif
978 /* Is it globally writable? */
979 if (st->st_mode & S_IWOTH)
980 return 1;
982 /* Am I the owner? */
983 if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR))
984 return 1;
986 /* Am I in the same group? */
987 if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP))
988 return 1;
990 located_path = mono_portability_find_file (path, FALSE);
992 /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid
993 * but it's the only sane option we have on unix.
995 MONO_ENTER_GC_SAFE;
996 ret = access (located_path != NULL ? located_path : path, W_OK) == 0;
997 MONO_EXIT_GC_SAFE;
999 g_free (located_path);
1001 return ret;
1005 static guint32 _wapi_stat_to_file_attributes (const gchar *pathname,
1006 struct stat *buf,
1007 struct stat *lbuf)
1009 guint32 attrs = 0;
1010 gchar *filename;
1012 /* FIXME: this could definitely be better, but there seems to
1013 * be no pattern to the attributes that are set
1016 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
1017 if (S_ISSOCK (buf->st_mode))
1018 buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */
1020 filename = _wapi_basename (pathname);
1022 if (S_ISDIR (buf->st_mode)) {
1023 attrs = FILE_ATTRIBUTE_DIRECTORY;
1024 if (!is_file_writable (buf, pathname)) {
1025 attrs |= FILE_ATTRIBUTE_READONLY;
1027 if (filename[0] == '.') {
1028 attrs |= FILE_ATTRIBUTE_HIDDEN;
1030 } else {
1031 if (!is_file_writable (buf, pathname)) {
1032 attrs = FILE_ATTRIBUTE_READONLY;
1034 if (filename[0] == '.') {
1035 attrs |= FILE_ATTRIBUTE_HIDDEN;
1037 } else if (filename[0] == '.') {
1038 attrs = FILE_ATTRIBUTE_HIDDEN;
1039 } else {
1040 attrs = FILE_ATTRIBUTE_NORMAL;
1044 if (lbuf != NULL) {
1045 if (S_ISLNK (lbuf->st_mode)) {
1046 attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
1050 g_free (filename);
1052 return attrs;
1055 static void
1056 _wapi_set_last_error_from_errno (void)
1058 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
1061 static void _wapi_set_last_path_error_from_errno (const gchar *dir,
1062 const gchar *path)
1064 if (errno == ENOENT) {
1065 /* Check the path - if it's a missing directory then
1066 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
1068 gchar *dirname;
1071 if (dir == NULL) {
1072 dirname = _wapi_dirname (path);
1073 } else {
1074 dirname = g_strdup (dir);
1077 if (_wapi_access (dirname, F_OK) == 0) {
1078 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
1079 } else {
1080 mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
1083 g_free (dirname);
1084 } else {
1085 _wapi_set_last_error_from_errno ();
1089 static gboolean
1090 file_read(FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1092 gint ret;
1093 MonoThreadInfo *info = mono_thread_info_current ();
1095 if(bytesread!=NULL) {
1096 *bytesread=0;
1099 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) {
1100 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1102 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1103 return(FALSE);
1106 do {
1107 MONO_ENTER_GC_SAFE;
1108 ret = read (((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1109 MONO_EXIT_GC_SAFE;
1110 } while (ret == -1 && errno == EINTR &&
1111 !mono_thread_info_is_interrupt_state (info));
1113 if(ret==-1) {
1114 gint err = errno;
1116 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(err));
1117 mono_w32error_set_last (mono_w32error_unix_to_win32 (err));
1118 return(FALSE);
1121 if (bytesread != NULL) {
1122 *bytesread = ret;
1125 return(TRUE);
1128 static gboolean
1129 file_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *byteswritten)
1131 gint ret;
1132 off_t current_pos = 0;
1133 MonoThreadInfo *info = mono_thread_info_current ();
1135 if(byteswritten!=NULL) {
1136 *byteswritten=0;
1139 if(!(filehandle->fileaccess & GENERIC_WRITE) && !(filehandle->fileaccess & GENERIC_ALL)) {
1140 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1142 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1143 return(FALSE);
1146 if (lock_while_writing) {
1147 /* Need to lock the region we're about to write to,
1148 * because we only do advisory locking on POSIX
1149 * systems
1151 MONO_ENTER_GC_SAFE;
1152 current_pos = lseek (((MonoFDHandle*) filehandle)->fd, (off_t)0, SEEK_CUR);
1153 MONO_EXIT_GC_SAFE;
1154 if (current_pos == -1) {
1155 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror (errno));
1156 _wapi_set_last_error_from_errno ();
1157 return(FALSE);
1160 if (_wapi_lock_file_region (((MonoFDHandle*) filehandle)->fd, current_pos, numbytes) == FALSE) {
1161 /* The error has already been set */
1162 return(FALSE);
1166 do {
1167 MONO_ENTER_GC_SAFE;
1168 ret = write (((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1169 MONO_EXIT_GC_SAFE;
1170 } while (ret == -1 && errno == EINTR &&
1171 !mono_thread_info_is_interrupt_state (info));
1173 if (lock_while_writing) {
1174 _wapi_unlock_file_region (((MonoFDHandle*) filehandle)->fd, current_pos, numbytes);
1177 if (ret == -1) {
1178 if (errno == EINTR) {
1179 ret = 0;
1180 } else {
1181 _wapi_set_last_error_from_errno ();
1183 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1185 return(FALSE);
1188 if (byteswritten != NULL) {
1189 *byteswritten = ret;
1191 return(TRUE);
1194 static gboolean file_flush(FileHandle *filehandle)
1196 gint ret;
1198 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1199 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1201 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1202 return(FALSE);
1205 MONO_ENTER_GC_SAFE;
1206 ret=fsync(((MonoFDHandle*) filehandle)->fd);
1207 MONO_EXIT_GC_SAFE;
1208 if (ret==-1) {
1209 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fsync of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1211 _wapi_set_last_error_from_errno ();
1212 return(FALSE);
1215 return(TRUE);
1218 static guint32 file_seek(FileHandle *filehandle, gint32 movedistance,
1219 gint32 *highmovedistance, gint method)
1221 gint64 offset, newpos;
1222 gint whence;
1223 guint32 ret;
1225 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
1226 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1228 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1229 return(INVALID_SET_FILE_POINTER);
1232 switch(method) {
1233 case FILE_BEGIN:
1234 whence=SEEK_SET;
1235 break;
1236 case FILE_CURRENT:
1237 whence=SEEK_CUR;
1238 break;
1239 case FILE_END:
1240 whence=SEEK_END;
1241 break;
1242 default:
1243 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: invalid seek type %d", __func__, method);
1245 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1246 return(INVALID_SET_FILE_POINTER);
1249 #ifdef HAVE_LARGE_FILE_SUPPORT
1250 if(highmovedistance==NULL) {
1251 offset=movedistance;
1252 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: setting offset to %" G_GINT64_FORMAT " (low %" G_GINT32_FORMAT ")", __func__,
1253 offset, movedistance);
1254 } else {
1255 offset=((gint64) *highmovedistance << 32) | (guint32)movedistance;
1257 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: setting offset to %" G_GINT64_FORMAT " 0x%" PRIx64 " (high %" G_GINT32_FORMAT " 0x%" PRIx32 ", low %" G_GINT32_FORMAT " 0x%" PRIx32 ")",
1258 __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
1260 #else
1261 offset=movedistance;
1262 #endif
1264 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: moving fd %d by %" G_GINT64_FORMAT " bytes from %d", __func__, ((MonoFDHandle*) filehandle)->fd, offset, whence);
1266 #ifdef HOST_ANDROID
1267 /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */
1268 MONO_ENTER_GC_SAFE;
1269 newpos=lseek64(((MonoFDHandle*) filehandle)->fd, offset, whence);
1270 MONO_EXIT_GC_SAFE;
1271 #else
1272 MONO_ENTER_GC_SAFE;
1273 newpos=lseek(((MonoFDHandle*) filehandle)->fd, offset, whence);
1274 MONO_EXIT_GC_SAFE;
1275 #endif
1276 if(newpos==-1) {
1277 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lseek on fd %d returned error %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1279 _wapi_set_last_error_from_errno ();
1280 return(INVALID_SET_FILE_POINTER);
1283 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lseek returns %" G_GINT64_FORMAT, __func__, newpos);
1285 #ifdef HAVE_LARGE_FILE_SUPPORT
1286 ret=newpos & 0xFFFFFFFF;
1287 if(highmovedistance!=NULL) {
1288 *highmovedistance=newpos>>32;
1290 #else
1291 ret=newpos;
1292 if(highmovedistance!=NULL) {
1293 /* Accurate, but potentially dodgy :-) */
1294 *highmovedistance=0;
1296 #endif
1298 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: move of fd %d returning %" G_GUINT32_FORMAT "/%" G_GINT32_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, ret, highmovedistance==NULL?0:*highmovedistance);
1300 return(ret);
1303 static gboolean file_setendoffile(FileHandle *filehandle)
1305 struct stat statbuf;
1306 off_t pos;
1307 gint ret;
1308 MonoThreadInfo *info = mono_thread_info_current ();
1310 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1311 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1313 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1314 return(FALSE);
1317 /* Find the current file position, and the file length. If
1318 * the file position is greater than the length, write to
1319 * extend the file with a hole. If the file position is less
1320 * than the length, truncate the file.
1323 MONO_ENTER_GC_SAFE;
1324 ret=fstat(((MonoFDHandle*) filehandle)->fd, &statbuf);
1325 MONO_EXIT_GC_SAFE;
1326 if(ret==-1) {
1327 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1329 _wapi_set_last_error_from_errno ();
1330 return(FALSE);
1333 MONO_ENTER_GC_SAFE;
1334 pos=lseek(((MonoFDHandle*) filehandle)->fd, (off_t)0, SEEK_CUR);
1335 MONO_EXIT_GC_SAFE;
1336 if(pos==-1) {
1337 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1339 _wapi_set_last_error_from_errno ();
1340 return(FALSE);
1343 #ifdef FTRUNCATE_DOESNT_EXTEND
1344 off_t size = statbuf.st_size;
1345 /* I haven't bothered to write the configure.ac stuff for this
1346 * because I don't know if any platform needs it. I'm leaving
1347 * this code just in case though
1349 if(pos>size) {
1350 /* Extend the file. Use write() here, because some
1351 * manuals say that ftruncate() behaviour is undefined
1352 * when the file needs extending. The POSIX spec says
1353 * that on XSI-conformant systems it extends, so if
1354 * every system we care about conforms, then we can
1355 * drop this write.
1357 do {
1358 MONO_ENTER_GC_SAFE;
1359 ret = write (((MonoFDHandle*) filehandle)->fd, "", 1);
1360 MONO_EXIT_GC_SAFE;
1361 } while (ret == -1 && errno == EINTR &&
1362 !mono_thread_info_is_interrupt_state (info));
1364 if(ret==-1) {
1365 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d extend write failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1367 _wapi_set_last_error_from_errno ();
1368 return(FALSE);
1371 /* And put the file position back after the write */
1372 MONO_ENTER_GC_SAFE;
1373 ret = lseek (((MonoFDHandle*) filehandle)->fd, pos, SEEK_SET);
1374 MONO_EXIT_GC_SAFE;
1375 if (ret == -1) {
1376 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d second lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1378 _wapi_set_last_error_from_errno ();
1379 return(FALSE);
1382 #endif
1384 /* always truncate, because the extend write() adds an extra
1385 * byte to the end of the file
1387 do {
1388 MONO_ENTER_GC_SAFE;
1389 ret=ftruncate(((MonoFDHandle*) filehandle)->fd, pos);
1390 MONO_EXIT_GC_SAFE;
1392 while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1393 if(ret==-1) {
1394 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d ftruncate failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1396 _wapi_set_last_error_from_errno ();
1397 return(FALSE);
1400 return(TRUE);
1403 static guint32 file_getfilesize(FileHandle *filehandle, guint32 *highsize)
1405 struct stat statbuf;
1406 guint32 size;
1407 gint ret;
1409 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
1410 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1412 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1413 return(INVALID_FILE_SIZE);
1416 /* If the file has a size with the low bits 0xFFFFFFFF the
1417 * caller can't tell if this is an error, so clear the error
1418 * value
1420 mono_w32error_set_last (ERROR_SUCCESS);
1422 MONO_ENTER_GC_SAFE;
1423 ret = fstat(((MonoFDHandle*) filehandle)->fd, &statbuf);
1424 MONO_EXIT_GC_SAFE;
1425 if (ret == -1) {
1426 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1428 _wapi_set_last_error_from_errno ();
1429 return(INVALID_FILE_SIZE);
1432 /* fstat indicates block devices as zero-length, so go a different path */
1433 #ifdef BLKGETSIZE64
1434 if (S_ISBLK(statbuf.st_mode)) {
1435 guint64 bigsize;
1436 gint res;
1437 MONO_ENTER_GC_SAFE;
1438 res = ioctl (((MonoFDHandle*) filehandle)->fd, BLKGETSIZE64, &bigsize);
1439 MONO_EXIT_GC_SAFE;
1440 if (res < 0) {
1441 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d ioctl BLKGETSIZE64 failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1443 _wapi_set_last_error_from_errno ();
1444 return(INVALID_FILE_SIZE);
1447 size = bigsize & 0xFFFFFFFF;
1448 if (highsize != NULL) {
1449 *highsize = bigsize>>32;
1452 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning block device size %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT,
1453 __func__, size, *highsize);
1455 return(size);
1457 #endif
1459 #ifdef HAVE_LARGE_FILE_SUPPORT
1460 size = statbuf.st_size & 0xFFFFFFFF;
1461 if (highsize != NULL) {
1462 *highsize = statbuf.st_size>>32;
1464 #else
1465 if (highsize != NULL) {
1466 /* Accurate, but potentially dodgy :-) */
1467 *highsize = 0;
1469 size = statbuf.st_size;
1470 #endif
1472 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning size %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT, __func__, size, *highsize);
1474 return(size);
1477 static guint64
1478 convert_unix_filetime_ms (const FILETIME *file_time, const char *ttype)
1480 guint64 t = ((guint64) file_time->dwHighDateTime << 32) + file_time->dwLowDateTime;
1482 /* This is (time_t)0. We can actually go to INT_MIN,
1483 * but this will do for now.
1485 if (t < CONVERT_BASE) {
1486 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: attempt to set %s time too early", __func__, ttype);
1487 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1488 return (FALSE);
1491 return t - CONVERT_BASE;
1494 #ifndef HAVE_STRUCT_TIMEVAL
1495 static guint64
1496 convert_unix_filetime (const FILETIME *file_time, size_t field_size, const char *ttype)
1498 guint64 t = convert_unix_filetime_ms (file_time, ttype);
1500 if (field_size == 4 && (t / 10000000) > INT_MAX) {
1501 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: attempt to set %s time that is too big for a 32bits time_t", __func__, ttype);
1502 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1503 return (FALSE);
1506 return t / 10000000;
1508 #endif
1510 static void convert_stattime_access_to_timeval (struct timeval *dest, struct stat *statbuf)
1512 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
1513 dest->tv_sec = statbuf->st_atimespec.tv_sec;
1514 dest->tv_usec = statbuf->st_atimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND;
1515 #else
1516 dest->tv_sec = statbuf->st_atime;
1517 #if HAVE_STRUCT_STAT_ST_ATIM
1518 dest->tv_usec = statbuf->st_atim.tv_nsec / NANOSECONDS_PER_MICROSECOND;
1519 #endif
1520 #endif
1523 static void convert_stattime_mod_to_timeval (struct timeval *dest, struct stat *statbuf)
1525 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
1526 dest->tv_sec = statbuf->st_mtimespec.tv_sec;
1527 dest->tv_usec = statbuf->st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND;
1528 #else
1529 dest->tv_sec = statbuf->st_mtime;
1530 #if HAVE_STRUCT_STAT_ST_ATIM
1531 dest->tv_usec = statbuf->st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND;
1532 #endif
1533 #endif
1536 static gboolean file_setfiletime(FileHandle *filehandle,
1537 const FILETIME *create_time G_GNUC_UNUSED,
1538 const FILETIME *access_time,
1539 const FILETIME *write_time)
1541 struct stat statbuf;
1542 gint ret;
1544 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1545 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1547 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1548 return(FALSE);
1551 if(filehandle->filename == NULL) {
1552 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d unknown filename", __func__, ((MonoFDHandle*) filehandle)->fd);
1554 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1555 return(FALSE);
1558 /* Get the current times, so we can put the same times back in
1559 * the event that one of the FileTime structs is NULL
1561 MONO_ENTER_GC_SAFE;
1562 ret=fstat (((MonoFDHandle*) filehandle)->fd, &statbuf);
1563 MONO_EXIT_GC_SAFE;
1564 if(ret==-1) {
1565 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1567 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1568 return(FALSE);
1571 #ifdef HAVE_STRUCT_TIMEVAL
1572 struct timeval times [2];
1573 memset (times, 0, sizeof (times));
1575 if (access_time) {
1576 guint64 actime = convert_unix_filetime_ms (access_time, "access");
1577 times [0].tv_sec = actime / TICKS_PER_SECOND;
1578 times [0].tv_usec = (actime % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND;
1579 } else {
1580 convert_stattime_access_to_timeval (&times [0], &statbuf);
1583 if (write_time) {
1584 guint64 wtime = convert_unix_filetime_ms (write_time, "write");
1585 times [1].tv_sec = wtime / TICKS_PER_SECOND;
1586 times [1].tv_usec = (wtime % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND;
1587 } else {
1588 convert_stattime_mod_to_timeval (&times [1], &statbuf);
1591 ret = _wapi_utimes (filehandle->filename, times);
1592 #else
1593 struct utimbuf utbuf;
1594 utbuf.actime = access_time ? convert_unix_filetime (access_time, sizeof (utbuf.actime), "access") : statbuf.st_atime;
1595 utbuf.modtime = write_time ? convert_unix_filetime (write_time, sizeof (utbuf.modtime), "write") : statbuf.st_mtime;
1597 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: setting fd %d access %ld write %ld", __func__, ((MonoFDHandle*) filehandle)->fd, utbuf.actime, utbuf.modtime);
1599 ret = _wapi_utime (filehandle->filename, &utbuf);
1600 #endif
1601 if (ret == -1) {
1602 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d [%s] utime failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->filename, g_strerror(errno));
1604 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1605 return(FALSE);
1608 return(TRUE);
1611 static gboolean
1612 console_read(FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1614 gint ret;
1615 MonoThreadInfo *info = mono_thread_info_current ();
1617 if(bytesread!=NULL) {
1618 *bytesread=0;
1621 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) {
1622 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1624 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1625 return(FALSE);
1628 do {
1629 MONO_ENTER_GC_SAFE;
1630 ret=read(((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1631 MONO_EXIT_GC_SAFE;
1632 } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1634 if(ret==-1) {
1635 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1637 _wapi_set_last_error_from_errno ();
1638 return(FALSE);
1641 if(bytesread!=NULL) {
1642 *bytesread=ret;
1645 return(TRUE);
1648 static gboolean
1649 console_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *byteswritten)
1651 gint ret;
1652 MonoThreadInfo *info = mono_thread_info_current ();
1654 if(byteswritten!=NULL) {
1655 *byteswritten=0;
1658 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1659 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1661 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1662 return(FALSE);
1665 do {
1666 MONO_ENTER_GC_SAFE;
1667 ret = write(((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1668 MONO_EXIT_GC_SAFE;
1669 } while (ret == -1 && errno == EINTR &&
1670 !mono_thread_info_is_interrupt_state (info));
1672 if (ret == -1) {
1673 if (errno == EINTR) {
1674 ret = 0;
1675 } else {
1676 _wapi_set_last_error_from_errno ();
1678 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write of fd %d error: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1680 return(FALSE);
1683 if(byteswritten!=NULL) {
1684 *byteswritten=ret;
1687 return(TRUE);
1690 static gboolean
1691 pipe_read (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1693 gint ret;
1694 MonoThreadInfo *info = mono_thread_info_current ();
1696 if(bytesread!=NULL) {
1697 *bytesread=0;
1700 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) {
1701 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1703 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1704 return(FALSE);
1707 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: reading up to %" G_GUINT32_FORMAT " bytes from pipe %d", __func__, numbytes, ((MonoFDHandle*) filehandle)->fd);
1709 do {
1710 MONO_ENTER_GC_SAFE;
1711 ret=read(((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1712 MONO_EXIT_GC_SAFE;
1713 } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1715 if (ret == -1) {
1716 if (errno == EINTR) {
1717 ret = 0;
1718 } else {
1719 _wapi_set_last_error_from_errno ();
1721 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read of fd %d error: %s", __func__,((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1723 return(FALSE);
1727 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read %d bytes from pipe %d", __func__, ret, ((MonoFDHandle*) filehandle)->fd);
1729 if(bytesread!=NULL) {
1730 *bytesread=ret;
1733 return(TRUE);
1736 static gboolean
1737 pipe_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *byteswritten)
1739 gint ret;
1740 MonoThreadInfo *info = mono_thread_info_current ();
1742 if(byteswritten!=NULL) {
1743 *byteswritten=0;
1746 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1747 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
1749 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1750 return(FALSE);
1753 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: writing up to %" G_GUINT32_FORMAT " bytes to pipe %d", __func__, numbytes, ((MonoFDHandle*) filehandle)->fd);
1755 do {
1756 MONO_ENTER_GC_SAFE;
1757 ret = write (((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1758 MONO_EXIT_GC_SAFE;
1759 } while (ret == -1 && errno == EINTR &&
1760 !mono_thread_info_is_interrupt_state (info));
1762 if (ret == -1) {
1763 if (errno == EINTR) {
1764 ret = 0;
1765 } else {
1766 _wapi_set_last_error_from_errno ();
1768 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write of fd %d error: %s", __func__,((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1770 return(FALSE);
1773 if(byteswritten!=NULL) {
1774 *byteswritten=ret;
1777 return(TRUE);
1780 static gint convert_flags(guint32 fileaccess, guint32 createmode)
1782 gint flags=0;
1784 switch(fileaccess) {
1785 case GENERIC_READ:
1786 flags=O_RDONLY;
1787 break;
1788 case GENERIC_WRITE:
1789 flags=O_WRONLY;
1790 break;
1791 case GENERIC_READ|GENERIC_WRITE:
1792 flags=O_RDWR;
1793 break;
1794 default:
1795 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unknown access type 0x%" PRIx32, __func__,
1796 fileaccess);
1797 break;
1800 switch(createmode) {
1801 case CREATE_NEW:
1802 flags|=O_CREAT|O_EXCL;
1803 break;
1804 case CREATE_ALWAYS:
1805 flags|=O_CREAT|O_TRUNC;
1806 break;
1807 case OPEN_EXISTING:
1808 break;
1809 case OPEN_ALWAYS:
1810 flags|=O_CREAT;
1811 break;
1812 case TRUNCATE_EXISTING:
1813 flags|=O_TRUNC;
1814 break;
1815 default:
1816 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unknown create mode 0x%" PRIx32, __func__,
1817 createmode);
1818 break;
1821 return(flags);
1824 #if 0 /* unused */
1825 static mode_t convert_perms(guint32 sharemode)
1827 mode_t perms=0600;
1829 if(sharemode&FILE_SHARE_READ) {
1830 perms|=044;
1832 if(sharemode&FILE_SHARE_WRITE) {
1833 perms|=022;
1836 return(perms);
1838 #endif
1840 static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode,
1841 guint32 fileaccess,
1842 FileShare **share_info)
1844 gboolean file_already_shared;
1845 guint32 file_existing_share, file_existing_access;
1847 file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info);
1849 if (file_already_shared) {
1850 /* The reference to this share info was incremented
1851 * when we looked it up, so be careful to put it back
1852 * if we conclude we can't use this file.
1854 if ((file_existing_share == FILE_SHARE_NONE) || (sharemode == FILE_SHARE_NONE)) {
1855 /* Quick and easy, no possibility to share */
1856 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Share mode prevents open: requested access: 0x%" PRIx32 ", file has sharing = NONE", __func__, fileaccess);
1858 file_share_release (*share_info);
1859 *share_info = NULL;
1861 return(FALSE);
1864 if (((file_existing_share == FILE_SHARE_READ) &&
1865 (fileaccess != GENERIC_READ)) ||
1866 ((file_existing_share == FILE_SHARE_WRITE) &&
1867 (fileaccess != GENERIC_WRITE))) {
1868 /* New access mode doesn't match up */
1869 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Share mode prevents open: requested access: 0x%" PRIx32 ", file has sharing: 0x%" PRIx32, __func__, fileaccess, file_existing_share);
1871 file_share_release (*share_info);
1872 *share_info = NULL;
1874 return(FALSE);
1876 } else {
1877 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: New file!", __func__);
1880 return(TRUE);
1884 static gboolean
1885 share_allows_delete (struct stat *statbuf, FileShare **share_info)
1887 gboolean file_already_shared;
1888 guint32 file_existing_share, file_existing_access;
1890 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);
1892 if (file_already_shared) {
1893 /* The reference to this share info was incremented
1894 * when we looked it up, so be careful to put it back
1895 * if we conclude we can't use this file.
1897 if (file_existing_share == 0) {
1898 /* Quick and easy, no possibility to share */
1899 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Share mode prevents open: requested access: 0x%" PRIx32 ", file has sharing = NONE", __func__, (*share_info)->access);
1901 file_share_release (*share_info);
1902 *share_info = NULL;
1904 return(FALSE);
1907 if (!(file_existing_share & FILE_SHARE_DELETE)) {
1908 /* New access mode doesn't match up */
1909 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Share mode prevents open: requested access: 0x%" PRIx32 ", file has sharing: 0x%" PRIx32, __func__, (*share_info)->access, file_existing_share);
1911 file_share_release (*share_info);
1912 *share_info = NULL;
1914 return(FALSE);
1916 } else {
1917 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: New file!", __func__);
1920 return(TRUE);
1923 gpointer
1924 mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs)
1926 FileHandle *filehandle;
1927 MonoFDType type;
1928 gint flags=convert_flags(fileaccess, createmode);
1929 /*mode_t perms=convert_perms(sharemode);*/
1930 /* we don't use sharemode, because that relates to sharing of
1931 * the file when the file is open and is already handled by
1932 * other code, perms instead are the on-disk permissions and
1933 * this is a sane default.
1935 mode_t perms=0666;
1936 gchar *filename;
1937 gint fd, ret;
1938 struct stat statbuf;
1939 ERROR_DECL (error);
1941 if (attrs & FILE_ATTRIBUTE_TEMPORARY)
1942 perms = 0600;
1944 if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
1945 mono_w32error_set_last (ERROR_ENCRYPTION_FAILED);
1946 return INVALID_HANDLE_VALUE;
1949 if (name == NULL) {
1950 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
1952 mono_w32error_set_last (ERROR_INVALID_NAME);
1953 return(INVALID_HANDLE_VALUE);
1956 filename = mono_unicode_to_external_checked (name, error);
1957 if (filename == NULL) {
1958 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
1960 mono_error_cleanup (error);
1961 mono_w32error_set_last (ERROR_INVALID_NAME);
1962 return(INVALID_HANDLE_VALUE);
1965 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Opening %s with share 0x%" PRIx32 " and access 0x%" PRIx32, __func__,
1966 filename, sharemode, fileaccess);
1968 fd = _wapi_open (filename, flags, perms);
1970 /* If we were trying to open a directory with write permissions
1971 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1972 * EISDIR. However, this is a bit bogus because calls to
1973 * manipulate the directory (e.g. mono_w32file_set_times) will still work on
1974 * the directory because they use other API calls
1975 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1976 * to open the directory again without write permission.
1978 if (fd == -1 && errno == EISDIR)
1980 /* Try again but don't try to make it writable */
1981 fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
1984 if (fd == -1) {
1985 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Error opening file %s: %s", __func__, filename, g_strerror(errno));
1986 _wapi_set_last_path_error_from_errno (NULL, filename);
1987 g_free (filename);
1989 return(INVALID_HANDLE_VALUE);
1992 MONO_ENTER_GC_SAFE;
1993 ret = fstat (fd, &statbuf);
1994 MONO_EXIT_GC_SAFE;
1995 if (ret == -1) {
1996 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fstat error of file %s: %s", __func__, filename, g_strerror (errno));
1997 _wapi_set_last_error_from_errno ();
1998 MONO_ENTER_GC_SAFE;
1999 close (fd);
2000 MONO_EXIT_GC_SAFE;
2002 return(INVALID_HANDLE_VALUE);
2005 #ifndef S_ISFIFO
2006 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
2007 #endif
2008 if (S_ISFIFO (statbuf.st_mode)) {
2009 type = MONO_FDTYPE_PIPE;
2010 /* maintain invariant that pipes have no filename */
2011 g_free (filename);
2012 filename = NULL;
2013 } else if (S_ISCHR (statbuf.st_mode)) {
2014 type = MONO_FDTYPE_CONSOLE;
2015 } else {
2016 type = MONO_FDTYPE_FILE;
2019 filehandle = file_data_create (type, fd);
2020 filehandle->filename = filename;
2021 filehandle->fileaccess = fileaccess;
2022 filehandle->sharemode = sharemode;
2023 filehandle->attrs = attrs;
2025 if (!share_allows_open (&statbuf, filehandle->sharemode, filehandle->fileaccess, &filehandle->share_info)) {
2026 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2027 MONO_ENTER_GC_SAFE;
2028 close (((MonoFDHandle*) filehandle)->fd);
2029 MONO_EXIT_GC_SAFE;
2031 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2032 return (INVALID_HANDLE_VALUE);
2034 if (!filehandle->share_info) {
2035 /* No space, so no more files can be opened */
2036 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: No space in the share table", __func__);
2038 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
2039 MONO_ENTER_GC_SAFE;
2040 close (((MonoFDHandle*) filehandle)->fd);
2041 MONO_EXIT_GC_SAFE;
2043 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2044 return(INVALID_HANDLE_VALUE);
2047 #ifdef HAVE_POSIX_FADVISE
2048 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) {
2049 MONO_ENTER_GC_SAFE;
2050 posix_fadvise (((MonoFDHandle*) filehandle)->fd, 0, 0, POSIX_FADV_SEQUENTIAL);
2051 MONO_EXIT_GC_SAFE;
2053 if (attrs & FILE_FLAG_RANDOM_ACCESS) {
2054 MONO_ENTER_GC_SAFE;
2055 posix_fadvise (((MonoFDHandle*) filehandle)->fd, 0, 0, POSIX_FADV_RANDOM);
2056 MONO_EXIT_GC_SAFE;
2058 #endif
2060 #ifdef F_RDAHEAD
2061 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) {
2062 MONO_ENTER_GC_SAFE;
2063 fcntl(((MonoFDHandle*) filehandle)->fd, F_RDAHEAD, 1);
2064 MONO_EXIT_GC_SAFE;
2066 #endif
2068 mono_fdhandle_insert ((MonoFDHandle*) filehandle);
2070 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: returning handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd));
2072 return GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd);
2075 gboolean
2076 mono_w32file_cancel (gpointer handle)
2078 mono_w32error_set_last (ERROR_NOT_SUPPORTED);
2079 return FALSE;
2082 gboolean
2083 mono_w32file_close (gpointer handle)
2085 if (!mono_fdhandle_close (GPOINTER_TO_INT (handle))) {
2086 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2087 return FALSE;
2090 return TRUE;
2093 gboolean mono_w32file_delete(const gunichar2 *name)
2095 gchar *filename;
2096 gint retval;
2097 gboolean ret = FALSE;
2098 ERROR_DECL (error);
2099 #if 0
2100 struct stat statbuf;
2101 FileShare *shareinfo;
2102 #endif
2104 if(name==NULL) {
2105 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2107 mono_w32error_set_last (ERROR_INVALID_NAME);
2108 return(FALSE);
2111 filename = mono_unicode_to_external_checked (name, error);
2112 if(filename==NULL) {
2113 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
2115 mono_error_cleanup (error);
2116 mono_w32error_set_last (ERROR_INVALID_NAME);
2117 return(FALSE);
2120 #if 0
2121 /* Check to make sure sharing allows us to open the file for
2122 * writing. See bug 323389.
2124 * Do the checks that don't need an open file descriptor, for
2125 * simplicity's sake. If we really have to do the full checks
2126 * then we can implement that later.
2128 if (_wapi_stat (filename, &statbuf) < 0) {
2129 _wapi_set_last_path_error_from_errno (NULL, filename);
2130 g_free (filename);
2131 return(FALSE);
2134 if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
2135 &shareinfo) == FALSE) {
2136 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2137 g_free (filename);
2138 return FALSE;
2140 if (shareinfo)
2141 file_share_release (shareinfo);
2142 #endif
2144 retval = _wapi_unlink (filename);
2146 if (retval == -1) {
2147 /* On linux, calling unlink on an non-existing file in a read-only mount will fail with EROFS.
2148 * The expected behavior is for this function to return FALSE and not trigger an exception.
2149 * To work around this behavior, we stat the file on failure.
2151 * This was supposedly fixed on kernel 3.0 [1] but we could reproduce it with Ubuntu 16.04 which has kernel 4.4.
2152 * We can't remove this workaround until the early 2020's when most Android deviced will have a fix.
2153 * [1] https://github.com/torvalds/linux/commit/50338b889dc504c69e0cb316ac92d1b9e51f3c8a
2155 if (errno == EROFS) {
2156 MonoIOStat stat;
2157 if (mono_w32file_get_attributes_ex (name, &stat)) //The file exists, so must be due the RO file system
2158 mono_set_errno (EROFS);
2160 _wapi_set_last_path_error_from_errno (NULL, filename);
2161 } else {
2162 ret = TRUE;
2165 g_free(filename);
2167 return(ret);
2170 static gboolean
2171 MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
2173 gchar *utf8_name, *utf8_dest_name;
2174 gint result, errno_copy;
2175 struct stat stat_src, stat_dest;
2176 gboolean ret = FALSE;
2177 FileShare *shareinfo;
2178 ERROR_DECL (error);
2180 if(name==NULL) {
2181 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2183 mono_w32error_set_last (ERROR_INVALID_NAME);
2184 return(FALSE);
2187 utf8_name = mono_unicode_to_external_checked (name, error);
2188 if (utf8_name == NULL) {
2189 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
2191 mono_error_cleanup (error);
2192 mono_w32error_set_last (ERROR_INVALID_NAME);
2193 return FALSE;
2196 if(dest_name==NULL) {
2197 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2199 g_free (utf8_name);
2200 mono_w32error_set_last (ERROR_INVALID_NAME);
2201 return(FALSE);
2204 utf8_dest_name = mono_unicode_to_external_checked (dest_name, error);
2205 if (utf8_dest_name == NULL) {
2206 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
2208 mono_error_cleanup (error);
2209 g_free (utf8_name);
2210 mono_w32error_set_last (ERROR_INVALID_NAME);
2211 return FALSE;
2215 * In C# land we check for the existence of src, but not for dest.
2216 * We check it here and return the failure if dest exists and is not
2217 * the same file as src.
2219 if (_wapi_stat (utf8_name, &stat_src) < 0) {
2220 if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
2221 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
2222 g_free (utf8_name);
2223 g_free (utf8_dest_name);
2224 return FALSE;
2228 if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
2229 if (stat_dest.st_dev != stat_src.st_dev ||
2230 stat_dest.st_ino != stat_src.st_ino) {
2231 g_free (utf8_name);
2232 g_free (utf8_dest_name);
2233 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2234 return FALSE;
2238 /* Check to make that we have delete sharing permission.
2239 * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
2241 * Do the checks that don't need an open file descriptor, for
2242 * simplicity's sake. If we really have to do the full checks
2243 * then we can implement that later.
2245 if (share_allows_delete (&stat_src, &shareinfo) == FALSE) {
2246 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2247 return FALSE;
2249 if (shareinfo) {
2250 file_share_release (shareinfo);
2251 shareinfo = NULL;
2254 result = _wapi_rename (utf8_name, utf8_dest_name);
2255 errno_copy = errno;
2257 if (result == -1) {
2258 switch(errno_copy) {
2259 case EEXIST:
2260 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2261 break;
2263 case EXDEV:
2264 /* Ignore here, it is dealt with below */
2265 break;
2267 case ENOENT:
2268 /* We already know src exists. Must be dest that doesn't exist. */
2269 _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name);
2270 break;
2272 default:
2273 _wapi_set_last_error_from_errno ();
2277 g_free (utf8_name);
2278 g_free (utf8_dest_name);
2280 if (result != 0 && errno_copy == EXDEV) {
2281 gint32 copy_error;
2283 if (S_ISDIR (stat_src.st_mode)) {
2284 mono_w32error_set_last (ERROR_NOT_SAME_DEVICE);
2285 return FALSE;
2287 /* Try a copy to the new location, and delete the source */
2288 if (!mono_w32file_copy (name, dest_name, FALSE, &copy_error)) {
2289 /* mono_w32file_copy will set the error */
2290 return(FALSE);
2293 return(mono_w32file_delete (name));
2296 if (result == 0) {
2297 ret = TRUE;
2300 return(ret);
2303 static gboolean
2304 write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_errors)
2306 gint remain, n;
2307 gchar *buf, *wbuf;
2308 gint buf_size = st_src->st_blksize;
2309 MonoThreadInfo *info = mono_thread_info_current ();
2311 buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
2312 buf = (gchar *) g_malloc (buf_size);
2314 for (;;) {
2315 MONO_ENTER_GC_SAFE;
2316 remain = read (src_fd, buf, buf_size);
2317 MONO_EXIT_GC_SAFE;
2318 if (remain < 0) {
2319 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2320 continue;
2322 if (report_errors)
2323 _wapi_set_last_error_from_errno ();
2325 g_free (buf);
2326 return FALSE;
2328 if (remain == 0) {
2329 break;
2332 wbuf = buf;
2333 while (remain > 0) {
2334 MONO_ENTER_GC_SAFE;
2335 n = write (dest_fd, wbuf, remain);
2336 MONO_EXIT_GC_SAFE;
2337 if (n < 0) {
2338 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2339 continue;
2341 if (report_errors)
2342 _wapi_set_last_error_from_errno ();
2343 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write failed.", __func__);
2344 g_free (buf);
2345 return FALSE;
2348 remain -= n;
2349 wbuf += n;
2353 g_free (buf);
2354 return TRUE ;
2357 #if HOST_DARWIN
2358 static int
2359 _wapi_clonefile(const char *from, const char *to, int flags)
2361 gchar *located_from, *located_to;
2362 int ret;
2364 g_assert (clonefile_ptr != NULL);
2366 located_from = mono_portability_find_file (from, FALSE);
2367 located_to = mono_portability_find_file (to, FALSE);
2369 MONO_ENTER_GC_SAFE;
2370 ret = clonefile_ptr (
2371 located_from == NULL ? from : located_from,
2372 located_to == NULL ? to : located_to,
2373 flags);
2374 MONO_EXIT_GC_SAFE;
2376 g_free (located_from);
2377 g_free (located_to);
2379 return ret;
2381 #endif
2383 static gboolean
2384 CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists)
2386 gchar *utf8_src, *utf8_dest;
2387 struct stat st, dest_st;
2388 gboolean ret = TRUE;
2389 ERROR_DECL (error);
2391 if(name==NULL) {
2392 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2394 mono_w32error_set_last (ERROR_INVALID_NAME);
2395 return(FALSE);
2398 utf8_src = mono_unicode_to_external_checked (name, error);
2399 if (utf8_src == NULL) {
2400 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of source returned NULL; %s",
2401 __func__, mono_error_get_message (error));
2403 mono_error_cleanup (error);
2404 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2405 return(FALSE);
2408 if(dest_name==NULL) {
2409 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: dest is NULL", __func__);
2411 g_free (utf8_src);
2412 mono_w32error_set_last (ERROR_INVALID_NAME);
2413 return(FALSE);
2416 utf8_dest = mono_unicode_to_external_checked (dest_name, error);
2417 if (utf8_dest == NULL) {
2418 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of dest returned NULL; %s",
2419 __func__, mono_error_get_message (error));
2421 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2423 mono_error_cleanup (error);
2424 g_free (utf8_src);
2426 return(FALSE);
2429 gint src_fd, dest_fd;
2430 gint ret_utime;
2431 gint syscall_res;
2433 src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2434 if (src_fd < 0) {
2435 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2437 g_free (utf8_src);
2438 g_free (utf8_dest);
2440 return(FALSE);
2443 MONO_ENTER_GC_SAFE;
2444 syscall_res = fstat (src_fd, &st);
2445 MONO_EXIT_GC_SAFE;
2446 if (syscall_res < 0) {
2447 _wapi_set_last_error_from_errno ();
2449 g_free (utf8_src);
2450 g_free (utf8_dest);
2451 MONO_ENTER_GC_SAFE;
2452 close (src_fd);
2453 MONO_EXIT_GC_SAFE;
2455 return(FALSE);
2458 if (!_wapi_stat (utf8_dest, &dest_st)) {
2459 /* Before trying to open/create the dest, we need to report a 'file busy'
2460 * error if src and dest are actually the same file. We do the check here to take
2461 * advantage of the IOMAP capability */
2462 if (st.st_dev == dest_st.st_dev && st.st_ino == dest_st.st_ino) {
2463 g_free (utf8_src);
2464 g_free (utf8_dest);
2465 MONO_ENTER_GC_SAFE;
2466 close (src_fd);
2467 MONO_EXIT_GC_SAFE;
2469 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2470 return (FALSE);
2473 /* Take advantage of the fact that we already know the file exists and bail out
2474 * early */
2475 if (fail_if_exists) {
2476 g_free (utf8_src);
2477 g_free (utf8_dest);
2478 MONO_ENTER_GC_SAFE;
2479 close (src_fd);
2480 MONO_EXIT_GC_SAFE;
2482 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2483 return (FALSE);
2486 #if HOST_DARWIN
2487 /* If we attempt to use clonefile API we need to unlink the destination file
2488 * first */
2489 if (clonefile_ptr != NULL) {
2491 /* Bail out if the destination is read-only */
2492 if (!is_file_writable (&dest_st, utf8_dest)) {
2493 g_free (utf8_src);
2494 g_free (utf8_dest);
2495 MONO_ENTER_GC_SAFE;
2496 close (src_fd);
2497 MONO_EXIT_GC_SAFE;
2499 mono_w32error_set_last (ERROR_ACCESS_DENIED);
2500 return (FALSE);
2503 _wapi_unlink (utf8_dest);
2505 #endif
2508 #if HOST_DARWIN
2509 if (clonefile_ptr != NULL) {
2510 ret = _wapi_clonefile (utf8_src, utf8_dest, 0);
2511 if (ret == 0 || (errno != ENOTSUP && errno != EXDEV)) {
2512 g_free (utf8_src);
2513 g_free (utf8_dest);
2514 MONO_ENTER_GC_SAFE;
2515 close (src_fd);
2516 MONO_EXIT_GC_SAFE;
2518 if (ret == 0) {
2519 return (TRUE);
2520 } else {
2521 _wapi_set_last_error_from_errno ();
2522 return (FALSE);
2526 #endif
2528 if (fail_if_exists) {
2529 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
2530 } else {
2531 /* FIXME: it kinda sucks that this code path potentially scans
2532 * the directory twice due to the weird mono_w32error_set_last()
2533 * behavior. */
2534 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2535 if (dest_fd < 0) {
2536 /* The file does not exist, try creating it */
2537 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
2538 } else {
2539 /* Apparently this error is set if we
2540 * overwrite the dest file
2542 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2545 if (dest_fd < 0) {
2546 _wapi_set_last_error_from_errno ();
2548 g_free (utf8_src);
2549 g_free (utf8_dest);
2550 MONO_ENTER_GC_SAFE;
2551 close (src_fd);
2552 MONO_EXIT_GC_SAFE;
2554 return(FALSE);
2557 if (!write_file (src_fd, dest_fd, &st, TRUE))
2558 ret = FALSE;
2560 close (src_fd);
2561 close (dest_fd);
2563 #ifdef HAVE_STRUCT_TIMEVAL
2564 struct timeval times [2];
2565 memset (times, 0, sizeof (times));
2567 convert_stattime_access_to_timeval (&times [0], &st);
2568 convert_stattime_mod_to_timeval (&times [1], &st);
2570 ret_utime = _wapi_utimes (utf8_dest, times);
2571 #else
2572 struct utimbuf dest_time;
2573 dest_time.modtime = st.st_mtime;
2574 dest_time.actime = st.st_atime;
2576 ret_utime = _wapi_utime (utf8_dest, &dest_time);
2577 #endif
2578 if (ret_utime == -1)
2579 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: file [%s] utime failed: %s", __func__, utf8_dest, g_strerror(errno));
2581 g_free (utf8_src);
2582 g_free (utf8_dest);
2584 return ret;
2587 static gchar*
2588 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2590 gchar *utf8_ret;
2591 ERROR_DECL (error);
2593 if (arg == NULL) {
2594 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: %s is NULL", __func__, arg_name);
2595 mono_w32error_set_last (ERROR_INVALID_NAME);
2596 return NULL;
2599 utf8_ret = mono_unicode_to_external_checked (arg, error);
2600 if (utf8_ret == NULL) {
2601 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of %s returned NULL; %s",
2602 __func__, arg_name, mono_error_get_message (error));
2604 mono_error_cleanup (error);
2605 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2606 return NULL;
2609 return utf8_ret;
2612 static gboolean
2613 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved)
2615 gint result, backup_fd = -1,replaced_fd = -1;
2616 gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2617 struct stat stBackup;
2618 gboolean ret = FALSE;
2620 if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2621 return FALSE;
2622 if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2623 goto replace_cleanup;
2624 if (backupFileName != NULL) {
2625 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2626 goto replace_cleanup;
2629 if (utf8_backupFileName) {
2630 // Open the backup file for read so we can restore the file if an error occurs.
2631 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2632 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2633 if (result == -1)
2634 goto replace_cleanup;
2637 result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2638 if (result == -1) {
2639 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2640 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2641 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2642 replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2643 stBackup.st_mode);
2645 if (replaced_fd == -1)
2646 goto replace_cleanup;
2648 write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2651 goto replace_cleanup;
2654 ret = TRUE;
2656 replace_cleanup:
2657 g_free (utf8_replacedFileName);
2658 g_free (utf8_replacementFileName);
2659 g_free (utf8_backupFileName);
2660 if (backup_fd != -1) {
2661 MONO_ENTER_GC_SAFE;
2662 close (backup_fd);
2663 MONO_EXIT_GC_SAFE;
2665 if (replaced_fd != -1) {
2666 MONO_ENTER_GC_SAFE;
2667 close (replaced_fd);
2668 MONO_EXIT_GC_SAFE;
2670 return ret;
2673 static gpointer
2674 _wapi_stdhandle_create (gint fd, const gchar *name)
2676 gint flags;
2677 FileHandle *filehandle;
2679 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: creating standard handle type %s, fd %d", __func__, name, fd);
2681 /* Check if fd is valid */
2682 do {
2683 flags = fcntl(fd, F_GETFL);
2684 } while (flags == -1 && errno == EINTR);
2686 if (flags == -1) {
2687 /* Invalid fd. Not really much point checking for EBADF
2688 * specifically
2690 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl error on fd %d: %s", __func__, fd, g_strerror(errno));
2692 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
2693 return INVALID_HANDLE_VALUE;
2696 filehandle = file_data_create (MONO_FDTYPE_CONSOLE, fd);
2697 filehandle->filename = g_strdup(name);
2699 switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) {
2700 case O_RDONLY:
2701 filehandle->fileaccess = GENERIC_READ;
2702 break;
2703 case O_WRONLY:
2704 filehandle->fileaccess = GENERIC_WRITE;
2705 break;
2706 case O_RDWR:
2707 filehandle->fileaccess = GENERIC_READ | GENERIC_WRITE;
2708 break;
2709 default:
2710 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Can't figure out flags 0x%x", __func__, flags);
2711 filehandle->fileaccess = 0;
2712 break;
2715 /* some default security attributes might be needed */
2716 filehandle->security_attributes = 0;
2718 /* Apparently input handles can't be written to. (I don't
2719 * know if output or error handles can't be read from.)
2721 if (fd == 0)
2722 filehandle->fileaccess &= ~GENERIC_WRITE;
2724 filehandle->sharemode = 0;
2725 filehandle->attrs = 0;
2727 if (!mono_fdhandle_try_insert ((MonoFDHandle*) filehandle)) {
2728 /* we raced between 2 invocations of _wapi_stdhandle_create */
2729 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2730 return GINT_TO_POINTER(fd);
2733 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: returning handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd));
2735 return GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd);
2738 enum {
2739 STD_INPUT_HANDLE = -10,
2740 STD_OUTPUT_HANDLE = -11,
2741 STD_ERROR_HANDLE = -12,
2744 static gpointer
2745 mono_w32file_get_std_handle (gint stdhandle)
2747 FileHandle **filehandle;
2748 gint fd;
2749 const gchar *name;
2751 switch(stdhandle) {
2752 case STD_INPUT_HANDLE:
2753 fd = 0;
2754 name = "<stdin>";
2755 break;
2757 case STD_OUTPUT_HANDLE:
2758 fd = 1;
2759 name = "<stdout>";
2760 break;
2762 case STD_ERROR_HANDLE:
2763 fd = 2;
2764 name = "<stderr>";
2765 break;
2767 default:
2768 g_assert_not_reached ();
2771 if (!mono_fdhandle_lookup_and_ref(fd, (MonoFDHandle**) &filehandle)) {
2772 gpointer handle;
2774 handle = _wapi_stdhandle_create (fd, name);
2775 if (handle == INVALID_HANDLE_VALUE) {
2776 mono_w32error_set_last (ERROR_NO_MORE_FILES);
2777 return INVALID_HANDLE_VALUE;
2781 return GINT_TO_POINTER (fd);
2784 static gboolean
2785 mono_w32file_read_or_write (gboolean read, gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, gint32 *win32error)
2787 MONO_REQ_GC_UNSAFE_MODE;
2789 FileHandle *filehandle;
2790 gboolean ret = FALSE;
2792 gboolean const ref = mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle);
2793 if (!ref) {
2794 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2795 goto exit;
2798 switch (((MonoFDHandle*) filehandle)->type) {
2799 case MONO_FDTYPE_FILE:
2800 ret = (read ? file_read : file_write) (filehandle, buffer, numbytes, bytesread);
2801 break;
2802 case MONO_FDTYPE_CONSOLE:
2803 ret = (read ? console_read : console_write) (filehandle, buffer, numbytes, bytesread);
2804 break;
2805 case MONO_FDTYPE_PIPE:
2806 ret = (read ? pipe_read : pipe_write) (filehandle, buffer, numbytes, bytesread);
2807 break;
2808 default:
2809 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2810 break;
2813 exit:
2814 if (ref)
2815 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2816 if (!ret)
2817 *win32error = mono_w32error_get_last ();
2818 return ret;
2821 gboolean
2822 mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, gint32 *win32error)
2824 return mono_w32file_read_or_write (TRUE, handle, buffer, numbytes, bytesread, win32error);
2827 gboolean
2828 mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, gint32 *win32error)
2830 return mono_w32file_read_or_write (FALSE, handle, (gpointer)buffer, numbytes, byteswritten, win32error);
2833 gboolean
2834 mono_w32file_flush (gpointer handle)
2836 FileHandle *filehandle;
2837 gboolean ret;
2839 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2840 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2841 return FALSE;
2844 switch (((MonoFDHandle*) filehandle)->type) {
2845 case MONO_FDTYPE_FILE:
2846 ret = file_flush(filehandle);
2847 break;
2848 default:
2849 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2850 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2851 return FALSE;
2854 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2855 return ret;
2858 gboolean
2859 mono_w32file_truncate (gpointer handle)
2861 FileHandle *filehandle;
2862 gboolean ret;
2864 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2865 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2866 return FALSE;
2869 switch (((MonoFDHandle*) filehandle)->type) {
2870 case MONO_FDTYPE_FILE:
2871 ret = file_setendoffile(filehandle);
2872 break;
2873 default:
2874 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2875 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2876 return FALSE;
2879 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2880 return ret;
2883 guint32
2884 mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method)
2886 FileHandle *filehandle;
2887 guint32 ret;
2889 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2890 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2891 return INVALID_SET_FILE_POINTER;
2894 switch (((MonoFDHandle*) filehandle)->type) {
2895 case MONO_FDTYPE_FILE:
2896 ret = file_seek(filehandle, movedistance, highmovedistance, method);
2897 break;
2898 default:
2899 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2900 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2901 return INVALID_SET_FILE_POINTER;
2904 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2905 return ret;
2908 gint
2909 mono_w32file_get_type(gpointer handle)
2911 FileHandle *filehandle;
2912 gint ret;
2914 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2915 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2916 return FILE_TYPE_UNKNOWN;
2919 switch (((MonoFDHandle*) filehandle)->type) {
2920 case MONO_FDTYPE_FILE:
2921 ret = FILE_TYPE_DISK;
2922 break;
2923 case MONO_FDTYPE_CONSOLE:
2924 ret = FILE_TYPE_CHAR;
2925 break;
2926 case MONO_FDTYPE_PIPE:
2927 ret = FILE_TYPE_PIPE;
2928 break;
2929 default:
2930 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2931 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2932 return FILE_TYPE_UNKNOWN;
2935 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2936 return ret;
2939 static guint32
2940 GetFileSize(gpointer handle, guint32 *highsize)
2942 FileHandle *filehandle;
2943 guint32 ret;
2945 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2946 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2947 return INVALID_FILE_SIZE;
2950 switch (((MonoFDHandle*) filehandle)->type) {
2951 case MONO_FDTYPE_FILE:
2952 ret = file_getfilesize(filehandle, highsize);
2953 break;
2954 default:
2955 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2956 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2957 return INVALID_FILE_SIZE;
2960 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2961 return ret;
2964 gboolean
2965 mono_w32file_set_times(gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time)
2967 FileHandle *filehandle;
2968 gboolean ret;
2970 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2971 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2972 return FALSE;
2975 switch (((MonoFDHandle*) filehandle)->type) {
2976 case MONO_FDTYPE_FILE:
2977 ret = file_setfiletime(filehandle, create_time, access_time, write_time);
2978 break;
2979 default:
2980 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2981 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2982 return FALSE;
2985 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2986 return ret;
2989 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2990 * January 1 1601 GMT
2993 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2995 static const guint16 mon_yday[2][13]={
2996 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2997 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
3000 gboolean
3001 mono_w32file_filetime_to_systemtime(const FILETIME *file_time, SYSTEMTIME *system_time)
3003 gint64 file_ticks, totaldays, rem, y;
3004 const guint16 *ip;
3006 if(system_time==NULL) {
3007 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: system_time NULL", __func__);
3009 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3010 return(FALSE);
3013 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
3014 file_time->dwLowDateTime;
3016 /* Really compares if file_ticks>=0x8000000000000000
3017 * (LLONG_MAX+1) but we're working with a signed value for the
3018 * year and day calculation to work later
3020 if(file_ticks<0) {
3021 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: file_time too big", __func__);
3023 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3024 return(FALSE);
3027 totaldays=(file_ticks / TICKS_PER_DAY);
3028 rem = file_ticks % TICKS_PER_DAY;
3029 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT " rem: %" G_GINT64_FORMAT, __func__,
3030 totaldays, rem);
3032 system_time->wHour=rem/TICKS_PER_HOUR;
3033 rem %= TICKS_PER_HOUR;
3034 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Hour: %d rem: %" G_GINT64_FORMAT, __func__,
3035 system_time->wHour, rem);
3037 system_time->wMinute = rem / TICKS_PER_MINUTE;
3038 rem %= TICKS_PER_MINUTE;
3039 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Minute: %d rem: %" G_GINT64_FORMAT, __func__,
3040 system_time->wMinute, rem);
3042 system_time->wSecond = rem / TICKS_PER_SECOND;
3043 rem %= TICKS_PER_SECOND;
3044 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Second: %d rem: %" G_GINT64_FORMAT, __func__,
3045 system_time->wSecond, rem);
3047 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
3048 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Milliseconds: %d", __func__,
3049 system_time->wMilliseconds);
3051 /* January 1, 1601 was a Monday, according to Emacs calendar */
3052 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
3053 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Day of week: %d", __func__, system_time->wDayOfWeek);
3055 /* This algorithm to find year and month given days from epoch
3056 * from glibc
3058 y=1601;
3060 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
3061 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
3063 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
3064 /* Guess a corrected year, assuming 365 days per year */
3065 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
3066 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT " yg: %" G_GINT64_FORMAT " y: %" G_GINT64_FORMAT, __func__,
3067 totaldays, yg, y);
3068 g_message("%s: LEAPS(yg): %li LEAPS(y): %li", __func__,
3069 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
3071 /* Adjust days and y to match the guessed year. */
3072 totaldays -= ((yg - y) * 365
3073 + LEAPS_THRU_END_OF (yg - 1)
3074 - LEAPS_THRU_END_OF (y - 1));
3075 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT,
3076 __func__, totaldays);
3077 y = yg;
3078 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: y: %" G_GINT64_FORMAT, __func__, y);
3081 system_time->wYear = y;
3082 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Year: %d", __func__, system_time->wYear);
3084 ip = mon_yday[isleap(y)];
3086 for(y=11; totaldays < ip[y]; --y) {
3087 continue;
3089 totaldays-=ip[y];
3090 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT, __func__, totaldays);
3092 system_time->wMonth = y + 1;
3093 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Month: %d", __func__, system_time->wMonth);
3095 system_time->wDay = totaldays + 1;
3096 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Day: %d", __func__, system_time->wDay);
3098 return(TRUE);
3101 static void
3102 findhandle_destroy (gpointer data)
3104 FindHandle *findhandle;
3106 findhandle = (FindHandle*) data;
3107 g_assert (findhandle);
3109 mono_coop_mutex_destroy (&findhandle->mutex);
3111 if (findhandle->namelist)
3112 g_strfreev (findhandle->namelist);
3113 if (findhandle->dir_part)
3114 g_free (findhandle->dir_part);
3116 g_free (findhandle);
3119 static FindHandle*
3120 findhandle_create (void)
3122 FindHandle* findhandle;
3124 findhandle = g_new0 (FindHandle, 1);
3125 mono_refcount_init (findhandle, findhandle_destroy);
3127 mono_coop_mutex_init (&findhandle->mutex);
3129 return findhandle;
3132 static void
3133 findhandle_insert (FindHandle *findhandle)
3135 mono_coop_mutex_lock (&finds_mutex);
3137 if (g_hash_table_lookup_extended (finds, (gpointer) findhandle, NULL, NULL))
3138 g_error("%s: duplicate Find handle %p", __func__, (gpointer) findhandle);
3140 g_hash_table_insert (finds, (gpointer) findhandle, findhandle);
3142 mono_coop_mutex_unlock (&finds_mutex);
3145 static gboolean
3146 findhandle_lookup_and_ref (gpointer handle, FindHandle **findhandle)
3148 mono_coop_mutex_lock (&finds_mutex);
3150 if (!g_hash_table_lookup_extended (finds, handle, NULL, (gpointer*) findhandle)) {
3151 mono_coop_mutex_unlock (&finds_mutex);
3152 return FALSE;
3155 mono_refcount_inc (*findhandle);
3157 mono_coop_mutex_unlock (&finds_mutex);
3159 return TRUE;
3162 static void
3163 findhandle_unref (FindHandle *findhandle)
3165 mono_refcount_dec (findhandle);
3168 static gboolean
3169 findhandle_close (gpointer handle)
3171 FindHandle *findhandle;
3172 gboolean removed;
3174 mono_coop_mutex_lock (&finds_mutex);
3176 if (!g_hash_table_lookup_extended (finds, handle, NULL, (gpointer*) &findhandle)) {
3177 mono_coop_mutex_unlock (&finds_mutex);
3179 return FALSE;
3182 removed = g_hash_table_remove (finds, (gpointer) findhandle);
3183 g_assert (removed);
3185 mono_coop_mutex_unlock (&finds_mutex);
3187 return TRUE;
3190 static void
3191 finds_remove (gpointer data)
3193 FindHandle* findhandle;
3195 findhandle = (FindHandle*) data;
3196 g_assert (findhandle);
3198 mono_refcount_dec (findhandle);
3201 gpointer
3202 mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data)
3204 FindHandle *findhandle;
3205 gchar *utf8_pattern = NULL, *dir_part, *entry_part, **namelist;
3206 gint result;
3207 ERROR_DECL (error);
3209 if (pattern == NULL) {
3210 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: pattern is NULL", __func__);
3212 mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
3213 return(INVALID_HANDLE_VALUE);
3216 utf8_pattern = mono_unicode_to_external_checked (pattern, error);
3217 if (utf8_pattern == NULL) {
3218 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3220 mono_error_cleanup (error);
3221 mono_w32error_set_last (ERROR_INVALID_NAME);
3222 return(INVALID_HANDLE_VALUE);
3225 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: looking for [%s]", __func__, utf8_pattern);
3227 /* Figure out which bit of the pattern is the directory */
3228 dir_part = _wapi_dirname (utf8_pattern);
3229 entry_part = _wapi_basename (utf8_pattern);
3231 #if 0
3232 /* Don't do this check for now, it breaks if directories
3233 * really do have metachars in their names (see bug 58116).
3234 * FIXME: Figure out a better solution to keep some checks...
3236 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3237 mono_w32error_set_last (ERROR_INVALID_NAME);
3238 g_free (dir_part);
3239 g_free (entry_part);
3240 g_free (utf8_pattern);
3241 return(INVALID_HANDLE_VALUE);
3243 #endif
3245 /* The pattern can specify a directory or a set of files.
3247 * The pattern can have wildcard characters ? and *, but only
3248 * in the section after the last directory delimiter. (Return
3249 * ERROR_INVALID_NAME if there are wildcards in earlier path
3250 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3251 * means "match one character", "??" seems to mean "match one
3252 * or two characters", "???" seems to mean "match one, two or
3253 * three characters", etc. Windows will also try and match
3254 * the mangled "short name" of files, so 8 character patterns
3255 * with wildcards will show some surprising results.
3257 * All the written documentation I can find says that '?'
3258 * should only match one character, and doesn't mention '??',
3259 * '???' etc. I'm going to assume that the strict behaviour
3260 * (ie '???' means three and only three characters) is the
3261 * correct one, because that lets me use fnmatch(3) rather
3262 * than mess around with regexes.
3265 namelist = NULL;
3266 result = _wapi_io_scandir (dir_part, entry_part,
3267 &namelist);
3269 if (result == 0) {
3270 /* No files, which windows seems to call
3271 * FILE_NOT_FOUND
3273 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
3274 g_free (utf8_pattern);
3275 g_free (entry_part);
3276 g_free (dir_part);
3277 g_strfreev (namelist);
3278 return (INVALID_HANDLE_VALUE);
3281 if (result < 0) {
3282 _wapi_set_last_path_error_from_errno (dir_part, NULL);
3283 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: scandir error: %s", __func__, g_strerror (errno));
3284 g_free (utf8_pattern);
3285 g_free (entry_part);
3286 g_free (dir_part);
3287 g_strfreev (namelist);
3288 return (INVALID_HANDLE_VALUE);
3291 g_free (utf8_pattern);
3292 g_free (entry_part);
3294 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Got %d matches", __func__, result);
3296 findhandle = findhandle_create ();
3297 findhandle->namelist = namelist;
3298 findhandle->dir_part = dir_part;
3299 findhandle->num = result;
3300 findhandle->count = 0;
3302 findhandle_insert (findhandle);
3304 if (!mono_w32file_find_next ((gpointer) findhandle, find_data)) {
3305 mono_w32file_find_close ((gpointer) findhandle);
3306 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3307 return INVALID_HANDLE_VALUE;
3310 return (gpointer) findhandle;
3313 gboolean
3314 mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data)
3316 FindHandle *findhandle;
3317 struct stat buf, linkbuf;
3318 gint result;
3319 gchar *filename;
3320 gchar *utf8_filename, *utf8_basename;
3321 gunichar2 *utf16_basename;
3322 time_t create_time;
3323 glong bytes;
3324 gboolean ret = FALSE;
3326 if (!findhandle_lookup_and_ref (handle, &findhandle)) {
3327 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3328 return FALSE;
3331 mono_coop_mutex_lock (&findhandle->mutex);
3333 retry:
3334 if (findhandle->count >= findhandle->num) {
3335 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3336 goto cleanup;
3339 /* stat next match */
3341 filename = g_build_filename (findhandle->dir_part, findhandle->namelist[findhandle->count ++], (const char*)NULL);
3343 result = _wapi_stat (filename, &buf);
3344 if (result == -1 && errno == ENOENT) {
3345 /* Might be a dangling symlink */
3346 result = _wapi_lstat (filename, &buf);
3349 if (result != 0) {
3350 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: stat failed: %s", __func__, filename);
3352 g_free (filename);
3353 goto retry;
3356 result = _wapi_lstat (filename, &linkbuf);
3357 if (result != 0) {
3358 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lstat failed: %s", __func__, filename);
3360 g_free (filename);
3361 goto retry;
3364 utf8_filename = mono_utf8_from_external (filename);
3365 if (utf8_filename == NULL) {
3366 /* We couldn't turn this filename into utf8 (eg the
3367 * encoding of the name wasn't convertible), so just
3368 * ignore it.
3370 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
3372 g_free (filename);
3373 goto retry;
3375 g_free (filename);
3377 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Found [%s]", __func__, utf8_filename);
3379 /* fill data block */
3381 if (buf.st_mtime < buf.st_ctime)
3382 create_time = buf.st_mtime;
3383 else
3384 create_time = buf.st_ctime;
3386 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
3388 time_t_to_filetime (create_time, &find_data->ftCreationTime);
3389 time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3390 time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3392 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3393 find_data->nFileSizeHigh = 0;
3394 find_data->nFileSizeLow = 0;
3395 } else {
3396 find_data->nFileSizeHigh = buf.st_size >> 32;
3397 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3400 find_data->dwReserved0 = 0;
3401 find_data->dwReserved1 = 0;
3403 utf8_basename = _wapi_basename (utf8_filename);
3404 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3405 NULL);
3406 if(utf16_basename==NULL) {
3407 g_free (utf8_basename);
3408 g_free (utf8_filename);
3409 goto retry;
3411 ret = TRUE;
3413 /* utf16 is 2 * utf8 */
3414 bytes *= 2;
3416 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3418 /* Truncating a utf16 string like this might leave the last
3419 * gchar incomplete
3421 memcpy (find_data->cFileName, utf16_basename,
3422 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3424 find_data->cAlternateFileName [0] = 0; /* not used */
3426 g_free (utf8_basename);
3427 g_free (utf8_filename);
3428 g_free (utf16_basename);
3430 cleanup:
3431 mono_coop_mutex_unlock (&findhandle->mutex);
3433 findhandle_unref (findhandle);
3435 return(ret);
3438 gboolean
3439 mono_w32file_find_close (gpointer handle)
3441 if (!findhandle_close (handle)) {
3442 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3443 return FALSE;
3446 return TRUE;
3449 gboolean
3450 mono_w32file_create_directory (const gunichar2 *name)
3452 gchar *utf8_name;
3453 gint result;
3454 ERROR_DECL (error);
3456 if (name == NULL) {
3457 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3459 mono_w32error_set_last (ERROR_INVALID_NAME);
3460 return(FALSE);
3463 utf8_name = mono_unicode_to_external_checked (name, error);
3464 if (utf8_name == NULL) {
3465 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3467 mono_error_cleanup (error);
3468 mono_w32error_set_last (ERROR_INVALID_NAME);
3469 return FALSE;
3472 result = _wapi_mkdir (utf8_name, 0777);
3474 if (result == 0) {
3475 g_free (utf8_name);
3476 return TRUE;
3479 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3480 g_free (utf8_name);
3481 return FALSE;
3484 gboolean
3485 mono_w32file_remove_directory (const gunichar2 *name)
3487 gchar *utf8_name;
3488 gint result;
3489 ERROR_DECL (error);
3491 if (name == NULL) {
3492 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3494 mono_w32error_set_last (ERROR_INVALID_NAME);
3495 return(FALSE);
3498 utf8_name = mono_unicode_to_external_checked (name, error);
3499 if (utf8_name == NULL) {
3500 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3502 mono_error_cleanup (error);
3503 mono_w32error_set_last (ERROR_INVALID_NAME);
3504 return FALSE;
3507 result = _wapi_rmdir (utf8_name);
3508 if (result == -1) {
3509 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3510 g_free (utf8_name);
3512 return(FALSE);
3514 g_free (utf8_name);
3516 return(TRUE);
3519 guint32
3520 mono_w32file_get_attributes (const gunichar2 *name)
3522 gchar *utf8_name;
3523 struct stat buf, linkbuf;
3524 gint result;
3525 guint32 ret;
3526 ERROR_DECL (error);
3528 if (name == NULL) {
3529 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3531 mono_w32error_set_last (ERROR_INVALID_NAME);
3532 return(FALSE);
3535 utf8_name = mono_unicode_to_external_checked (name, error);
3536 if (utf8_name == NULL) {
3537 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3539 mono_error_cleanup (error);
3540 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3541 return (INVALID_FILE_ATTRIBUTES);
3544 result = _wapi_stat (utf8_name, &buf);
3545 if (result == -1 && (errno == ENOENT || errno == ELOOP)) {
3546 /* Might be a dangling symlink... */
3547 result = _wapi_lstat (utf8_name, &buf);
3550 if (result != 0) {
3551 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3552 g_free (utf8_name);
3553 return (INVALID_FILE_ATTRIBUTES);
3556 result = _wapi_lstat (utf8_name, &linkbuf);
3557 if (result != 0) {
3558 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3559 g_free (utf8_name);
3560 return (INVALID_FILE_ATTRIBUTES);
3563 ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3565 g_free (utf8_name);
3567 return(ret);
3570 gboolean
3571 mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat)
3573 gchar *utf8_name;
3575 struct stat buf, linkbuf;
3576 gint result;
3577 ERROR_DECL (error);
3579 if (name == NULL) {
3580 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3582 mono_w32error_set_last (ERROR_INVALID_NAME);
3583 return(FALSE);
3586 utf8_name = mono_unicode_to_external_checked (name, error);
3587 if (utf8_name == NULL) {
3588 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3590 mono_error_cleanup (error);
3591 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3592 return FALSE;
3595 result = _wapi_stat (utf8_name, &buf);
3596 if (result == -1 && errno == ENOENT) {
3597 /* Might be a dangling symlink... */
3598 result = _wapi_lstat (utf8_name, &buf);
3601 if (result != 0) {
3602 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3603 g_free (utf8_name);
3604 return FALSE;
3607 result = _wapi_lstat (utf8_name, &linkbuf);
3608 if (result != 0) {
3609 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3610 g_free (utf8_name);
3611 return(FALSE);
3614 /* fill stat block */
3616 stat->attributes = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3617 stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size;
3619 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
3620 if (linkbuf.st_mtimespec.tv_sec < linkbuf.st_ctimespec.tv_sec || (linkbuf.st_mtimespec.tv_sec == linkbuf.st_ctimespec.tv_sec && linkbuf.st_mtimespec.tv_nsec < linkbuf.st_ctimespec.tv_nsec))
3621 stat->creation_time = linkbuf.st_mtimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3622 else
3623 stat->creation_time = linkbuf.st_ctimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_ctimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3625 stat->last_access_time = linkbuf.st_atimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_atimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3626 stat->last_write_time = linkbuf.st_mtimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3627 #elif HAVE_STRUCT_STAT_ST_ATIM
3628 if (linkbuf.st_mtime < linkbuf.st_ctime || (linkbuf.st_mtime == linkbuf.st_ctime && linkbuf.st_mtim.tv_nsec < linkbuf.st_ctim.tv_nsec))
3629 stat->creation_time = linkbuf.st_mtime * TICKS_PER_SECOND + (linkbuf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3630 else
3631 stat->creation_time = linkbuf.st_ctime * TICKS_PER_SECOND + (linkbuf.st_ctim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3633 stat->last_access_time = linkbuf.st_atime * TICKS_PER_SECOND + (linkbuf.st_atim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3634 stat->last_write_time = linkbuf.st_mtime * TICKS_PER_SECOND + (linkbuf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3635 #else
3636 stat->creation_time = (((guint64) (linkbuf.st_mtime < linkbuf.st_ctime ? linkbuf.st_mtime : linkbuf.st_ctime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3637 stat->last_access_time = (((guint64) (linkbuf.st_atime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3638 stat->last_write_time = (((guint64) (linkbuf.st_mtime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3639 #endif
3641 g_free (utf8_name);
3642 return TRUE;
3645 gboolean
3646 mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs)
3648 /* FIXME: think of something clever to do on unix */
3649 gchar *utf8_name;
3650 struct stat buf;
3651 gint result;
3652 ERROR_DECL (error);
3655 * Currently we only handle one *internal* case, with a value that is
3656 * not standard: 0x80000000, which means `set executable bit'
3659 if (name == NULL) {
3660 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3662 mono_w32error_set_last (ERROR_INVALID_NAME);
3663 return(FALSE);
3666 utf8_name = mono_unicode_to_external_checked (name, error);
3667 if (utf8_name == NULL) {
3668 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3670 mono_error_cleanup (error);
3671 mono_w32error_set_last (ERROR_INVALID_NAME);
3672 return FALSE;
3675 result = _wapi_stat (utf8_name, &buf);
3676 if (result == -1 && errno == ENOENT) {
3677 /* Might be a dangling symlink... */
3678 result = _wapi_lstat (utf8_name, &buf);
3681 if (result != 0) {
3682 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3683 g_free (utf8_name);
3684 return FALSE;
3687 /* Contrary to the documentation, ms allows NORMAL to be
3688 * specified along with other attributes, so dont bother to
3689 * catch that case here.
3691 if (attrs & FILE_ATTRIBUTE_READONLY) {
3692 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP));
3693 } else {
3694 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR);
3697 /* Ignore the other attributes for now */
3699 if (attrs & 0x80000000){
3700 mode_t exec_mask = 0;
3702 if ((buf.st_mode & S_IRUSR) != 0)
3703 exec_mask |= S_IXUSR;
3705 if ((buf.st_mode & S_IRGRP) != 0)
3706 exec_mask |= S_IXGRP;
3708 if ((buf.st_mode & S_IROTH) != 0)
3709 exec_mask |= S_IXOTH;
3711 #if defined(HAVE_CHMOD)
3712 MONO_ENTER_GC_SAFE;
3713 result = chmod (utf8_name, buf.st_mode | exec_mask);
3714 MONO_EXIT_GC_SAFE;
3715 #else
3716 result = -1;
3717 #endif
3719 /* Don't bother to reset executable (might need to change this
3720 * policy)
3723 g_free (utf8_name);
3725 return(TRUE);
3728 guint32
3729 mono_w32file_get_cwd (guint32 length, gunichar2 *buffer)
3731 gunichar2 *utf16_path;
3732 glong count;
3733 gsize bytes;
3735 if (getcwd ((gchar*)buffer, length) == NULL) {
3736 if (errno == ERANGE) { /*buffer length is not big enough */
3737 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*/
3738 if (path == NULL)
3739 return 0;
3740 utf16_path = mono_unicode_from_external (path, &bytes);
3741 g_free (utf16_path);
3742 g_free (path);
3743 return (bytes/2)+1;
3745 _wapi_set_last_error_from_errno ();
3746 return 0;
3749 utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3750 count = (bytes/2)+1;
3751 g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3753 /* Add the terminator */
3754 memset (buffer, '\0', bytes+2);
3755 memcpy (buffer, utf16_path, bytes);
3757 g_free (utf16_path);
3759 return count;
3762 gboolean
3763 mono_w32file_set_cwd (const gunichar2 *path)
3765 gchar *utf8_path;
3766 gboolean result;
3767 ERROR_DECL (error);
3769 if (path == NULL) {
3770 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3771 return(FALSE);
3774 utf8_path = mono_unicode_to_external_checked (path, error);
3775 if (utf8_path == NULL) {
3776 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3778 mono_error_cleanup (error);
3779 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3780 return FALSE;
3783 if (_wapi_chdir (utf8_path) != 0) {
3784 _wapi_set_last_error_from_errno ();
3785 result = FALSE;
3787 else
3788 result = TRUE;
3790 g_free (utf8_path);
3791 return result;
3794 gboolean
3795 mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size)
3797 FileHandle *read_filehandle, *write_filehandle;
3798 gint filedes[2];
3799 gint ret;
3801 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Creating pipe", __func__);
3803 MONO_ENTER_GC_SAFE;
3804 ret=pipe (filedes);
3805 MONO_EXIT_GC_SAFE;
3806 if (ret==-1) {
3807 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Error creating pipe: (%d) %s",
3808 __func__, errno, g_strerror (errno));
3810 _wapi_set_last_error_from_errno ();
3811 return FALSE;
3814 /* filedes[0] is open for reading, filedes[1] for writing */
3816 read_filehandle = file_data_create (MONO_FDTYPE_PIPE, filedes[0]);
3817 read_filehandle->fileaccess = GENERIC_READ;
3819 write_filehandle = file_data_create (MONO_FDTYPE_PIPE, filedes[1]);
3820 write_filehandle->fileaccess = GENERIC_WRITE;
3822 mono_fdhandle_insert ((MonoFDHandle*) read_filehandle);
3823 mono_fdhandle_insert ((MonoFDHandle*) write_filehandle);
3825 *readpipe = GINT_TO_POINTER(((MonoFDHandle*) read_filehandle)->fd);
3826 *writepipe = GINT_TO_POINTER(((MonoFDHandle*) write_filehandle)->fd);
3828 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning pipe: read handle %p, write handle %p",
3829 __func__, GINT_TO_POINTER(((MonoFDHandle*) read_filehandle)->fd), GINT_TO_POINTER(((MonoFDHandle*) write_filehandle)->fd));
3831 return(TRUE);
3834 #ifdef HAVE_GETFSSTAT
3835 /* Darwin has getfsstat */
3836 gint32
3837 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3839 struct statfs *stats;
3840 gint size, n, i;
3841 gunichar2 *dir;
3842 glong length, total = 0;
3843 gint syscall_res;
3845 MONO_ENTER_GC_SAFE;
3846 n = getfsstat (NULL, 0, MNT_NOWAIT);
3847 MONO_EXIT_GC_SAFE;
3848 if (n == -1)
3849 return 0;
3850 size = n * sizeof (struct statfs);
3851 stats = (struct statfs *) g_malloc (size);
3852 if (stats == NULL)
3853 return 0;
3854 MONO_ENTER_GC_SAFE;
3855 syscall_res = getfsstat (stats, size, MNT_NOWAIT);
3856 MONO_EXIT_GC_SAFE;
3857 if (syscall_res == -1){
3858 g_free (stats);
3859 return 0;
3861 for (i = 0; i < n; i++){
3862 dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL);
3863 if (total + length < len){
3864 memcpy (buf + total, dir, sizeof (gunichar2) * length);
3865 buf [total+length] = 0;
3867 g_free (dir);
3868 total += length + 1;
3870 if (total < len)
3871 buf [total] = 0;
3872 total++;
3873 g_free (stats);
3874 return total;
3876 #elif _AIX
3877 gint32
3878 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3880 struct vmount *mounts;
3881 // ret will first be the errno cond, then no of structs
3882 int needsize, ret, total;
3883 gunichar2 *dir;
3884 glong length;
3885 total = 0;
3887 MONO_ENTER_GC_SAFE;
3888 ret = mntctl (MCTL_QUERY, sizeof(needsize), &needsize);
3889 MONO_EXIT_GC_SAFE;
3890 if (ret == -1)
3891 return 0;
3892 mounts = (struct vmount *) g_malloc (needsize);
3893 if (mounts == NULL)
3894 return 0;
3895 MONO_ENTER_GC_SAFE;
3896 ret = mntctl (MCTL_QUERY, needsize, mounts);
3897 MONO_EXIT_GC_SAFE;
3898 if (ret == -1) {
3899 g_free (mounts);
3900 return 0;
3903 for (int i = 0; i < ret; i++) {
3904 dir = g_utf8_to_utf16 (vmt2dataptr(mounts, VMT_STUB), -1, NULL, &length, NULL);
3905 if (total + length < len){
3906 memcpy (buf + total, dir, sizeof (gunichar2) * length);
3907 buf [total+length] = 0;
3909 g_free (dir);
3910 total += length + 1;
3911 mounts = (void*)mounts + mounts->vmt_length; // next!
3913 if (total < len)
3914 buf [total] = 0;
3915 total++;
3916 g_free (mounts);
3917 return total;
3919 #else
3920 /* In-place octal sequence replacement */
3921 static void
3922 unescape_octal (gchar *str)
3924 gchar *rptr;
3925 gchar *wptr;
3927 if (str == NULL)
3928 return;
3930 rptr = wptr = str;
3931 while (*rptr != '\0') {
3932 if (*rptr == '\\') {
3933 gchar c;
3934 rptr++;
3935 c = (*(rptr++) - '0') << 6;
3936 c += (*(rptr++) - '0') << 3;
3937 c += *(rptr++) - '0';
3938 *wptr++ = c;
3939 } else if (wptr != rptr) {
3940 *wptr++ = *rptr++;
3941 } else {
3942 rptr++; wptr++;
3945 *wptr = '\0';
3947 static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf);
3949 #if __linux__
3950 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
3951 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
3952 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
3954 typedef struct
3956 glong total;
3957 guint32 buffer_index;
3958 guint32 mountpoint_index;
3959 guint32 field_number;
3960 guint32 allocated_size;
3961 guint32 fsname_index;
3962 guint32 fstype_index;
3963 gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1];
3964 gchar *mountpoint_allocated;
3965 gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER];
3966 gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3967 gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3968 ssize_t nbytes;
3969 gchar delimiter;
3970 gboolean check_mount_source;
3971 } LinuxMountInfoParseState;
3973 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3974 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3975 static void append_to_mountpoint (LinuxMountInfoParseState *state);
3976 static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3978 gint32
3979 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3981 gint fd;
3982 gint32 ret = 0;
3983 LinuxMountInfoParseState state;
3984 gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL;
3986 memset (buf, 0, len * sizeof (gunichar2));
3987 MONO_ENTER_GC_SAFE;
3988 fd = open ("/proc/self/mountinfo", O_RDONLY);
3989 MONO_EXIT_GC_SAFE;
3990 if (fd != -1)
3991 parser = GetLogicalDriveStrings_MountInfo;
3992 else {
3993 MONO_ENTER_GC_SAFE;
3994 fd = open ("/proc/mounts", O_RDONLY);
3995 MONO_EXIT_GC_SAFE;
3996 if (fd != -1)
3997 parser = GetLogicalDriveStrings_Mounts;
4000 if (!parser) {
4001 ret = GetLogicalDriveStrings_Mtab (len, buf);
4002 goto done_and_out;
4005 memset (&state, 0, sizeof (LinuxMountInfoParseState));
4006 state.field_number = 1;
4007 state.delimiter = ' ';
4009 while (1) {
4010 MONO_ENTER_GC_SAFE;
4011 state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER);
4012 MONO_EXIT_GC_SAFE;
4013 if (!(state.nbytes > 0))
4014 break;
4015 state.buffer_index = 0;
4017 while ((*parser)(len, buf, &state)) {
4018 if (state.buffer [state.buffer_index] == '\n') {
4019 gboolean quit = add_drive_string (len, buf, &state);
4020 state.field_number = 1;
4021 state.buffer_index++;
4022 if (state.mountpoint_allocated) {
4023 g_free (state.mountpoint_allocated);
4024 state.mountpoint_allocated = NULL;
4026 if (quit) {
4027 ret = state.total;
4028 goto done_and_out;
4033 ret = state.total;
4035 done_and_out:
4036 if (fd != -1) {
4037 MONO_ENTER_GC_SAFE;
4038 close (fd);
4039 MONO_EXIT_GC_SAFE;
4041 return ret;
4044 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4046 gchar *ptr;
4048 if (state->field_number == 1)
4049 state->check_mount_source = TRUE;
4051 while (state->buffer_index < (guint32)state->nbytes) {
4052 if (state->buffer [state->buffer_index] == state->delimiter) {
4053 state->field_number++;
4054 switch (state->field_number) {
4055 case 2:
4056 state->mountpoint_index = 0;
4057 break;
4059 case 3:
4060 if (state->mountpoint_allocated)
4061 state->mountpoint_allocated [state->mountpoint_index] = 0;
4062 else
4063 state->mountpoint [state->mountpoint_index] = 0;
4064 break;
4066 default:
4067 ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index);
4068 if (ptr)
4069 state->buffer_index = (ptr - (gchar*)state->buffer) - 1;
4070 else
4071 state->buffer_index = state->nbytes;
4072 return TRUE;
4074 state->buffer_index++;
4075 continue;
4076 } else if (state->buffer [state->buffer_index] == '\n')
4077 return TRUE;
4079 switch (state->field_number) {
4080 case 1:
4081 if (state->check_mount_source) {
4082 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4083 /* We can ignore the rest, it's a device
4084 * path */
4085 state->check_mount_source = FALSE;
4086 state->fsname [state->fsname_index++] = '/';
4087 break;
4089 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4090 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4092 break;
4094 case 2:
4095 append_to_mountpoint (state);
4096 break;
4098 case 3:
4099 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4100 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4101 break;
4104 state->buffer_index++;
4107 return FALSE;
4110 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4112 while (state->buffer_index < (guint32)state->nbytes) {
4113 if (state->buffer [state->buffer_index] == state->delimiter) {
4114 state->field_number++;
4115 switch (state->field_number) {
4116 case 5:
4117 state->mountpoint_index = 0;
4118 break;
4120 case 6:
4121 if (state->mountpoint_allocated)
4122 state->mountpoint_allocated [state->mountpoint_index] = 0;
4123 else
4124 state->mountpoint [state->mountpoint_index] = 0;
4125 break;
4127 case 7:
4128 state->delimiter = '-';
4129 break;
4131 case 8:
4132 state->delimiter = ' ';
4133 break;
4135 case 10:
4136 state->check_mount_source = TRUE;
4137 break;
4139 state->buffer_index++;
4140 continue;
4141 } else if (state->buffer [state->buffer_index] == '\n')
4142 return TRUE;
4144 switch (state->field_number) {
4145 case 5:
4146 append_to_mountpoint (state);
4147 break;
4149 case 9:
4150 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4151 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4152 break;
4154 case 10:
4155 if (state->check_mount_source) {
4156 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4157 /* We can ignore the rest, it's a device
4158 * path */
4159 state->check_mount_source = FALSE;
4160 state->fsname [state->fsname_index++] = '/';
4161 break;
4163 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4164 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4166 break;
4169 state->buffer_index++;
4172 return FALSE;
4175 static void
4176 append_to_mountpoint (LinuxMountInfoParseState *state)
4178 gchar ch = state->buffer [state->buffer_index];
4179 if (state->mountpoint_allocated) {
4180 if (state->mountpoint_index >= state->allocated_size) {
4181 guint32 newsize = (state->allocated_size << 1) + 1;
4182 gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar));
4184 memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index);
4185 g_free (state->mountpoint_allocated);
4186 state->mountpoint_allocated = newbuf;
4187 state->allocated_size = newsize;
4189 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4190 } else {
4191 if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) {
4192 state->allocated_size = (state->mountpoint_index << 1) + 1;
4193 state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar));
4194 memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index);
4195 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4196 } else
4197 state->mountpoint [state->mountpoint_index++] = ch;
4201 static gboolean
4202 add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4204 gboolean quit = FALSE;
4205 gboolean ignore_entry;
4207 if (state->fsname_index == 1 && state->fsname [0] == '/')
4208 ignore_entry = FALSE;
4209 else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 ||
4210 memcmp ("aufs", state->fstype, state->fstype_index) == 0) {
4211 /* Don't ignore overlayfs and aufs - these might be used on Docker
4212 * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */
4213 ignore_entry = FALSE;
4214 } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) {
4215 ignore_entry = TRUE;
4216 } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) {
4217 /* Ignore GNOME's gvfs */
4218 if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0)
4219 ignore_entry = TRUE;
4220 else
4221 ignore_entry = FALSE;
4222 } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0)
4223 ignore_entry = FALSE;
4224 else
4225 ignore_entry = TRUE;
4227 if (!ignore_entry) {
4228 gunichar2 *dir;
4229 glong length;
4230 gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint;
4232 unescape_octal (mountpoint);
4233 dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL);
4234 if (state->total + length + 1 > len) {
4235 quit = TRUE;
4236 state->total = len * 2;
4237 } else {
4238 length++;
4239 memcpy (buf + state->total, dir, sizeof (gunichar2) * length);
4240 state->total += length;
4242 g_free (dir);
4244 state->fsname_index = 0;
4245 state->fstype_index = 0;
4247 return quit;
4249 #else
4250 gint32
4251 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
4253 return GetLogicalDriveStrings_Mtab (len, buf);
4255 #endif
4256 static gint32
4257 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf)
4259 FILE *fp;
4260 gunichar2 *ptr, *dir;
4261 glong length, total = 0;
4262 gchar buffer [512];
4263 gchar **splitted;
4265 memset (buf, 0, sizeof (gunichar2) * (len + 1));
4266 buf [0] = '/';
4267 buf [1] = 0;
4268 buf [2] = 0;
4270 /* Sigh, mntent and friends don't work well.
4271 * It stops on the first line that doesn't begin with a '/'.
4272 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4273 MONO_ENTER_GC_SAFE;
4274 fp = fopen ("/etc/mtab", "rt");
4275 MONO_EXIT_GC_SAFE;
4276 if (fp == NULL) {
4277 MONO_ENTER_GC_SAFE;
4278 fp = fopen ("/etc/mnttab", "rt");
4279 MONO_EXIT_GC_SAFE;
4280 if (fp == NULL)
4281 return 1;
4284 ptr = buf;
4285 while (1) {
4286 gchar *fgets_res;
4287 MONO_ENTER_GC_SAFE;
4288 fgets_res = fgets (buffer, 512, fp);
4289 MONO_EXIT_GC_SAFE;
4290 if (!fgets_res)
4291 break;
4292 if (*buffer != '/')
4293 continue;
4295 splitted = g_strsplit (buffer, " ", 0);
4296 if (!*splitted || !*(splitted + 1)) {
4297 g_strfreev (splitted);
4298 continue;
4301 unescape_octal (*(splitted + 1));
4302 dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
4303 g_strfreev (splitted);
4304 if (total + length + 1 > len) {
4305 MONO_ENTER_GC_SAFE;
4306 fclose (fp);
4307 MONO_EXIT_GC_SAFE;
4308 g_free (dir);
4309 return len * 2; /* guess */
4312 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4313 g_free (dir);
4314 total += length + 1;
4317 MONO_ENTER_GC_SAFE;
4318 fclose (fp);
4319 MONO_EXIT_GC_SAFE;
4320 return total;
4321 /* Commented out, does not work with my mtab!!! - Gonz */
4322 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4324 FILE *fp;
4325 struct mntent *mnt;
4326 gunichar2 *ptr, *dir;
4327 glong len, total = 0;
4330 MONO_ENTER_GC_SAFE;
4331 fp = setmntent ("/etc/mtab", "rt");
4332 MONO_EXIT_GC_SAFE;
4333 if (fp == NULL) {
4334 MONO_ENTER_GC_SAFE;
4335 fp = setmntent ("/etc/mnttab", "rt");
4336 MONO_EXIT_GC_SAFE;
4337 if (fp == NULL)
4338 return;
4341 ptr = buf;
4342 while (1) {
4343 MONO_ENTER_GC_SAFE;
4344 mnt = getmntent (fp);
4345 MONO_EXIT_GC_SAFE;
4346 if (mnt == NULL)
4347 break;
4348 g_print ("GOT %s\n", mnt->mnt_dir);
4349 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4350 if (total + len + 1 > len) {
4351 MONO_ENTER_GC_SAFE;
4352 endmntent (fp);
4353 MONO_EXIT_GC_SAFE;
4354 return len * 2; /* guess */
4357 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4358 g_free (dir);
4359 total += len + 1;
4362 MONO_ENTER_GC_SAFE;
4363 endmntent (fp);
4364 MONO_EXIT_GC_SAFE;
4365 return total;
4367 #endif
4369 #endif
4371 #ifndef PLATFORM_NO_DRIVEINFO
4372 gboolean
4373 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)
4375 g_assert (free_bytes_avail);
4376 g_assert (total_number_of_bytes);
4377 g_assert (total_number_of_free_bytes);
4379 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
4380 #ifdef HAVE_STATVFS
4381 struct statvfs fsstat;
4382 #elif defined(HAVE_STATFS)
4383 struct statfs fsstat;
4384 #endif
4385 gboolean isreadonly;
4386 gchar *utf8_path_name;
4387 gint ret;
4388 unsigned long block_size;
4389 ERROR_DECL (error);
4391 if (path_name == NULL) {
4392 utf8_path_name = g_strdup (g_get_current_dir());
4393 if (utf8_path_name == NULL) {
4394 mono_w32error_set_last (ERROR_DIRECTORY);
4395 return(FALSE);
4398 else {
4399 utf8_path_name = mono_unicode_to_external_checked (path_name, error);
4400 if (utf8_path_name == NULL) {
4401 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
4403 mono_error_cleanup (error);
4404 mono_w32error_set_last (ERROR_INVALID_NAME);
4405 return(FALSE);
4409 do {
4410 #ifdef HAVE_STATVFS
4411 MONO_ENTER_GC_SAFE;
4412 ret = statvfs (utf8_path_name, &fsstat);
4413 MONO_EXIT_GC_SAFE;
4414 isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
4415 block_size = fsstat.f_frsize;
4416 #elif defined(HAVE_STATFS)
4417 MONO_ENTER_GC_SAFE;
4418 ret = statfs (utf8_path_name, &fsstat);
4419 MONO_EXIT_GC_SAFE;
4420 #if defined (MNT_RDONLY)
4421 isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
4422 #elif defined (MS_RDONLY)
4423 isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY);
4424 #endif
4425 block_size = fsstat.f_bsize;
4426 #endif
4427 } while(ret == -1 && errno == EINTR);
4429 g_free(utf8_path_name);
4431 if (ret == -1) {
4432 _wapi_set_last_error_from_errno ();
4433 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: statvfs failed: %s", __func__, g_strerror (errno));
4434 return(FALSE);
4437 /* total number of free bytes for non-root */
4438 if (isreadonly)
4439 *free_bytes_avail = 0;
4440 else
4441 *free_bytes_avail = block_size * (guint64)fsstat.f_bavail;
4443 /* total number of bytes available for non-root */
4444 *total_number_of_bytes = block_size * (guint64)fsstat.f_blocks;
4446 /* total number of bytes available for root */
4447 if (isreadonly)
4448 *total_number_of_free_bytes = 0;
4449 else
4450 *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree;
4451 #endif
4452 return(TRUE);
4454 #endif // PLATFORM_NO_DRIVEINFO
4457 * General Unix support
4459 typedef struct {
4460 guint32 drive_type;
4461 #if __linux__
4462 // http://man7.org/linux/man-pages/man2/statfs.2.html
4464 // The __fsword_t type used for various fields in the statfs structure
4465 // definition is a glibc internal type, not intended for public use.
4466 // This leaves the programmer in a bit of a conundrum when trying to
4467 // copy or compare these fields to local variables in a program. Using
4468 // unsigned int for such variables suffices on most systems.
4470 // Let's hope "most" is enough, and that it works with other libc.
4471 unsigned fstypeid;
4472 #endif
4473 const gchar* fstype;
4474 } _wapi_drive_type;
4476 static const _wapi_drive_type _wapi_drive_types[] = {
4477 #if HOST_DARWIN
4478 { DRIVE_REMOTE, "afp" },
4479 { DRIVE_REMOTE, "afpfs" },
4480 { DRIVE_REMOTE, "autofs" },
4481 { DRIVE_CDROM, "cddafs" },
4482 { DRIVE_CDROM, "cd9660" },
4483 { DRIVE_RAMDISK, "devfs" },
4484 { DRIVE_RAMDISK, "nullfs" },
4485 { DRIVE_FIXED, "exfat" },
4486 { DRIVE_RAMDISK, "fdesc" },
4487 { DRIVE_REMOTE, "ftp" },
4488 { DRIVE_FIXED, "hfs" },
4489 { DRIVE_FIXED, "apfs" },
4490 { DRIVE_REMOTE, "kbfuse" },
4491 { DRIVE_FIXED, "msdos" },
4492 { DRIVE_REMOTE, "nfs" },
4493 { DRIVE_FIXED, "ntfs" },
4494 { DRIVE_REMOTE, "smbfs" },
4495 { DRIVE_FIXED, "udf" },
4496 { DRIVE_REMOTE, "webdav" },
4497 { DRIVE_FIXED, "ufsd_NTFS"},
4498 { DRIVE_UNKNOWN, NULL }
4499 #elif __linux__
4500 { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"},
4501 { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"},
4502 { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"},
4503 { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"},
4504 { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"},
4505 { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" },
4506 { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"},
4507 { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"},
4508 { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"},
4509 { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"},
4510 { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"},
4511 { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"},
4512 { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"},
4513 { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"},
4514 { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"},
4515 { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"},
4516 { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"},
4517 { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"},
4518 { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"},
4519 { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"},
4520 { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"},
4521 { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"},
4522 { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"},
4523 { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"},
4524 { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"},
4525 { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"},
4526 { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"},
4527 { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"},
4528 { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"},
4529 { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"},
4530 { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"},
4531 { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"},
4532 { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"},
4533 { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"},
4534 { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"},
4535 { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"},
4536 { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"},
4537 { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"},
4538 { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"},
4539 { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"},
4540 { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"},
4541 { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"},
4542 { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"},
4543 { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"},
4544 { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"},
4545 { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"},
4546 { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"},
4547 { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"},
4548 { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"},
4549 { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"},
4550 { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"},
4551 { DRIVE_FIXED, UFS_MAGIC, "ufs"},
4552 { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"},
4553 { DRIVE_FIXED, UFS2_MAGIC, "ufs2"},
4554 { DRIVE_FIXED, UFS_CIGAM, "ufs"},
4555 { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"},
4556 { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"},
4557 { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"},
4558 { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"},
4559 { DRIVE_FIXED, V9FS_MAGIC, "9p"},
4560 { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"},
4561 { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"},
4562 { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"},
4563 { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"},
4564 { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"},
4565 { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"},
4566 { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"},
4567 { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"},
4568 { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"},
4569 { DRIVE_FIXED, OMFS_MAGIC, "omfs"},
4570 { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"},
4571 { DRIVE_UNKNOWN, 0, NULL}
4572 #else
4573 { DRIVE_RAMDISK, "ramfs" },
4574 { DRIVE_RAMDISK, "tmpfs" },
4575 { DRIVE_RAMDISK, "proc" },
4576 { DRIVE_RAMDISK, "sysfs" },
4577 { DRIVE_RAMDISK, "debugfs" },
4578 { DRIVE_RAMDISK, "devpts" },
4579 { DRIVE_RAMDISK, "securityfs" },
4580 { DRIVE_RAMDISK, "procfs" }, // AIX procfs
4581 { DRIVE_RAMDISK, "namefs" }, // AIX soft mounts
4582 { DRIVE_RAMDISK, "nullfs" },
4583 { DRIVE_CDROM, "iso9660" },
4584 { DRIVE_CDROM, "cdrfs" }, // AIX ISO9660 CDs
4585 { DRIVE_CDROM, "udfs" }, // AIX UDF CDs
4586 { DRIVE_CDROM, "QOPT" }, // IBM i CD mount
4587 { DRIVE_FIXED, "ext2" },
4588 { DRIVE_FIXED, "ext3" },
4589 { DRIVE_FIXED, "ext4" },
4590 { DRIVE_FIXED, "sysv" },
4591 { DRIVE_FIXED, "reiserfs" },
4592 { DRIVE_FIXED, "ufs" },
4593 { DRIVE_FIXED, "vfat" },
4594 { DRIVE_FIXED, "msdos" },
4595 { DRIVE_FIXED, "udf" },
4596 { DRIVE_FIXED, "hfs" },
4597 { DRIVE_FIXED, "hpfs" },
4598 { DRIVE_FIXED, "qnx4" },
4599 { DRIVE_FIXED, "ntfs" },
4600 { DRIVE_FIXED, "ntfs-3g" },
4601 { DRIVE_FIXED, "jfs" }, // IBM JFS
4602 { DRIVE_FIXED, "jfs2" }, // IBM JFS (AIX defalt filesystem)
4603 { DRIVE_FIXED, "EPFS" }, // IBM i IFS (root and QOpenSys)
4604 { DRIVE_FIXED, "EPFSP" }, // IBM i auxiliary storage pool FS
4605 { DRIVE_FIXED, "QSYS" }, // IBM i native system libraries
4606 { DRIVE_FIXED, "QDLS" }, // IBM i legacy S/36 directories
4607 { DRIVE_REMOTE, "smbfs" },
4608 { DRIVE_REMOTE, "fuse" },
4609 { DRIVE_REMOTE, "nfs" },
4610 { DRIVE_REMOTE, "nfs4" },
4611 { DRIVE_REMOTE, "cifs" },
4612 { DRIVE_REMOTE, "ncpfs" },
4613 { DRIVE_REMOTE, "coda" },
4614 { DRIVE_REMOTE, "afs" },
4615 { DRIVE_REMOTE, "nfs3" },
4616 { DRIVE_REMOTE, "stnfs" }, // AIX "short-term" NFS
4617 { DRIVE_REMOTE, "autofs" }, // AIX automounter NFS
4618 { DRIVE_REMOTE, "cachefs" }, // AIX cached NFS
4619 { DRIVE_REMOTE, "NFS" }, // IBM i NFS
4620 { DRIVE_REMOTE, "QNETC" }, // IBM i CIFS
4621 { DRIVE_REMOTE, "QRFS" }, // IBM i native remote FS
4622 { DRIVE_UNKNOWN, NULL }
4623 #endif
4626 #if __linux__
4627 static guint32 _wapi_get_drive_type(unsigned f_type)
4629 const _wapi_drive_type *current;
4631 current = &_wapi_drive_types[0];
4632 while (current->drive_type != DRIVE_UNKNOWN) {
4633 if (current->fstypeid == f_type)
4634 return current->drive_type;
4635 current++;
4638 return DRIVE_UNKNOWN;
4640 #else
4641 static guint32 _wapi_get_drive_type(const gchar* fstype)
4643 const _wapi_drive_type *current;
4645 current = &_wapi_drive_types[0];
4646 while (current->drive_type != DRIVE_UNKNOWN) {
4647 if (strcmp (current->fstype, fstype) == 0)
4648 return current->drive_type;
4650 current++;
4653 return DRIVE_UNKNOWN;
4655 #endif
4657 #if defined (HOST_DARWIN) || defined (__linux__) || defined (_AIX)
4658 static guint32
4659 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4661 #if defined (_AIX)
4662 struct statvfs buf;
4663 #else
4664 struct statfs buf;
4665 #endif
4666 gint res;
4668 MONO_ENTER_GC_SAFE;
4669 #if defined (_AIX)
4670 res = statvfs (utf8_root_path_name, &buf);
4671 #else
4672 res = statfs (utf8_root_path_name, &buf);
4673 #endif
4674 MONO_EXIT_GC_SAFE;
4675 if (res == -1)
4676 return DRIVE_UNKNOWN;
4677 #if HOST_DARWIN
4678 return _wapi_get_drive_type (buf.f_fstypename);
4679 #elif defined (_AIX)
4680 return _wapi_get_drive_type (buf.f_basetype);
4681 #else
4682 return _wapi_get_drive_type (buf.f_type);
4683 #endif
4685 #else
4686 static guint32
4687 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4689 guint32 drive_type;
4690 FILE *fp;
4691 gchar buffer [512];
4692 gchar **splitted;
4694 MONO_ENTER_GC_SAFE;
4695 fp = fopen ("/etc/mtab", "rt");
4696 MONO_EXIT_GC_SAFE;
4697 if (fp == NULL) {
4698 MONO_ENTER_GC_SAFE;
4699 fp = fopen ("/etc/mnttab", "rt");
4700 MONO_EXIT_GC_SAFE;
4701 if (fp == NULL)
4702 return(DRIVE_UNKNOWN);
4705 drive_type = DRIVE_NO_ROOT_DIR;
4706 while (1) {
4707 gchar *fgets_res;
4708 MONO_ENTER_GC_SAFE;
4709 fgets_res = fgets (buffer, 512, fp);
4710 MONO_EXIT_GC_SAFE;
4711 if (fgets_res == NULL)
4712 break;
4713 splitted = g_strsplit (buffer, " ", 0);
4714 if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
4715 g_strfreev (splitted);
4716 continue;
4719 /* compare given root_path_name with the one from mtab,
4720 if length of utf8_root_path_name is zero it must be the root dir */
4721 if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
4722 (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
4723 drive_type = _wapi_get_drive_type (*(splitted + 2));
4724 /* it is possible this path might be mounted again with
4725 a known type...keep looking */
4726 if (drive_type != DRIVE_UNKNOWN) {
4727 g_strfreev (splitted);
4728 break;
4732 g_strfreev (splitted);
4735 MONO_ENTER_GC_SAFE;
4736 fclose (fp);
4737 MONO_EXIT_GC_SAFE;
4738 return drive_type;
4740 #endif
4742 #ifndef ENABLE_NETCORE
4743 guint32
4744 ves_icall_System_IO_DriveInfo_GetDriveType (const gunichar2 *root_path_name, gint32 root_path_name_length, MonoError *error)
4746 // FIXME Check for embedded nuls here or in managed.
4748 gchar *utf8_root_path_name;
4749 guint32 drive_type;
4751 if (root_path_name == NULL) {
4752 utf8_root_path_name = g_strdup (g_get_current_dir());
4753 if (utf8_root_path_name == NULL) {
4754 return(DRIVE_NO_ROOT_DIR);
4757 else {
4758 utf8_root_path_name = mono_unicode_to_external_checked (root_path_name, error);
4759 if (utf8_root_path_name == NULL) {
4760 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
4761 return(DRIVE_NO_ROOT_DIR);
4764 /* strip trailing slash for compare below */
4765 if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) {
4766 utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
4769 drive_type = GetDriveTypeFromPath (utf8_root_path_name);
4770 g_free (utf8_root_path_name);
4772 return (drive_type);
4774 #endif
4776 #if defined (HOST_DARWIN) || defined (__linux__) || defined(HOST_BSD) || defined(__FreeBSD_kernel__) || defined(__HAIKU__) || defined(_AIX)
4777 static gchar*
4778 get_fstypename (gchar *utfpath)
4780 #if defined (_AIX)
4781 /* statvfs offers the FS type name, easily, no need to iterate */
4782 struct statvfs stat;
4783 gint statvfs_res;
4785 MONO_ENTER_GC_SAFE;
4786 statvfs_res = statvfs (utfpath, &stat);
4787 MONO_EXIT_GC_SAFE;
4789 if (statvfs_res != -1) {
4790 return g_strdup (stat.f_basetype);
4793 return NULL;
4794 #elif defined (HOST_DARWIN) || defined (__linux__)
4795 struct statfs stat;
4796 #if __linux__
4797 const _wapi_drive_type *current;
4798 #endif
4799 gint statfs_res;
4800 MONO_ENTER_GC_SAFE;
4801 statfs_res = statfs (utfpath, &stat);
4802 MONO_EXIT_GC_SAFE;
4803 if (statfs_res == -1)
4804 return NULL;
4805 #if HOST_DARWIN
4806 return g_strdup (stat.f_fstypename);
4807 #else
4808 current = &_wapi_drive_types[0];
4809 while (current->drive_type != DRIVE_UNKNOWN) {
4810 if (stat.f_type == current->fstypeid)
4811 return g_strdup (current->fstype);
4812 current++;
4814 return NULL;
4815 #endif
4816 #else
4817 return NULL;
4818 #endif
4821 /* Linux has struct statfs which has a different layout */
4822 gboolean
4823 mono_w32file_get_file_system_type (const gunichar2 *path, gunichar2 *fsbuffer, gint fsbuffersize)
4825 gchar *utfpath;
4826 gchar *fstypename;
4827 gboolean status = FALSE;
4828 glong len;
4830 // We only support getting the file system type
4831 if (fsbuffer == NULL)
4832 return 0;
4834 utfpath = mono_unicode_to_external (path);
4835 if ((fstypename = get_fstypename (utfpath)) != NULL){
4836 gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL);
4837 if (ret != NULL && len < fsbuffersize){
4838 memcpy (fsbuffer, ret, len * sizeof (gunichar2));
4839 fsbuffer [len] = 0;
4840 status = TRUE;
4842 if (ret != NULL)
4843 g_free (ret);
4844 g_free (fstypename);
4846 g_free (utfpath);
4847 return status;
4849 #endif
4851 static gboolean
4852 LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4854 FileHandle *filehandle;
4855 gboolean ret;
4856 off_t offset, length;
4858 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
4859 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4860 return FALSE;
4863 if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_FILE) {
4864 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4865 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4866 return FALSE;
4869 if (!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
4870 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
4871 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4872 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4873 return FALSE;
4876 #ifdef HAVE_LARGE_FILE_SUPPORT
4877 offset = ((gint64)offset_high << 32) | offset_low;
4878 length = ((gint64)length_high << 32) | length_low;
4880 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Locking fd %d, offset %" G_GINT64_FORMAT ", length %" G_GINT64_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, (gint64) offset, (gint64) length);
4881 #else
4882 if (offset_high > 0 || length_high > 0) {
4883 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
4884 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4885 return FALSE;
4888 offset = offset_low;
4889 length = length_low;
4891 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Locking fd %d, offset %" G_GINT64_FORMAT ", length %" G_GINT64_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, (gint64) offset, (gint64) length);
4892 #endif
4894 ret = _wapi_lock_file_region (((MonoFDHandle*) filehandle)->fd, offset, length);
4896 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4897 return ret;
4900 static gboolean
4901 UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4903 FileHandle *filehandle;
4904 gboolean ret;
4905 off_t offset, length;
4907 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
4908 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4909 return FALSE;
4912 if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_FILE) {
4913 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4914 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4915 return FALSE;
4918 if (!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
4919 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, ((MonoFDHandle*) filehandle)->fd, filehandle->fileaccess);
4920 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4921 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4922 return FALSE;
4925 #ifdef HAVE_LARGE_FILE_SUPPORT
4926 offset = ((gint64)offset_high << 32) | offset_low;
4927 length = ((gint64)length_high << 32) | length_low;
4928 #else
4929 offset = offset_low;
4930 length = length_low;
4931 #endif
4932 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unlocking fd %d, offset %" G_GINT64_FORMAT ", length %" G_GINT64_FORMAT, __func__, ((MonoFDHandle*) filehandle)->fd, (gint64) offset, (gint64) length);
4934 ret = _wapi_unlock_file_region (((MonoFDHandle*) filehandle)->fd, offset, length);
4936 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4937 return ret;
4940 void
4941 mono_w32file_init (void)
4943 MonoFDHandleCallback file_data_callbacks;
4944 memset (&file_data_callbacks, 0, sizeof (file_data_callbacks));
4945 file_data_callbacks.close = file_data_close;
4946 file_data_callbacks.destroy = file_data_destroy;
4948 mono_fdhandle_register (MONO_FDTYPE_FILE, &file_data_callbacks);
4949 mono_fdhandle_register (MONO_FDTYPE_CONSOLE, &file_data_callbacks);
4950 mono_fdhandle_register (MONO_FDTYPE_PIPE, &file_data_callbacks);
4952 mono_coop_mutex_init (&file_share_mutex);
4954 finds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, finds_remove);
4955 mono_coop_mutex_init (&finds_mutex);
4957 #if HOST_DARWIN
4958 libc_handle = mono_dl_open ("/usr/lib/libc.dylib", 0, NULL);
4959 g_assert (libc_handle);
4960 g_free (mono_dl_symbol (libc_handle, "clonefile", (void**)&clonefile_ptr));
4961 #endif
4963 if (g_hasenv ("MONO_STRICT_IO_EMULATION"))
4964 lock_while_writing = TRUE;
4967 void
4968 mono_w32file_cleanup (void)
4970 mono_coop_mutex_destroy (&file_share_mutex);
4972 if (file_share_table)
4973 g_hash_table_destroy (file_share_table);
4975 g_hash_table_destroy (finds);
4976 mono_coop_mutex_destroy (&finds_mutex);
4978 #if HOST_DARWIN
4979 mono_dl_close (libc_handle);
4980 #endif
4983 gboolean
4984 mono_w32file_move (const gunichar2 *path, const gunichar2 *dest, gint32 *error)
4986 gboolean result;
4988 result = MoveFile (path, dest);
4989 if (!result)
4990 *error = mono_w32error_get_last ();
4991 return result;
4994 gboolean
4995 mono_w32file_copy (const gunichar2 *path, const gunichar2 *dest, gboolean overwrite, gint32 *error)
4997 gboolean result;
4999 result = CopyFile (path, dest, !overwrite);
5000 if (!result)
5001 *error = mono_w32error_get_last ();
5003 return result;
5006 gboolean
5007 mono_w32file_replace (const gunichar2 *destination_file_name, const gunichar2 *source_file_name, const gunichar2 *destination_backup_file_name, guint32 flags, gint32 *error)
5009 gboolean result;
5011 result = ReplaceFile (destination_file_name, source_file_name, destination_backup_file_name, flags, NULL, NULL);
5012 if (!result)
5013 *error = mono_w32error_get_last ();
5014 return result;
5017 gint64
5018 mono_w32file_get_file_size (gpointer handle, gint32 *error)
5020 gint64 length;
5021 guint32 length_hi = 0;
5023 length = GetFileSize (handle, &length_hi);
5024 if(length==INVALID_FILE_SIZE) {
5025 *error=mono_w32error_get_last ();
5028 return length | ((gint64)length_hi << 32);
5031 gboolean
5032 mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error)
5034 gboolean result;
5036 result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
5037 if (!result)
5038 *error = mono_w32error_get_last ();
5039 return result;
5042 gboolean
5043 mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error)
5045 gboolean result;
5047 result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
5048 if (!result)
5049 *error = mono_w32error_get_last ();
5050 return result;
5053 gpointer
5054 mono_w32file_get_console_input (void)
5056 return mono_w32file_get_std_handle (STD_INPUT_HANDLE);
5059 gpointer
5060 mono_w32file_get_console_output (void)
5062 return mono_w32file_get_std_handle (STD_OUTPUT_HANDLE);
5065 gpointer
5066 mono_w32file_get_console_error (void)
5068 return mono_w32file_get_std_handle (STD_ERROR_HANDLE);