Naming: err -> error
[mono-project.git] / mono / metadata / w32file-unix.c
blob184165554a9e418d4201192d10f7a6d528021834
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 == 0) {
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);
1877 if (((file_existing_access & GENERIC_READ) &&
1878 !(sharemode & FILE_SHARE_READ)) ||
1879 ((file_existing_access & GENERIC_WRITE) &&
1880 !(sharemode & FILE_SHARE_WRITE))) {
1881 /* New share mode doesn't match up */
1882 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Access mode prevents open: requested share: 0x%" PRIx32 ", file has access: 0x%" PRIx32, __func__, sharemode, file_existing_access);
1884 file_share_release (*share_info);
1885 *share_info = NULL;
1887 return(FALSE);
1889 } else {
1890 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: New file!", __func__);
1893 return(TRUE);
1897 static gboolean
1898 share_allows_delete (struct stat *statbuf, FileShare **share_info)
1900 gboolean file_already_shared;
1901 guint32 file_existing_share, file_existing_access;
1903 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);
1905 if (file_already_shared) {
1906 /* The reference to this share info was incremented
1907 * when we looked it up, so be careful to put it back
1908 * if we conclude we can't use this file.
1910 if (file_existing_share == 0) {
1911 /* Quick and easy, no possibility to share */
1912 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);
1914 file_share_release (*share_info);
1915 *share_info = NULL;
1917 return(FALSE);
1920 if (!(file_existing_share & FILE_SHARE_DELETE)) {
1921 /* New access mode doesn't match up */
1922 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);
1924 file_share_release (*share_info);
1925 *share_info = NULL;
1927 return(FALSE);
1929 } else {
1930 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: New file!", __func__);
1933 return(TRUE);
1936 gpointer
1937 mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs)
1939 FileHandle *filehandle;
1940 MonoFDType type;
1941 gint flags=convert_flags(fileaccess, createmode);
1942 /*mode_t perms=convert_perms(sharemode);*/
1943 /* we don't use sharemode, because that relates to sharing of
1944 * the file when the file is open and is already handled by
1945 * other code, perms instead are the on-disk permissions and
1946 * this is a sane default.
1948 mode_t perms=0666;
1949 gchar *filename;
1950 gint fd, ret;
1951 struct stat statbuf;
1952 ERROR_DECL (error);
1954 if (attrs & FILE_ATTRIBUTE_TEMPORARY)
1955 perms = 0600;
1957 if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
1958 mono_w32error_set_last (ERROR_ENCRYPTION_FAILED);
1959 return INVALID_HANDLE_VALUE;
1962 if (name == NULL) {
1963 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
1965 mono_w32error_set_last (ERROR_INVALID_NAME);
1966 return(INVALID_HANDLE_VALUE);
1969 filename = mono_unicode_to_external_checked (name, error);
1970 if (filename == NULL) {
1971 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
1973 mono_error_cleanup (error);
1974 mono_w32error_set_last (ERROR_INVALID_NAME);
1975 return(INVALID_HANDLE_VALUE);
1978 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Opening %s with share 0x%" PRIx32 " and access 0x%" PRIx32, __func__,
1979 filename, sharemode, fileaccess);
1981 fd = _wapi_open (filename, flags, perms);
1983 /* If we were trying to open a directory with write permissions
1984 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1985 * EISDIR. However, this is a bit bogus because calls to
1986 * manipulate the directory (e.g. mono_w32file_set_times) will still work on
1987 * the directory because they use other API calls
1988 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1989 * to open the directory again without write permission.
1991 if (fd == -1 && errno == EISDIR)
1993 /* Try again but don't try to make it writable */
1994 fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
1997 if (fd == -1) {
1998 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Error opening file %s: %s", __func__, filename, g_strerror(errno));
1999 _wapi_set_last_path_error_from_errno (NULL, filename);
2000 g_free (filename);
2002 return(INVALID_HANDLE_VALUE);
2005 MONO_ENTER_GC_SAFE;
2006 ret = fstat (fd, &statbuf);
2007 MONO_EXIT_GC_SAFE;
2008 if (ret == -1) {
2009 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fstat error of file %s: %s", __func__, filename, g_strerror (errno));
2010 _wapi_set_last_error_from_errno ();
2011 MONO_ENTER_GC_SAFE;
2012 close (fd);
2013 MONO_EXIT_GC_SAFE;
2015 return(INVALID_HANDLE_VALUE);
2018 #ifndef S_ISFIFO
2019 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
2020 #endif
2021 if (S_ISFIFO (statbuf.st_mode)) {
2022 type = MONO_FDTYPE_PIPE;
2023 /* maintain invariant that pipes have no filename */
2024 g_free (filename);
2025 filename = NULL;
2026 } else if (S_ISCHR (statbuf.st_mode)) {
2027 type = MONO_FDTYPE_CONSOLE;
2028 } else {
2029 type = MONO_FDTYPE_FILE;
2032 filehandle = file_data_create (type, fd);
2033 filehandle->filename = filename;
2034 filehandle->fileaccess = fileaccess;
2035 filehandle->sharemode = sharemode;
2036 filehandle->attrs = attrs;
2038 if (!share_allows_open (&statbuf, filehandle->sharemode, filehandle->fileaccess, &filehandle->share_info)) {
2039 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2040 MONO_ENTER_GC_SAFE;
2041 close (((MonoFDHandle*) filehandle)->fd);
2042 MONO_EXIT_GC_SAFE;
2044 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2045 return (INVALID_HANDLE_VALUE);
2047 if (!filehandle->share_info) {
2048 /* No space, so no more files can be opened */
2049 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: No space in the share table", __func__);
2051 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
2052 MONO_ENTER_GC_SAFE;
2053 close (((MonoFDHandle*) filehandle)->fd);
2054 MONO_EXIT_GC_SAFE;
2056 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2057 return(INVALID_HANDLE_VALUE);
2060 #ifdef HAVE_POSIX_FADVISE
2061 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) {
2062 MONO_ENTER_GC_SAFE;
2063 posix_fadvise (((MonoFDHandle*) filehandle)->fd, 0, 0, POSIX_FADV_SEQUENTIAL);
2064 MONO_EXIT_GC_SAFE;
2066 if (attrs & FILE_FLAG_RANDOM_ACCESS) {
2067 MONO_ENTER_GC_SAFE;
2068 posix_fadvise (((MonoFDHandle*) filehandle)->fd, 0, 0, POSIX_FADV_RANDOM);
2069 MONO_EXIT_GC_SAFE;
2071 #endif
2073 #ifdef F_RDAHEAD
2074 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) {
2075 MONO_ENTER_GC_SAFE;
2076 fcntl(((MonoFDHandle*) filehandle)->fd, F_RDAHEAD, 1);
2077 MONO_EXIT_GC_SAFE;
2079 #endif
2081 mono_fdhandle_insert ((MonoFDHandle*) filehandle);
2083 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: returning handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd));
2085 return GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd);
2088 gboolean
2089 mono_w32file_close (gpointer handle)
2091 if (!mono_fdhandle_close (GPOINTER_TO_INT (handle))) {
2092 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2093 return FALSE;
2096 return TRUE;
2099 gboolean mono_w32file_delete(const gunichar2 *name)
2101 gchar *filename;
2102 gint retval;
2103 gboolean ret = FALSE;
2104 ERROR_DECL (error);
2105 #if 0
2106 struct stat statbuf;
2107 FileShare *shareinfo;
2108 #endif
2110 if(name==NULL) {
2111 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2113 mono_w32error_set_last (ERROR_INVALID_NAME);
2114 return(FALSE);
2117 filename = mono_unicode_to_external_checked (name, error);
2118 if(filename==NULL) {
2119 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
2121 mono_error_cleanup (error);
2122 mono_w32error_set_last (ERROR_INVALID_NAME);
2123 return(FALSE);
2126 #if 0
2127 /* Check to make sure sharing allows us to open the file for
2128 * writing. See bug 323389.
2130 * Do the checks that don't need an open file descriptor, for
2131 * simplicity's sake. If we really have to do the full checks
2132 * then we can implement that later.
2134 if (_wapi_stat (filename, &statbuf) < 0) {
2135 _wapi_set_last_path_error_from_errno (NULL, filename);
2136 g_free (filename);
2137 return(FALSE);
2140 if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
2141 &shareinfo) == FALSE) {
2142 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2143 g_free (filename);
2144 return FALSE;
2146 if (shareinfo)
2147 file_share_release (shareinfo);
2148 #endif
2150 retval = _wapi_unlink (filename);
2152 if (retval == -1) {
2153 /* On linux, calling unlink on an non-existing file in a read-only mount will fail with EROFS.
2154 * The expected behavior is for this function to return FALSE and not trigger an exception.
2155 * To work around this behavior, we stat the file on failure.
2157 * This was supposedly fixed on kernel 3.0 [1] but we could reproduce it with Ubuntu 16.04 which has kernel 4.4.
2158 * We can't remove this workaround until the early 2020's when most Android deviced will have a fix.
2159 * [1] https://github.com/torvalds/linux/commit/50338b889dc504c69e0cb316ac92d1b9e51f3c8a
2161 if (errno == EROFS) {
2162 MonoIOStat stat;
2163 if (mono_w32file_get_attributes_ex (name, &stat)) //The file exists, so must be due the RO file system
2164 mono_set_errno (EROFS);
2166 _wapi_set_last_path_error_from_errno (NULL, filename);
2167 } else {
2168 ret = TRUE;
2171 g_free(filename);
2173 return(ret);
2176 static gboolean
2177 MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
2179 gchar *utf8_name, *utf8_dest_name;
2180 gint result, errno_copy;
2181 struct stat stat_src, stat_dest;
2182 gboolean ret = FALSE;
2183 FileShare *shareinfo;
2184 ERROR_DECL (error);
2186 if(name==NULL) {
2187 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2189 mono_w32error_set_last (ERROR_INVALID_NAME);
2190 return(FALSE);
2193 utf8_name = mono_unicode_to_external_checked (name, error);
2194 if (utf8_name == NULL) {
2195 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
2197 mono_error_cleanup (error);
2198 mono_w32error_set_last (ERROR_INVALID_NAME);
2199 return FALSE;
2202 if(dest_name==NULL) {
2203 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2205 g_free (utf8_name);
2206 mono_w32error_set_last (ERROR_INVALID_NAME);
2207 return(FALSE);
2210 utf8_dest_name = mono_unicode_to_external_checked (dest_name, error);
2211 if (utf8_dest_name == NULL) {
2212 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
2214 mono_error_cleanup (error);
2215 g_free (utf8_name);
2216 mono_w32error_set_last (ERROR_INVALID_NAME);
2217 return FALSE;
2221 * In C# land we check for the existence of src, but not for dest.
2222 * We check it here and return the failure if dest exists and is not
2223 * the same file as src.
2225 if (_wapi_stat (utf8_name, &stat_src) < 0) {
2226 if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
2227 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
2228 g_free (utf8_name);
2229 g_free (utf8_dest_name);
2230 return FALSE;
2234 if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
2235 if (stat_dest.st_dev != stat_src.st_dev ||
2236 stat_dest.st_ino != stat_src.st_ino) {
2237 g_free (utf8_name);
2238 g_free (utf8_dest_name);
2239 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2240 return FALSE;
2244 /* Check to make that we have delete sharing permission.
2245 * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
2247 * Do the checks that don't need an open file descriptor, for
2248 * simplicity's sake. If we really have to do the full checks
2249 * then we can implement that later.
2251 if (share_allows_delete (&stat_src, &shareinfo) == FALSE) {
2252 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2253 return FALSE;
2255 if (shareinfo) {
2256 file_share_release (shareinfo);
2257 shareinfo = NULL;
2260 result = _wapi_rename (utf8_name, utf8_dest_name);
2261 errno_copy = errno;
2263 if (result == -1) {
2264 switch(errno_copy) {
2265 case EEXIST:
2266 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2267 break;
2269 case EXDEV:
2270 /* Ignore here, it is dealt with below */
2271 break;
2273 case ENOENT:
2274 /* We already know src exists. Must be dest that doesn't exist. */
2275 _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name);
2276 break;
2278 default:
2279 _wapi_set_last_error_from_errno ();
2283 g_free (utf8_name);
2284 g_free (utf8_dest_name);
2286 if (result != 0 && errno_copy == EXDEV) {
2287 gint32 copy_error;
2289 if (S_ISDIR (stat_src.st_mode)) {
2290 mono_w32error_set_last (ERROR_NOT_SAME_DEVICE);
2291 return FALSE;
2293 /* Try a copy to the new location, and delete the source */
2294 if (!mono_w32file_copy (name, dest_name, FALSE, &copy_error)) {
2295 /* mono_w32file_copy will set the error */
2296 return(FALSE);
2299 return(mono_w32file_delete (name));
2302 if (result == 0) {
2303 ret = TRUE;
2306 return(ret);
2309 static gboolean
2310 write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_errors)
2312 gint remain, n;
2313 gchar *buf, *wbuf;
2314 gint buf_size = st_src->st_blksize;
2315 MonoThreadInfo *info = mono_thread_info_current ();
2317 buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
2318 buf = (gchar *) g_malloc (buf_size);
2320 for (;;) {
2321 MONO_ENTER_GC_SAFE;
2322 remain = read (src_fd, buf, buf_size);
2323 MONO_EXIT_GC_SAFE;
2324 if (remain < 0) {
2325 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2326 continue;
2328 if (report_errors)
2329 _wapi_set_last_error_from_errno ();
2331 g_free (buf);
2332 return FALSE;
2334 if (remain == 0) {
2335 break;
2338 wbuf = buf;
2339 while (remain > 0) {
2340 MONO_ENTER_GC_SAFE;
2341 n = write (dest_fd, wbuf, remain);
2342 MONO_EXIT_GC_SAFE;
2343 if (n < 0) {
2344 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2345 continue;
2347 if (report_errors)
2348 _wapi_set_last_error_from_errno ();
2349 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write failed.", __func__);
2350 g_free (buf);
2351 return FALSE;
2354 remain -= n;
2355 wbuf += n;
2359 g_free (buf);
2360 return TRUE ;
2363 #if HOST_DARWIN
2364 static int
2365 _wapi_clonefile(const char *from, const char *to, int flags)
2367 gchar *located_from, *located_to;
2368 int ret;
2370 g_assert (clonefile_ptr != NULL);
2372 located_from = mono_portability_find_file (from, FALSE);
2373 located_to = mono_portability_find_file (to, FALSE);
2375 MONO_ENTER_GC_SAFE;
2376 ret = clonefile_ptr (
2377 located_from == NULL ? from : located_from,
2378 located_to == NULL ? to : located_to,
2379 flags);
2380 MONO_EXIT_GC_SAFE;
2382 g_free (located_from);
2383 g_free (located_to);
2385 return ret;
2387 #endif
2389 static gboolean
2390 CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists)
2392 gchar *utf8_src, *utf8_dest;
2393 struct stat st, dest_st;
2394 gboolean ret = TRUE;
2395 ERROR_DECL (error);
2397 if(name==NULL) {
2398 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2400 mono_w32error_set_last (ERROR_INVALID_NAME);
2401 return(FALSE);
2404 utf8_src = mono_unicode_to_external_checked (name, error);
2405 if (utf8_src == NULL) {
2406 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of source returned NULL; %s",
2407 __func__, mono_error_get_message (error));
2409 mono_error_cleanup (error);
2410 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2411 return(FALSE);
2414 if(dest_name==NULL) {
2415 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: dest is NULL", __func__);
2417 g_free (utf8_src);
2418 mono_w32error_set_last (ERROR_INVALID_NAME);
2419 return(FALSE);
2422 utf8_dest = mono_unicode_to_external_checked (dest_name, error);
2423 if (utf8_dest == NULL) {
2424 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of dest returned NULL; %s",
2425 __func__, mono_error_get_message (error));
2427 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2429 mono_error_cleanup (error);
2430 g_free (utf8_src);
2432 return(FALSE);
2435 gint src_fd, dest_fd;
2436 gint ret_utime;
2437 gint syscall_res;
2439 src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2440 if (src_fd < 0) {
2441 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2443 g_free (utf8_src);
2444 g_free (utf8_dest);
2446 return(FALSE);
2449 MONO_ENTER_GC_SAFE;
2450 syscall_res = fstat (src_fd, &st);
2451 MONO_EXIT_GC_SAFE;
2452 if (syscall_res < 0) {
2453 _wapi_set_last_error_from_errno ();
2455 g_free (utf8_src);
2456 g_free (utf8_dest);
2457 MONO_ENTER_GC_SAFE;
2458 close (src_fd);
2459 MONO_EXIT_GC_SAFE;
2461 return(FALSE);
2464 if (!_wapi_stat (utf8_dest, &dest_st)) {
2465 /* Before trying to open/create the dest, we need to report a 'file busy'
2466 * error if src and dest are actually the same file. We do the check here to take
2467 * advantage of the IOMAP capability */
2468 if (st.st_dev == dest_st.st_dev && st.st_ino == dest_st.st_ino) {
2469 g_free (utf8_src);
2470 g_free (utf8_dest);
2471 MONO_ENTER_GC_SAFE;
2472 close (src_fd);
2473 MONO_EXIT_GC_SAFE;
2475 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2476 return (FALSE);
2479 /* Take advantage of the fact that we already know the file exists and bail out
2480 * early */
2481 if (fail_if_exists) {
2482 g_free (utf8_src);
2483 g_free (utf8_dest);
2484 MONO_ENTER_GC_SAFE;
2485 close (src_fd);
2486 MONO_EXIT_GC_SAFE;
2488 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2489 return (FALSE);
2492 #if HOST_DARWIN
2493 /* If we attempt to use clonefile API we need to unlink the destination file
2494 * first */
2495 if (clonefile_ptr != NULL) {
2497 /* Bail out if the destination is read-only */
2498 if (!is_file_writable (&dest_st, utf8_dest)) {
2499 g_free (utf8_src);
2500 g_free (utf8_dest);
2501 MONO_ENTER_GC_SAFE;
2502 close (src_fd);
2503 MONO_EXIT_GC_SAFE;
2505 mono_w32error_set_last (ERROR_ACCESS_DENIED);
2506 return (FALSE);
2509 _wapi_unlink (utf8_dest);
2511 #endif
2514 #if HOST_DARWIN
2515 if (clonefile_ptr != NULL) {
2516 ret = _wapi_clonefile (utf8_src, utf8_dest, 0);
2517 if (ret == 0 || (errno != ENOTSUP && errno != EXDEV)) {
2518 g_free (utf8_src);
2519 g_free (utf8_dest);
2520 MONO_ENTER_GC_SAFE;
2521 close (src_fd);
2522 MONO_EXIT_GC_SAFE;
2524 if (ret == 0) {
2525 return (TRUE);
2526 } else {
2527 _wapi_set_last_error_from_errno ();
2528 return (FALSE);
2532 #endif
2534 if (fail_if_exists) {
2535 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
2536 } else {
2537 /* FIXME: it kinda sucks that this code path potentially scans
2538 * the directory twice due to the weird mono_w32error_set_last()
2539 * behavior. */
2540 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2541 if (dest_fd < 0) {
2542 /* The file does not exist, try creating it */
2543 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
2544 } else {
2545 /* Apparently this error is set if we
2546 * overwrite the dest file
2548 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2551 if (dest_fd < 0) {
2552 _wapi_set_last_error_from_errno ();
2554 g_free (utf8_src);
2555 g_free (utf8_dest);
2556 MONO_ENTER_GC_SAFE;
2557 close (src_fd);
2558 MONO_EXIT_GC_SAFE;
2560 return(FALSE);
2563 if (!write_file (src_fd, dest_fd, &st, TRUE))
2564 ret = FALSE;
2566 close (src_fd);
2567 close (dest_fd);
2569 #ifdef HAVE_STRUCT_TIMEVAL
2570 struct timeval times [2];
2571 memset (times, 0, sizeof (times));
2573 convert_stattime_access_to_timeval (&times [0], &st);
2574 convert_stattime_mod_to_timeval (&times [1], &st);
2576 ret_utime = _wapi_utimes (utf8_dest, times);
2577 #else
2578 struct utimbuf dest_time;
2579 dest_time.modtime = st.st_mtime;
2580 dest_time.actime = st.st_atime;
2582 ret_utime = _wapi_utime (utf8_dest, &dest_time);
2583 #endif
2584 if (ret_utime == -1)
2585 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: file [%s] utime failed: %s", __func__, utf8_dest, g_strerror(errno));
2587 g_free (utf8_src);
2588 g_free (utf8_dest);
2590 return ret;
2593 static gchar*
2594 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2596 gchar *utf8_ret;
2597 ERROR_DECL (error);
2599 if (arg == NULL) {
2600 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: %s is NULL", __func__, arg_name);
2601 mono_w32error_set_last (ERROR_INVALID_NAME);
2602 return NULL;
2605 utf8_ret = mono_unicode_to_external_checked (arg, error);
2606 if (utf8_ret == NULL) {
2607 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of %s returned NULL; %s",
2608 __func__, arg_name, mono_error_get_message (error));
2610 mono_error_cleanup (error);
2611 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2612 return NULL;
2615 return utf8_ret;
2618 static gboolean
2619 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved)
2621 gint result, backup_fd = -1,replaced_fd = -1;
2622 gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2623 struct stat stBackup;
2624 gboolean ret = FALSE;
2626 if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2627 return FALSE;
2628 if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2629 goto replace_cleanup;
2630 if (backupFileName != NULL) {
2631 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2632 goto replace_cleanup;
2635 if (utf8_backupFileName) {
2636 // Open the backup file for read so we can restore the file if an error occurs.
2637 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2638 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2639 if (result == -1)
2640 goto replace_cleanup;
2643 result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2644 if (result == -1) {
2645 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2646 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2647 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2648 replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2649 stBackup.st_mode);
2651 if (replaced_fd == -1)
2652 goto replace_cleanup;
2654 write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2657 goto replace_cleanup;
2660 ret = TRUE;
2662 replace_cleanup:
2663 g_free (utf8_replacedFileName);
2664 g_free (utf8_replacementFileName);
2665 g_free (utf8_backupFileName);
2666 if (backup_fd != -1) {
2667 MONO_ENTER_GC_SAFE;
2668 close (backup_fd);
2669 MONO_EXIT_GC_SAFE;
2671 if (replaced_fd != -1) {
2672 MONO_ENTER_GC_SAFE;
2673 close (replaced_fd);
2674 MONO_EXIT_GC_SAFE;
2676 return ret;
2679 static gpointer
2680 _wapi_stdhandle_create (gint fd, const gchar *name)
2682 gint flags;
2683 FileHandle *filehandle;
2685 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: creating standard handle type %s, fd %d", __func__, name, fd);
2687 /* Check if fd is valid */
2688 do {
2689 flags = fcntl(fd, F_GETFL);
2690 } while (flags == -1 && errno == EINTR);
2692 if (flags == -1) {
2693 /* Invalid fd. Not really much point checking for EBADF
2694 * specifically
2696 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl error on fd %d: %s", __func__, fd, g_strerror(errno));
2698 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
2699 return INVALID_HANDLE_VALUE;
2702 filehandle = file_data_create (MONO_FDTYPE_CONSOLE, fd);
2703 filehandle->filename = g_strdup(name);
2705 switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) {
2706 case O_RDONLY:
2707 filehandle->fileaccess = GENERIC_READ;
2708 break;
2709 case O_WRONLY:
2710 filehandle->fileaccess = GENERIC_WRITE;
2711 break;
2712 case O_RDWR:
2713 filehandle->fileaccess = GENERIC_READ | GENERIC_WRITE;
2714 break;
2715 default:
2716 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Can't figure out flags 0x%x", __func__, flags);
2717 filehandle->fileaccess = 0;
2718 break;
2721 /* some default security attributes might be needed */
2722 filehandle->security_attributes = 0;
2724 /* Apparently input handles can't be written to. (I don't
2725 * know if output or error handles can't be read from.)
2727 if (fd == 0)
2728 filehandle->fileaccess &= ~GENERIC_WRITE;
2730 filehandle->sharemode = 0;
2731 filehandle->attrs = 0;
2733 if (!mono_fdhandle_try_insert ((MonoFDHandle*) filehandle)) {
2734 /* we raced between 2 invocations of _wapi_stdhandle_create */
2735 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2736 return GINT_TO_POINTER(fd);
2739 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: returning handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd));
2741 return GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd);
2744 enum {
2745 STD_INPUT_HANDLE = -10,
2746 STD_OUTPUT_HANDLE = -11,
2747 STD_ERROR_HANDLE = -12,
2750 static gpointer
2751 mono_w32file_get_std_handle (gint stdhandle)
2753 FileHandle **filehandle;
2754 gint fd;
2755 const gchar *name;
2757 switch(stdhandle) {
2758 case STD_INPUT_HANDLE:
2759 fd = 0;
2760 name = "<stdin>";
2761 break;
2763 case STD_OUTPUT_HANDLE:
2764 fd = 1;
2765 name = "<stdout>";
2766 break;
2768 case STD_ERROR_HANDLE:
2769 fd = 2;
2770 name = "<stderr>";
2771 break;
2773 default:
2774 g_assert_not_reached ();
2777 if (!mono_fdhandle_lookup_and_ref(fd, (MonoFDHandle**) &filehandle)) {
2778 gpointer handle;
2780 handle = _wapi_stdhandle_create (fd, name);
2781 if (handle == INVALID_HANDLE_VALUE) {
2782 mono_w32error_set_last (ERROR_NO_MORE_FILES);
2783 return INVALID_HANDLE_VALUE;
2787 return GINT_TO_POINTER (fd);
2790 static gboolean
2791 mono_w32file_read_or_write (gboolean read, gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, gint32 *win32error)
2793 MONO_REQ_GC_UNSAFE_MODE;
2795 FileHandle *filehandle;
2796 gboolean ret = FALSE;
2798 gboolean const ref = mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle);
2799 if (!ref) {
2800 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2801 goto exit;
2804 switch (((MonoFDHandle*) filehandle)->type) {
2805 case MONO_FDTYPE_FILE:
2806 ret = (read ? file_read : file_write) (filehandle, buffer, numbytes, bytesread);
2807 break;
2808 case MONO_FDTYPE_CONSOLE:
2809 ret = (read ? console_read : console_write) (filehandle, buffer, numbytes, bytesread);
2810 break;
2811 case MONO_FDTYPE_PIPE:
2812 ret = (read ? pipe_read : pipe_write) (filehandle, buffer, numbytes, bytesread);
2813 break;
2814 default:
2815 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2816 break;
2819 exit:
2820 if (ref)
2821 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2822 if (!ret)
2823 *win32error = mono_w32error_get_last ();
2824 return ret;
2827 gboolean
2828 mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, gint32 *win32error)
2830 return mono_w32file_read_or_write (TRUE, handle, buffer, numbytes, bytesread, win32error);
2833 gboolean
2834 mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, gint32 *win32error)
2836 return mono_w32file_read_or_write (FALSE, handle, (gpointer)buffer, numbytes, byteswritten, win32error);
2839 gboolean
2840 mono_w32file_flush (gpointer handle)
2842 FileHandle *filehandle;
2843 gboolean ret;
2845 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2846 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2847 return FALSE;
2850 switch (((MonoFDHandle*) filehandle)->type) {
2851 case MONO_FDTYPE_FILE:
2852 ret = file_flush(filehandle);
2853 break;
2854 default:
2855 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2856 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2857 return FALSE;
2860 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2861 return ret;
2864 gboolean
2865 mono_w32file_truncate (gpointer handle)
2867 FileHandle *filehandle;
2868 gboolean ret;
2870 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2871 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2872 return FALSE;
2875 switch (((MonoFDHandle*) filehandle)->type) {
2876 case MONO_FDTYPE_FILE:
2877 ret = file_setendoffile(filehandle);
2878 break;
2879 default:
2880 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2881 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2882 return FALSE;
2885 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2886 return ret;
2889 guint32
2890 mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method)
2892 FileHandle *filehandle;
2893 guint32 ret;
2895 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2896 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2897 return INVALID_SET_FILE_POINTER;
2900 switch (((MonoFDHandle*) filehandle)->type) {
2901 case MONO_FDTYPE_FILE:
2902 ret = file_seek(filehandle, movedistance, highmovedistance, method);
2903 break;
2904 default:
2905 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2906 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2907 return INVALID_SET_FILE_POINTER;
2910 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2911 return ret;
2914 gint
2915 mono_w32file_get_type(gpointer handle)
2917 FileHandle *filehandle;
2918 gint ret;
2920 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2921 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2922 return FILE_TYPE_UNKNOWN;
2925 switch (((MonoFDHandle*) filehandle)->type) {
2926 case MONO_FDTYPE_FILE:
2927 ret = FILE_TYPE_DISK;
2928 break;
2929 case MONO_FDTYPE_CONSOLE:
2930 ret = FILE_TYPE_CHAR;
2931 break;
2932 case MONO_FDTYPE_PIPE:
2933 ret = FILE_TYPE_PIPE;
2934 break;
2935 default:
2936 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2937 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2938 return FILE_TYPE_UNKNOWN;
2941 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2942 return ret;
2945 static guint32
2946 GetFileSize(gpointer handle, guint32 *highsize)
2948 FileHandle *filehandle;
2949 guint32 ret;
2951 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2952 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2953 return INVALID_FILE_SIZE;
2956 switch (((MonoFDHandle*) filehandle)->type) {
2957 case MONO_FDTYPE_FILE:
2958 ret = file_getfilesize(filehandle, highsize);
2959 break;
2960 default:
2961 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2962 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2963 return INVALID_FILE_SIZE;
2966 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2967 return ret;
2970 gboolean
2971 mono_w32file_set_times(gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time)
2973 FileHandle *filehandle;
2974 gboolean ret;
2976 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2977 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2978 return FALSE;
2981 switch (((MonoFDHandle*) filehandle)->type) {
2982 case MONO_FDTYPE_FILE:
2983 ret = file_setfiletime(filehandle, create_time, access_time, write_time);
2984 break;
2985 default:
2986 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2987 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2988 return FALSE;
2991 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2992 return ret;
2995 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2996 * January 1 1601 GMT
2999 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
3001 static const guint16 mon_yday[2][13]={
3002 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
3003 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
3006 gboolean
3007 mono_w32file_filetime_to_systemtime(const FILETIME *file_time, SYSTEMTIME *system_time)
3009 gint64 file_ticks, totaldays, rem, y;
3010 const guint16 *ip;
3012 if(system_time==NULL) {
3013 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: system_time NULL", __func__);
3015 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3016 return(FALSE);
3019 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
3020 file_time->dwLowDateTime;
3022 /* Really compares if file_ticks>=0x8000000000000000
3023 * (LLONG_MAX+1) but we're working with a signed value for the
3024 * year and day calculation to work later
3026 if(file_ticks<0) {
3027 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: file_time too big", __func__);
3029 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3030 return(FALSE);
3033 totaldays=(file_ticks / TICKS_PER_DAY);
3034 rem = file_ticks % TICKS_PER_DAY;
3035 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT " rem: %" G_GINT64_FORMAT, __func__,
3036 totaldays, rem);
3038 system_time->wHour=rem/TICKS_PER_HOUR;
3039 rem %= TICKS_PER_HOUR;
3040 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Hour: %d rem: %" G_GINT64_FORMAT, __func__,
3041 system_time->wHour, rem);
3043 system_time->wMinute = rem / TICKS_PER_MINUTE;
3044 rem %= TICKS_PER_MINUTE;
3045 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Minute: %d rem: %" G_GINT64_FORMAT, __func__,
3046 system_time->wMinute, rem);
3048 system_time->wSecond = rem / TICKS_PER_SECOND;
3049 rem %= TICKS_PER_SECOND;
3050 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Second: %d rem: %" G_GINT64_FORMAT, __func__,
3051 system_time->wSecond, rem);
3053 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
3054 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Milliseconds: %d", __func__,
3055 system_time->wMilliseconds);
3057 /* January 1, 1601 was a Monday, according to Emacs calendar */
3058 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
3059 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Day of week: %d", __func__, system_time->wDayOfWeek);
3061 /* This algorithm to find year and month given days from epoch
3062 * from glibc
3064 y=1601;
3066 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
3067 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
3069 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
3070 /* Guess a corrected year, assuming 365 days per year */
3071 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
3072 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__,
3073 totaldays, yg, y);
3074 g_message("%s: LEAPS(yg): %li LEAPS(y): %li", __func__,
3075 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
3077 /* Adjust days and y to match the guessed year. */
3078 totaldays -= ((yg - y) * 365
3079 + LEAPS_THRU_END_OF (yg - 1)
3080 - LEAPS_THRU_END_OF (y - 1));
3081 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT,
3082 __func__, totaldays);
3083 y = yg;
3084 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: y: %" G_GINT64_FORMAT, __func__, y);
3087 system_time->wYear = y;
3088 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Year: %d", __func__, system_time->wYear);
3090 ip = mon_yday[isleap(y)];
3092 for(y=11; totaldays < ip[y]; --y) {
3093 continue;
3095 totaldays-=ip[y];
3096 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT, __func__, totaldays);
3098 system_time->wMonth = y + 1;
3099 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Month: %d", __func__, system_time->wMonth);
3101 system_time->wDay = totaldays + 1;
3102 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Day: %d", __func__, system_time->wDay);
3104 return(TRUE);
3107 static void
3108 findhandle_destroy (gpointer data)
3110 FindHandle *findhandle;
3112 findhandle = (FindHandle*) data;
3113 g_assert (findhandle);
3115 mono_coop_mutex_destroy (&findhandle->mutex);
3117 if (findhandle->namelist)
3118 g_strfreev (findhandle->namelist);
3119 if (findhandle->dir_part)
3120 g_free (findhandle->dir_part);
3122 g_free (findhandle);
3125 static FindHandle*
3126 findhandle_create (void)
3128 FindHandle* findhandle;
3130 findhandle = g_new0 (FindHandle, 1);
3131 mono_refcount_init (findhandle, findhandle_destroy);
3133 mono_coop_mutex_init (&findhandle->mutex);
3135 return findhandle;
3138 static void
3139 findhandle_insert (FindHandle *findhandle)
3141 mono_coop_mutex_lock (&finds_mutex);
3143 if (g_hash_table_lookup_extended (finds, (gpointer) findhandle, NULL, NULL))
3144 g_error("%s: duplicate Find handle %p", __func__, (gpointer) findhandle);
3146 g_hash_table_insert (finds, (gpointer) findhandle, findhandle);
3148 mono_coop_mutex_unlock (&finds_mutex);
3151 static gboolean
3152 findhandle_lookup_and_ref (gpointer handle, FindHandle **findhandle)
3154 mono_coop_mutex_lock (&finds_mutex);
3156 if (!g_hash_table_lookup_extended (finds, handle, NULL, (gpointer*) findhandle)) {
3157 mono_coop_mutex_unlock (&finds_mutex);
3158 return FALSE;
3161 mono_refcount_inc (*findhandle);
3163 mono_coop_mutex_unlock (&finds_mutex);
3165 return TRUE;
3168 static void
3169 findhandle_unref (FindHandle *findhandle)
3171 mono_refcount_dec (findhandle);
3174 static gboolean
3175 findhandle_close (gpointer handle)
3177 FindHandle *findhandle;
3178 gboolean removed;
3180 mono_coop_mutex_lock (&finds_mutex);
3182 if (!g_hash_table_lookup_extended (finds, handle, NULL, (gpointer*) &findhandle)) {
3183 mono_coop_mutex_unlock (&finds_mutex);
3185 return FALSE;
3188 removed = g_hash_table_remove (finds, (gpointer) findhandle);
3189 g_assert (removed);
3191 mono_coop_mutex_unlock (&finds_mutex);
3193 return TRUE;
3196 static void
3197 finds_remove (gpointer data)
3199 FindHandle* findhandle;
3201 findhandle = (FindHandle*) data;
3202 g_assert (findhandle);
3204 mono_refcount_dec (findhandle);
3207 gpointer
3208 mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data)
3210 FindHandle *findhandle;
3211 gchar *utf8_pattern = NULL, *dir_part, *entry_part, **namelist;
3212 gint result;
3213 ERROR_DECL (error);
3215 if (pattern == NULL) {
3216 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: pattern is NULL", __func__);
3218 mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
3219 return(INVALID_HANDLE_VALUE);
3222 utf8_pattern = mono_unicode_to_external_checked (pattern, error);
3223 if (utf8_pattern == NULL) {
3224 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3226 mono_error_cleanup (error);
3227 mono_w32error_set_last (ERROR_INVALID_NAME);
3228 return(INVALID_HANDLE_VALUE);
3231 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: looking for [%s]", __func__, utf8_pattern);
3233 /* Figure out which bit of the pattern is the directory */
3234 dir_part = _wapi_dirname (utf8_pattern);
3235 entry_part = _wapi_basename (utf8_pattern);
3237 #if 0
3238 /* Don't do this check for now, it breaks if directories
3239 * really do have metachars in their names (see bug 58116).
3240 * FIXME: Figure out a better solution to keep some checks...
3242 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3243 mono_w32error_set_last (ERROR_INVALID_NAME);
3244 g_free (dir_part);
3245 g_free (entry_part);
3246 g_free (utf8_pattern);
3247 return(INVALID_HANDLE_VALUE);
3249 #endif
3251 /* The pattern can specify a directory or a set of files.
3253 * The pattern can have wildcard characters ? and *, but only
3254 * in the section after the last directory delimiter. (Return
3255 * ERROR_INVALID_NAME if there are wildcards in earlier path
3256 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3257 * means "match one character", "??" seems to mean "match one
3258 * or two characters", "???" seems to mean "match one, two or
3259 * three characters", etc. Windows will also try and match
3260 * the mangled "short name" of files, so 8 character patterns
3261 * with wildcards will show some surprising results.
3263 * All the written documentation I can find says that '?'
3264 * should only match one character, and doesn't mention '??',
3265 * '???' etc. I'm going to assume that the strict behaviour
3266 * (ie '???' means three and only three characters) is the
3267 * correct one, because that lets me use fnmatch(3) rather
3268 * than mess around with regexes.
3271 namelist = NULL;
3272 result = _wapi_io_scandir (dir_part, entry_part,
3273 &namelist);
3275 if (result == 0) {
3276 /* No files, which windows seems to call
3277 * FILE_NOT_FOUND
3279 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
3280 g_free (utf8_pattern);
3281 g_free (entry_part);
3282 g_free (dir_part);
3283 g_strfreev (namelist);
3284 return (INVALID_HANDLE_VALUE);
3287 if (result < 0) {
3288 _wapi_set_last_path_error_from_errno (dir_part, NULL);
3289 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: scandir error: %s", __func__, g_strerror (errno));
3290 g_free (utf8_pattern);
3291 g_free (entry_part);
3292 g_free (dir_part);
3293 g_strfreev (namelist);
3294 return (INVALID_HANDLE_VALUE);
3297 g_free (utf8_pattern);
3298 g_free (entry_part);
3300 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Got %d matches", __func__, result);
3302 findhandle = findhandle_create ();
3303 findhandle->namelist = namelist;
3304 findhandle->dir_part = dir_part;
3305 findhandle->num = result;
3306 findhandle->count = 0;
3308 findhandle_insert (findhandle);
3310 if (!mono_w32file_find_next ((gpointer) findhandle, find_data)) {
3311 mono_w32file_find_close ((gpointer) findhandle);
3312 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3313 return INVALID_HANDLE_VALUE;
3316 return (gpointer) findhandle;
3319 gboolean
3320 mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data)
3322 FindHandle *findhandle;
3323 struct stat buf, linkbuf;
3324 gint result;
3325 gchar *filename;
3326 gchar *utf8_filename, *utf8_basename;
3327 gunichar2 *utf16_basename;
3328 time_t create_time;
3329 glong bytes;
3330 gboolean ret = FALSE;
3332 if (!findhandle_lookup_and_ref (handle, &findhandle)) {
3333 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3334 return FALSE;
3337 mono_coop_mutex_lock (&findhandle->mutex);
3339 retry:
3340 if (findhandle->count >= findhandle->num) {
3341 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3342 goto cleanup;
3345 /* stat next match */
3347 filename = g_build_filename (findhandle->dir_part, findhandle->namelist[findhandle->count ++], NULL);
3349 result = _wapi_stat (filename, &buf);
3350 if (result == -1 && errno == ENOENT) {
3351 /* Might be a dangling symlink */
3352 result = _wapi_lstat (filename, &buf);
3355 if (result != 0) {
3356 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: stat failed: %s", __func__, filename);
3358 g_free (filename);
3359 goto retry;
3362 result = _wapi_lstat (filename, &linkbuf);
3363 if (result != 0) {
3364 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lstat failed: %s", __func__, filename);
3366 g_free (filename);
3367 goto retry;
3370 utf8_filename = mono_utf8_from_external (filename);
3371 if (utf8_filename == NULL) {
3372 /* We couldn't turn this filename into utf8 (eg the
3373 * encoding of the name wasn't convertible), so just
3374 * ignore it.
3376 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
3378 g_free (filename);
3379 goto retry;
3381 g_free (filename);
3383 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Found [%s]", __func__, utf8_filename);
3385 /* fill data block */
3387 if (buf.st_mtime < buf.st_ctime)
3388 create_time = buf.st_mtime;
3389 else
3390 create_time = buf.st_ctime;
3392 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
3394 time_t_to_filetime (create_time, &find_data->ftCreationTime);
3395 time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3396 time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3398 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3399 find_data->nFileSizeHigh = 0;
3400 find_data->nFileSizeLow = 0;
3401 } else {
3402 find_data->nFileSizeHigh = buf.st_size >> 32;
3403 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3406 find_data->dwReserved0 = 0;
3407 find_data->dwReserved1 = 0;
3409 utf8_basename = _wapi_basename (utf8_filename);
3410 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3411 NULL);
3412 if(utf16_basename==NULL) {
3413 g_free (utf8_basename);
3414 g_free (utf8_filename);
3415 goto retry;
3417 ret = TRUE;
3419 /* utf16 is 2 * utf8 */
3420 bytes *= 2;
3422 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3424 /* Truncating a utf16 string like this might leave the last
3425 * gchar incomplete
3427 memcpy (find_data->cFileName, utf16_basename,
3428 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3430 find_data->cAlternateFileName [0] = 0; /* not used */
3432 g_free (utf8_basename);
3433 g_free (utf8_filename);
3434 g_free (utf16_basename);
3436 cleanup:
3437 mono_coop_mutex_unlock (&findhandle->mutex);
3439 findhandle_unref (findhandle);
3441 return(ret);
3444 gboolean
3445 mono_w32file_find_close (gpointer handle)
3447 if (!findhandle_close (handle)) {
3448 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3449 return FALSE;
3452 return TRUE;
3455 gboolean
3456 mono_w32file_create_directory (const gunichar2 *name)
3458 gchar *utf8_name;
3459 gint result;
3460 ERROR_DECL (error);
3462 if (name == NULL) {
3463 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3465 mono_w32error_set_last (ERROR_INVALID_NAME);
3466 return(FALSE);
3469 utf8_name = mono_unicode_to_external_checked (name, error);
3470 if (utf8_name == NULL) {
3471 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3473 mono_error_cleanup (error);
3474 mono_w32error_set_last (ERROR_INVALID_NAME);
3475 return FALSE;
3478 result = _wapi_mkdir (utf8_name, 0777);
3480 if (result == 0) {
3481 g_free (utf8_name);
3482 return TRUE;
3485 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3486 g_free (utf8_name);
3487 return FALSE;
3490 gboolean
3491 mono_w32file_remove_directory (const gunichar2 *name)
3493 gchar *utf8_name;
3494 gint result;
3495 ERROR_DECL (error);
3497 if (name == NULL) {
3498 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3500 mono_w32error_set_last (ERROR_INVALID_NAME);
3501 return(FALSE);
3504 utf8_name = mono_unicode_to_external_checked (name, error);
3505 if (utf8_name == NULL) {
3506 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3508 mono_error_cleanup (error);
3509 mono_w32error_set_last (ERROR_INVALID_NAME);
3510 return FALSE;
3513 result = _wapi_rmdir (utf8_name);
3514 if (result == -1) {
3515 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3516 g_free (utf8_name);
3518 return(FALSE);
3520 g_free (utf8_name);
3522 return(TRUE);
3525 guint32
3526 mono_w32file_get_attributes (const gunichar2 *name)
3528 gchar *utf8_name;
3529 struct stat buf, linkbuf;
3530 gint result;
3531 guint32 ret;
3532 ERROR_DECL (error);
3534 if (name == NULL) {
3535 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3537 mono_w32error_set_last (ERROR_INVALID_NAME);
3538 return(FALSE);
3541 utf8_name = mono_unicode_to_external_checked (name, error);
3542 if (utf8_name == NULL) {
3543 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3545 mono_error_cleanup (error);
3546 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3547 return (INVALID_FILE_ATTRIBUTES);
3550 result = _wapi_stat (utf8_name, &buf);
3551 if (result == -1 && (errno == ENOENT || errno == ELOOP)) {
3552 /* Might be a dangling symlink... */
3553 result = _wapi_lstat (utf8_name, &buf);
3556 if (result != 0) {
3557 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3558 g_free (utf8_name);
3559 return (INVALID_FILE_ATTRIBUTES);
3562 result = _wapi_lstat (utf8_name, &linkbuf);
3563 if (result != 0) {
3564 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3565 g_free (utf8_name);
3566 return (INVALID_FILE_ATTRIBUTES);
3569 ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3571 g_free (utf8_name);
3573 return(ret);
3576 gboolean
3577 mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat)
3579 gchar *utf8_name;
3581 struct stat buf, linkbuf;
3582 gint result;
3583 ERROR_DECL (error);
3585 if (name == NULL) {
3586 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3588 mono_w32error_set_last (ERROR_INVALID_NAME);
3589 return(FALSE);
3592 utf8_name = mono_unicode_to_external_checked (name, error);
3593 if (utf8_name == NULL) {
3594 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3596 mono_error_cleanup (error);
3597 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3598 return FALSE;
3601 result = _wapi_stat (utf8_name, &buf);
3602 if (result == -1 && errno == ENOENT) {
3603 /* Might be a dangling symlink... */
3604 result = _wapi_lstat (utf8_name, &buf);
3607 if (result != 0) {
3608 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3609 g_free (utf8_name);
3610 return FALSE;
3613 result = _wapi_lstat (utf8_name, &linkbuf);
3614 if (result != 0) {
3615 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3616 g_free (utf8_name);
3617 return(FALSE);
3620 /* fill stat block */
3622 stat->attributes = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3623 stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size;
3625 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
3626 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))
3627 stat->creation_time = linkbuf.st_mtimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3628 else
3629 stat->creation_time = linkbuf.st_ctimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_ctimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3631 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;
3632 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;
3633 #elif HAVE_STRUCT_STAT_ST_ATIM
3634 if (linkbuf.st_mtime < linkbuf.st_ctime || (linkbuf.st_mtime == linkbuf.st_ctime && linkbuf.st_mtim.tv_nsec < linkbuf.st_ctim.tv_nsec))
3635 stat->creation_time = linkbuf.st_mtime * TICKS_PER_SECOND + (linkbuf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3636 else
3637 stat->creation_time = linkbuf.st_ctime * TICKS_PER_SECOND + (linkbuf.st_ctim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3639 stat->last_access_time = linkbuf.st_atime * TICKS_PER_SECOND + (linkbuf.st_atim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3640 stat->last_write_time = linkbuf.st_mtime * TICKS_PER_SECOND + (linkbuf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3641 #else
3642 stat->creation_time = (((guint64) (linkbuf.st_mtime < linkbuf.st_ctime ? linkbuf.st_mtime : linkbuf.st_ctime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3643 stat->last_access_time = (((guint64) (linkbuf.st_atime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3644 stat->last_write_time = (((guint64) (linkbuf.st_mtime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3645 #endif
3647 g_free (utf8_name);
3648 return TRUE;
3651 gboolean
3652 mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs)
3654 /* FIXME: think of something clever to do on unix */
3655 gchar *utf8_name;
3656 struct stat buf;
3657 gint result;
3658 ERROR_DECL (error);
3661 * Currently we only handle one *internal* case, with a value that is
3662 * not standard: 0x80000000, which means `set executable bit'
3665 if (name == NULL) {
3666 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3668 mono_w32error_set_last (ERROR_INVALID_NAME);
3669 return(FALSE);
3672 utf8_name = mono_unicode_to_external_checked (name, error);
3673 if (utf8_name == NULL) {
3674 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3676 mono_error_cleanup (error);
3677 mono_w32error_set_last (ERROR_INVALID_NAME);
3678 return FALSE;
3681 result = _wapi_stat (utf8_name, &buf);
3682 if (result == -1 && errno == ENOENT) {
3683 /* Might be a dangling symlink... */
3684 result = _wapi_lstat (utf8_name, &buf);
3687 if (result != 0) {
3688 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3689 g_free (utf8_name);
3690 return FALSE;
3693 /* Contrary to the documentation, ms allows NORMAL to be
3694 * specified along with other attributes, so dont bother to
3695 * catch that case here.
3697 if (attrs & FILE_ATTRIBUTE_READONLY) {
3698 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP));
3699 } else {
3700 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR);
3703 /* Ignore the other attributes for now */
3705 if (attrs & 0x80000000){
3706 mode_t exec_mask = 0;
3708 if ((buf.st_mode & S_IRUSR) != 0)
3709 exec_mask |= S_IXUSR;
3711 if ((buf.st_mode & S_IRGRP) != 0)
3712 exec_mask |= S_IXGRP;
3714 if ((buf.st_mode & S_IROTH) != 0)
3715 exec_mask |= S_IXOTH;
3717 #if defined(HAVE_CHMOD)
3718 MONO_ENTER_GC_SAFE;
3719 result = chmod (utf8_name, buf.st_mode | exec_mask);
3720 MONO_EXIT_GC_SAFE;
3721 #else
3722 result = -1;
3723 #endif
3725 /* Don't bother to reset executable (might need to change this
3726 * policy)
3729 g_free (utf8_name);
3731 return(TRUE);
3734 guint32
3735 mono_w32file_get_cwd (guint32 length, gunichar2 *buffer)
3737 gunichar2 *utf16_path;
3738 glong count;
3739 gsize bytes;
3741 if (getcwd ((gchar*)buffer, length) == NULL) {
3742 if (errno == ERANGE) { /*buffer length is not big enough */
3743 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*/
3744 if (path == NULL)
3745 return 0;
3746 utf16_path = mono_unicode_from_external (path, &bytes);
3747 g_free (utf16_path);
3748 g_free (path);
3749 return (bytes/2)+1;
3751 _wapi_set_last_error_from_errno ();
3752 return 0;
3755 utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3756 count = (bytes/2)+1;
3757 g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3759 /* Add the terminator */
3760 memset (buffer, '\0', bytes+2);
3761 memcpy (buffer, utf16_path, bytes);
3763 g_free (utf16_path);
3765 return count;
3768 gboolean
3769 mono_w32file_set_cwd (const gunichar2 *path)
3771 gchar *utf8_path;
3772 gboolean result;
3773 ERROR_DECL (error);
3775 if (path == NULL) {
3776 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3777 return(FALSE);
3780 utf8_path = mono_unicode_to_external_checked (path, error);
3781 if (utf8_path == NULL) {
3782 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
3784 mono_error_cleanup (error);
3785 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3786 return FALSE;
3789 if (_wapi_chdir (utf8_path) != 0) {
3790 _wapi_set_last_error_from_errno ();
3791 result = FALSE;
3793 else
3794 result = TRUE;
3796 g_free (utf8_path);
3797 return result;
3800 gboolean
3801 mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size)
3803 FileHandle *read_filehandle, *write_filehandle;
3804 gint filedes[2];
3805 gint ret;
3807 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Creating pipe", __func__);
3809 MONO_ENTER_GC_SAFE;
3810 ret=pipe (filedes);
3811 MONO_EXIT_GC_SAFE;
3812 if (ret==-1) {
3813 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Error creating pipe: (%d) %s",
3814 __func__, errno, g_strerror (errno));
3816 _wapi_set_last_error_from_errno ();
3817 return FALSE;
3820 /* filedes[0] is open for reading, filedes[1] for writing */
3822 read_filehandle = file_data_create (MONO_FDTYPE_PIPE, filedes[0]);
3823 read_filehandle->fileaccess = GENERIC_READ;
3825 write_filehandle = file_data_create (MONO_FDTYPE_PIPE, filedes[1]);
3826 write_filehandle->fileaccess = GENERIC_WRITE;
3828 mono_fdhandle_insert ((MonoFDHandle*) read_filehandle);
3829 mono_fdhandle_insert ((MonoFDHandle*) write_filehandle);
3831 *readpipe = GINT_TO_POINTER(((MonoFDHandle*) read_filehandle)->fd);
3832 *writepipe = GINT_TO_POINTER(((MonoFDHandle*) write_filehandle)->fd);
3834 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning pipe: read handle %p, write handle %p",
3835 __func__, GINT_TO_POINTER(((MonoFDHandle*) read_filehandle)->fd), GINT_TO_POINTER(((MonoFDHandle*) write_filehandle)->fd));
3837 return(TRUE);
3840 #ifdef HAVE_GETFSSTAT
3841 /* Darwin has getfsstat */
3842 gint32
3843 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3845 struct statfs *stats;
3846 gint size, n, i;
3847 gunichar2 *dir;
3848 glong length, total = 0;
3849 gint syscall_res;
3851 MONO_ENTER_GC_SAFE;
3852 n = getfsstat (NULL, 0, MNT_NOWAIT);
3853 MONO_EXIT_GC_SAFE;
3854 if (n == -1)
3855 return 0;
3856 size = n * sizeof (struct statfs);
3857 stats = (struct statfs *) g_malloc (size);
3858 if (stats == NULL)
3859 return 0;
3860 MONO_ENTER_GC_SAFE;
3861 syscall_res = getfsstat (stats, size, MNT_NOWAIT);
3862 MONO_EXIT_GC_SAFE;
3863 if (syscall_res == -1){
3864 g_free (stats);
3865 return 0;
3867 for (i = 0; i < n; i++){
3868 dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL);
3869 if (total + length < len){
3870 memcpy (buf + total, dir, sizeof (gunichar2) * length);
3871 buf [total+length] = 0;
3873 g_free (dir);
3874 total += length + 1;
3876 if (total < len)
3877 buf [total] = 0;
3878 total++;
3879 g_free (stats);
3880 return total;
3882 #elif _AIX
3883 gint32
3884 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3886 struct vmount *mounts;
3887 // ret will first be the errno cond, then no of structs
3888 int needsize, ret, total;
3889 gunichar2 *dir;
3890 glong length;
3891 total = 0;
3893 MONO_ENTER_GC_SAFE;
3894 ret = mntctl (MCTL_QUERY, sizeof(needsize), &needsize);
3895 MONO_EXIT_GC_SAFE;
3896 if (ret == -1)
3897 return 0;
3898 mounts = (struct vmount *) g_malloc (needsize);
3899 if (mounts == NULL)
3900 return 0;
3901 MONO_ENTER_GC_SAFE;
3902 ret = mntctl (MCTL_QUERY, needsize, mounts);
3903 MONO_EXIT_GC_SAFE;
3904 if (ret == -1) {
3905 g_free (mounts);
3906 return 0;
3909 for (int i = 0; i < ret; i++) {
3910 dir = g_utf8_to_utf16 (vmt2dataptr(mounts, VMT_STUB), -1, NULL, &length, NULL);
3911 if (total + length < len){
3912 memcpy (buf + total, dir, sizeof (gunichar2) * length);
3913 buf [total+length] = 0;
3915 g_free (dir);
3916 total += length + 1;
3917 mounts = (void*)mounts + mounts->vmt_length; // next!
3919 if (total < len)
3920 buf [total] = 0;
3921 total++;
3922 g_free (mounts);
3923 return total;
3925 #else
3926 /* In-place octal sequence replacement */
3927 static void
3928 unescape_octal (gchar *str)
3930 gchar *rptr;
3931 gchar *wptr;
3933 if (str == NULL)
3934 return;
3936 rptr = wptr = str;
3937 while (*rptr != '\0') {
3938 if (*rptr == '\\') {
3939 gchar c;
3940 rptr++;
3941 c = (*(rptr++) - '0') << 6;
3942 c += (*(rptr++) - '0') << 3;
3943 c += *(rptr++) - '0';
3944 *wptr++ = c;
3945 } else if (wptr != rptr) {
3946 *wptr++ = *rptr++;
3947 } else {
3948 rptr++; wptr++;
3951 *wptr = '\0';
3953 static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf);
3955 #if __linux__
3956 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
3957 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
3958 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
3960 typedef struct
3962 glong total;
3963 guint32 buffer_index;
3964 guint32 mountpoint_index;
3965 guint32 field_number;
3966 guint32 allocated_size;
3967 guint32 fsname_index;
3968 guint32 fstype_index;
3969 gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1];
3970 gchar *mountpoint_allocated;
3971 gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER];
3972 gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3973 gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3974 ssize_t nbytes;
3975 gchar delimiter;
3976 gboolean check_mount_source;
3977 } LinuxMountInfoParseState;
3979 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3980 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3981 static void append_to_mountpoint (LinuxMountInfoParseState *state);
3982 static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3984 gint32
3985 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3987 gint fd;
3988 gint32 ret = 0;
3989 LinuxMountInfoParseState state;
3990 gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL;
3992 memset (buf, 0, len * sizeof (gunichar2));
3993 MONO_ENTER_GC_SAFE;
3994 fd = open ("/proc/self/mountinfo", O_RDONLY);
3995 MONO_EXIT_GC_SAFE;
3996 if (fd != -1)
3997 parser = GetLogicalDriveStrings_MountInfo;
3998 else {
3999 MONO_ENTER_GC_SAFE;
4000 fd = open ("/proc/mounts", O_RDONLY);
4001 MONO_EXIT_GC_SAFE;
4002 if (fd != -1)
4003 parser = GetLogicalDriveStrings_Mounts;
4006 if (!parser) {
4007 ret = GetLogicalDriveStrings_Mtab (len, buf);
4008 goto done_and_out;
4011 memset (&state, 0, sizeof (LinuxMountInfoParseState));
4012 state.field_number = 1;
4013 state.delimiter = ' ';
4015 while (1) {
4016 MONO_ENTER_GC_SAFE;
4017 state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER);
4018 MONO_EXIT_GC_SAFE;
4019 if (!(state.nbytes > 0))
4020 break;
4021 state.buffer_index = 0;
4023 while ((*parser)(len, buf, &state)) {
4024 if (state.buffer [state.buffer_index] == '\n') {
4025 gboolean quit = add_drive_string (len, buf, &state);
4026 state.field_number = 1;
4027 state.buffer_index++;
4028 if (state.mountpoint_allocated) {
4029 g_free (state.mountpoint_allocated);
4030 state.mountpoint_allocated = NULL;
4032 if (quit) {
4033 ret = state.total;
4034 goto done_and_out;
4039 ret = state.total;
4041 done_and_out:
4042 if (fd != -1) {
4043 MONO_ENTER_GC_SAFE;
4044 close (fd);
4045 MONO_EXIT_GC_SAFE;
4047 return ret;
4050 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4052 gchar *ptr;
4054 if (state->field_number == 1)
4055 state->check_mount_source = TRUE;
4057 while (state->buffer_index < (guint32)state->nbytes) {
4058 if (state->buffer [state->buffer_index] == state->delimiter) {
4059 state->field_number++;
4060 switch (state->field_number) {
4061 case 2:
4062 state->mountpoint_index = 0;
4063 break;
4065 case 3:
4066 if (state->mountpoint_allocated)
4067 state->mountpoint_allocated [state->mountpoint_index] = 0;
4068 else
4069 state->mountpoint [state->mountpoint_index] = 0;
4070 break;
4072 default:
4073 ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index);
4074 if (ptr)
4075 state->buffer_index = (ptr - (gchar*)state->buffer) - 1;
4076 else
4077 state->buffer_index = state->nbytes;
4078 return TRUE;
4080 state->buffer_index++;
4081 continue;
4082 } else if (state->buffer [state->buffer_index] == '\n')
4083 return TRUE;
4085 switch (state->field_number) {
4086 case 1:
4087 if (state->check_mount_source) {
4088 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4089 /* We can ignore the rest, it's a device
4090 * path */
4091 state->check_mount_source = FALSE;
4092 state->fsname [state->fsname_index++] = '/';
4093 break;
4095 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4096 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4098 break;
4100 case 2:
4101 append_to_mountpoint (state);
4102 break;
4104 case 3:
4105 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4106 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4107 break;
4110 state->buffer_index++;
4113 return FALSE;
4116 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4118 while (state->buffer_index < (guint32)state->nbytes) {
4119 if (state->buffer [state->buffer_index] == state->delimiter) {
4120 state->field_number++;
4121 switch (state->field_number) {
4122 case 5:
4123 state->mountpoint_index = 0;
4124 break;
4126 case 6:
4127 if (state->mountpoint_allocated)
4128 state->mountpoint_allocated [state->mountpoint_index] = 0;
4129 else
4130 state->mountpoint [state->mountpoint_index] = 0;
4131 break;
4133 case 7:
4134 state->delimiter = '-';
4135 break;
4137 case 8:
4138 state->delimiter = ' ';
4139 break;
4141 case 10:
4142 state->check_mount_source = TRUE;
4143 break;
4145 state->buffer_index++;
4146 continue;
4147 } else if (state->buffer [state->buffer_index] == '\n')
4148 return TRUE;
4150 switch (state->field_number) {
4151 case 5:
4152 append_to_mountpoint (state);
4153 break;
4155 case 9:
4156 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4157 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4158 break;
4160 case 10:
4161 if (state->check_mount_source) {
4162 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4163 /* We can ignore the rest, it's a device
4164 * path */
4165 state->check_mount_source = FALSE;
4166 state->fsname [state->fsname_index++] = '/';
4167 break;
4169 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4170 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4172 break;
4175 state->buffer_index++;
4178 return FALSE;
4181 static void
4182 append_to_mountpoint (LinuxMountInfoParseState *state)
4184 gchar ch = state->buffer [state->buffer_index];
4185 if (state->mountpoint_allocated) {
4186 if (state->mountpoint_index >= state->allocated_size) {
4187 guint32 newsize = (state->allocated_size << 1) + 1;
4188 gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar));
4190 memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index);
4191 g_free (state->mountpoint_allocated);
4192 state->mountpoint_allocated = newbuf;
4193 state->allocated_size = newsize;
4195 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4196 } else {
4197 if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) {
4198 state->allocated_size = (state->mountpoint_index << 1) + 1;
4199 state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar));
4200 memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index);
4201 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4202 } else
4203 state->mountpoint [state->mountpoint_index++] = ch;
4207 static gboolean
4208 add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4210 gboolean quit = FALSE;
4211 gboolean ignore_entry;
4213 if (state->fsname_index == 1 && state->fsname [0] == '/')
4214 ignore_entry = FALSE;
4215 else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 ||
4216 memcmp ("aufs", state->fstype, state->fstype_index) == 0) {
4217 /* Don't ignore overlayfs and aufs - these might be used on Docker
4218 * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */
4219 ignore_entry = FALSE;
4220 } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) {
4221 ignore_entry = TRUE;
4222 } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) {
4223 /* Ignore GNOME's gvfs */
4224 if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0)
4225 ignore_entry = TRUE;
4226 else
4227 ignore_entry = FALSE;
4228 } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0)
4229 ignore_entry = FALSE;
4230 else
4231 ignore_entry = TRUE;
4233 if (!ignore_entry) {
4234 gunichar2 *dir;
4235 glong length;
4236 gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint;
4238 unescape_octal (mountpoint);
4239 dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL);
4240 if (state->total + length + 1 > len) {
4241 quit = TRUE;
4242 state->total = len * 2;
4243 } else {
4244 length++;
4245 memcpy (buf + state->total, dir, sizeof (gunichar2) * length);
4246 state->total += length;
4248 g_free (dir);
4250 state->fsname_index = 0;
4251 state->fstype_index = 0;
4253 return quit;
4255 #else
4256 gint32
4257 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
4259 return GetLogicalDriveStrings_Mtab (len, buf);
4261 #endif
4262 static gint32
4263 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf)
4265 FILE *fp;
4266 gunichar2 *ptr, *dir;
4267 glong length, total = 0;
4268 gchar buffer [512];
4269 gchar **splitted;
4271 memset (buf, 0, sizeof (gunichar2) * (len + 1));
4272 buf [0] = '/';
4273 buf [1] = 0;
4274 buf [2] = 0;
4276 /* Sigh, mntent and friends don't work well.
4277 * It stops on the first line that doesn't begin with a '/'.
4278 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4279 MONO_ENTER_GC_SAFE;
4280 fp = fopen ("/etc/mtab", "rt");
4281 MONO_EXIT_GC_SAFE;
4282 if (fp == NULL) {
4283 MONO_ENTER_GC_SAFE;
4284 fp = fopen ("/etc/mnttab", "rt");
4285 MONO_EXIT_GC_SAFE;
4286 if (fp == NULL)
4287 return 1;
4290 ptr = buf;
4291 while (1) {
4292 gchar *fgets_res;
4293 MONO_ENTER_GC_SAFE;
4294 fgets_res = fgets (buffer, 512, fp);
4295 MONO_EXIT_GC_SAFE;
4296 if (!fgets_res)
4297 break;
4298 if (*buffer != '/')
4299 continue;
4301 splitted = g_strsplit (buffer, " ", 0);
4302 if (!*splitted || !*(splitted + 1)) {
4303 g_strfreev (splitted);
4304 continue;
4307 unescape_octal (*(splitted + 1));
4308 dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
4309 g_strfreev (splitted);
4310 if (total + length + 1 > len) {
4311 MONO_ENTER_GC_SAFE;
4312 fclose (fp);
4313 MONO_EXIT_GC_SAFE;
4314 g_free (dir);
4315 return len * 2; /* guess */
4318 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4319 g_free (dir);
4320 total += length + 1;
4323 MONO_ENTER_GC_SAFE;
4324 fclose (fp);
4325 MONO_EXIT_GC_SAFE;
4326 return total;
4327 /* Commented out, does not work with my mtab!!! - Gonz */
4328 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4330 FILE *fp;
4331 struct mntent *mnt;
4332 gunichar2 *ptr, *dir;
4333 glong len, total = 0;
4336 MONO_ENTER_GC_SAFE;
4337 fp = setmntent ("/etc/mtab", "rt");
4338 MONO_EXIT_GC_SAFE;
4339 if (fp == NULL) {
4340 MONO_ENTER_GC_SAFE;
4341 fp = setmntent ("/etc/mnttab", "rt");
4342 MONO_EXIT_GC_SAFE;
4343 if (fp == NULL)
4344 return;
4347 ptr = buf;
4348 while (1) {
4349 MONO_ENTER_GC_SAFE;
4350 mnt = getmntent (fp);
4351 MONO_EXIT_GC_SAFE;
4352 if (mnt == NULL)
4353 break;
4354 g_print ("GOT %s\n", mnt->mnt_dir);
4355 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4356 if (total + len + 1 > len) {
4357 MONO_ENTER_GC_SAFE;
4358 endmntent (fp);
4359 MONO_EXIT_GC_SAFE;
4360 return len * 2; /* guess */
4363 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4364 g_free (dir);
4365 total += len + 1;
4368 MONO_ENTER_GC_SAFE;
4369 endmntent (fp);
4370 MONO_EXIT_GC_SAFE;
4371 return total;
4373 #endif
4375 #endif
4377 #ifndef PLATFORM_NO_DRIVEINFO
4378 gboolean
4379 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)
4381 g_assert (free_bytes_avail);
4382 g_assert (total_number_of_bytes);
4383 g_assert (total_number_of_free_bytes);
4385 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
4386 #ifdef HAVE_STATVFS
4387 struct statvfs fsstat;
4388 #elif defined(HAVE_STATFS)
4389 struct statfs fsstat;
4390 #endif
4391 gboolean isreadonly;
4392 gchar *utf8_path_name;
4393 gint ret;
4394 unsigned long block_size;
4395 ERROR_DECL (error);
4397 if (path_name == NULL) {
4398 utf8_path_name = g_strdup (g_get_current_dir());
4399 if (utf8_path_name == NULL) {
4400 mono_w32error_set_last (ERROR_DIRECTORY);
4401 return(FALSE);
4404 else {
4405 utf8_path_name = mono_unicode_to_external_checked (path_name, error);
4406 if (utf8_path_name == NULL) {
4407 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
4409 mono_error_cleanup (error);
4410 mono_w32error_set_last (ERROR_INVALID_NAME);
4411 return(FALSE);
4415 do {
4416 #ifdef HAVE_STATVFS
4417 MONO_ENTER_GC_SAFE;
4418 ret = statvfs (utf8_path_name, &fsstat);
4419 MONO_EXIT_GC_SAFE;
4420 isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
4421 block_size = fsstat.f_frsize;
4422 #elif defined(HAVE_STATFS)
4423 MONO_ENTER_GC_SAFE;
4424 ret = statfs (utf8_path_name, &fsstat);
4425 MONO_EXIT_GC_SAFE;
4426 #if defined (MNT_RDONLY)
4427 isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
4428 #elif defined (MS_RDONLY)
4429 isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY);
4430 #endif
4431 block_size = fsstat.f_bsize;
4432 #endif
4433 } while(ret == -1 && errno == EINTR);
4435 g_free(utf8_path_name);
4437 if (ret == -1) {
4438 _wapi_set_last_error_from_errno ();
4439 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: statvfs failed: %s", __func__, g_strerror (errno));
4440 return(FALSE);
4443 /* total number of free bytes for non-root */
4444 if (isreadonly)
4445 *free_bytes_avail = 0;
4446 else
4447 *free_bytes_avail = block_size * (guint64)fsstat.f_bavail;
4449 /* total number of bytes available for non-root */
4450 *total_number_of_bytes = block_size * (guint64)fsstat.f_blocks;
4452 /* total number of bytes available for root */
4453 if (isreadonly)
4454 *total_number_of_free_bytes = 0;
4455 else
4456 *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree;
4457 #endif
4458 return(TRUE);
4460 #endif // PLATFORM_NO_DRIVEINFO
4463 * General Unix support
4465 typedef struct {
4466 guint32 drive_type;
4467 #if __linux__
4468 // http://man7.org/linux/man-pages/man2/statfs.2.html
4470 // The __fsword_t type used for various fields in the statfs structure
4471 // definition is a glibc internal type, not intended for public use.
4472 // This leaves the programmer in a bit of a conundrum when trying to
4473 // copy or compare these fields to local variables in a program. Using
4474 // unsigned int for such variables suffices on most systems.
4476 // Let's hope "most" is enough, and that it works with other libc.
4477 unsigned fstypeid;
4478 #endif
4479 const gchar* fstype;
4480 } _wapi_drive_type;
4482 static const _wapi_drive_type _wapi_drive_types[] = {
4483 #if HOST_DARWIN
4484 { DRIVE_REMOTE, "afp" },
4485 { DRIVE_REMOTE, "afpfs" },
4486 { DRIVE_REMOTE, "autofs" },
4487 { DRIVE_CDROM, "cddafs" },
4488 { DRIVE_CDROM, "cd9660" },
4489 { DRIVE_RAMDISK, "devfs" },
4490 { DRIVE_RAMDISK, "nullfs" },
4491 { DRIVE_FIXED, "exfat" },
4492 { DRIVE_RAMDISK, "fdesc" },
4493 { DRIVE_REMOTE, "ftp" },
4494 { DRIVE_FIXED, "hfs" },
4495 { DRIVE_FIXED, "apfs" },
4496 { DRIVE_REMOTE, "kbfuse" },
4497 { DRIVE_FIXED, "msdos" },
4498 { DRIVE_REMOTE, "nfs" },
4499 { DRIVE_FIXED, "ntfs" },
4500 { DRIVE_REMOTE, "smbfs" },
4501 { DRIVE_FIXED, "udf" },
4502 { DRIVE_REMOTE, "webdav" },
4503 { DRIVE_FIXED, "ufsd_NTFS"},
4504 { DRIVE_UNKNOWN, NULL }
4505 #elif __linux__
4506 { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"},
4507 { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"},
4508 { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"},
4509 { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"},
4510 { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"},
4511 { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" },
4512 { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"},
4513 { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"},
4514 { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"},
4515 { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"},
4516 { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"},
4517 { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"},
4518 { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"},
4519 { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"},
4520 { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"},
4521 { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"},
4522 { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"},
4523 { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"},
4524 { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"},
4525 { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"},
4526 { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"},
4527 { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"},
4528 { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"},
4529 { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"},
4530 { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"},
4531 { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"},
4532 { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"},
4533 { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"},
4534 { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"},
4535 { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"},
4536 { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"},
4537 { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"},
4538 { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"},
4539 { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"},
4540 { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"},
4541 { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"},
4542 { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"},
4543 { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"},
4544 { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"},
4545 { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"},
4546 { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"},
4547 { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"},
4548 { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"},
4549 { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"},
4550 { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"},
4551 { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"},
4552 { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"},
4553 { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"},
4554 { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"},
4555 { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"},
4556 { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"},
4557 { DRIVE_FIXED, UFS_MAGIC, "ufs"},
4558 { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"},
4559 { DRIVE_FIXED, UFS2_MAGIC, "ufs2"},
4560 { DRIVE_FIXED, UFS_CIGAM, "ufs"},
4561 { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"},
4562 { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"},
4563 { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"},
4564 { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"},
4565 { DRIVE_FIXED, V9FS_MAGIC, "9p"},
4566 { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"},
4567 { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"},
4568 { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"},
4569 { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"},
4570 { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"},
4571 { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"},
4572 { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"},
4573 { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"},
4574 { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"},
4575 { DRIVE_FIXED, OMFS_MAGIC, "omfs"},
4576 { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"},
4577 { DRIVE_UNKNOWN, 0, NULL}
4578 #else
4579 { DRIVE_RAMDISK, "ramfs" },
4580 { DRIVE_RAMDISK, "tmpfs" },
4581 { DRIVE_RAMDISK, "proc" },
4582 { DRIVE_RAMDISK, "sysfs" },
4583 { DRIVE_RAMDISK, "debugfs" },
4584 { DRIVE_RAMDISK, "devpts" },
4585 { DRIVE_RAMDISK, "securityfs" },
4586 { DRIVE_RAMDISK, "procfs" }, // AIX procfs
4587 { DRIVE_RAMDISK, "namefs" }, // AIX soft mounts
4588 { DRIVE_RAMDISK, "nullfs" },
4589 { DRIVE_CDROM, "iso9660" },
4590 { DRIVE_CDROM, "cdrfs" }, // AIX ISO9660 CDs
4591 { DRIVE_CDROM, "udfs" }, // AIX UDF CDs
4592 { DRIVE_CDROM, "QOPT" }, // IBM i CD mount
4593 { DRIVE_FIXED, "ext2" },
4594 { DRIVE_FIXED, "ext3" },
4595 { DRIVE_FIXED, "ext4" },
4596 { DRIVE_FIXED, "sysv" },
4597 { DRIVE_FIXED, "reiserfs" },
4598 { DRIVE_FIXED, "ufs" },
4599 { DRIVE_FIXED, "vfat" },
4600 { DRIVE_FIXED, "msdos" },
4601 { DRIVE_FIXED, "udf" },
4602 { DRIVE_FIXED, "hfs" },
4603 { DRIVE_FIXED, "hpfs" },
4604 { DRIVE_FIXED, "qnx4" },
4605 { DRIVE_FIXED, "ntfs" },
4606 { DRIVE_FIXED, "ntfs-3g" },
4607 { DRIVE_FIXED, "jfs" }, // IBM JFS
4608 { DRIVE_FIXED, "jfs2" }, // IBM JFS (AIX defalt filesystem)
4609 { DRIVE_FIXED, "EPFS" }, // IBM i IFS (root and QOpenSys)
4610 { DRIVE_FIXED, "EPFSP" }, // IBM i auxiliary storage pool FS
4611 { DRIVE_FIXED, "QSYS" }, // IBM i native system libraries
4612 { DRIVE_FIXED, "QDLS" }, // IBM i legacy S/36 directories
4613 { DRIVE_REMOTE, "smbfs" },
4614 { DRIVE_REMOTE, "fuse" },
4615 { DRIVE_REMOTE, "nfs" },
4616 { DRIVE_REMOTE, "nfs4" },
4617 { DRIVE_REMOTE, "cifs" },
4618 { DRIVE_REMOTE, "ncpfs" },
4619 { DRIVE_REMOTE, "coda" },
4620 { DRIVE_REMOTE, "afs" },
4621 { DRIVE_REMOTE, "nfs3" },
4622 { DRIVE_REMOTE, "stnfs" }, // AIX "short-term" NFS
4623 { DRIVE_REMOTE, "autofs" }, // AIX automounter NFS
4624 { DRIVE_REMOTE, "cachefs" }, // AIX cached NFS
4625 { DRIVE_REMOTE, "NFS" }, // IBM i NFS
4626 { DRIVE_REMOTE, "QNETC" }, // IBM i CIFS
4627 { DRIVE_REMOTE, "QRFS" }, // IBM i native remote FS
4628 { DRIVE_UNKNOWN, NULL }
4629 #endif
4632 #if __linux__
4633 static guint32 _wapi_get_drive_type(unsigned f_type)
4635 const _wapi_drive_type *current;
4637 current = &_wapi_drive_types[0];
4638 while (current->drive_type != DRIVE_UNKNOWN) {
4639 if (current->fstypeid == f_type)
4640 return current->drive_type;
4641 current++;
4644 return DRIVE_UNKNOWN;
4646 #else
4647 static guint32 _wapi_get_drive_type(const gchar* fstype)
4649 const _wapi_drive_type *current;
4651 current = &_wapi_drive_types[0];
4652 while (current->drive_type != DRIVE_UNKNOWN) {
4653 if (strcmp (current->fstype, fstype) == 0)
4654 return current->drive_type;
4656 current++;
4659 return DRIVE_UNKNOWN;
4661 #endif
4663 #if defined (HOST_DARWIN) || defined (__linux__) || defined (_AIX)
4664 static guint32
4665 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4667 #if defined (_AIX)
4668 struct statvfs buf;
4669 #else
4670 struct statfs buf;
4671 #endif
4672 gint res;
4674 MONO_ENTER_GC_SAFE;
4675 #if defined (_AIX)
4676 res = statvfs (utf8_root_path_name, &buf);
4677 #else
4678 res = statfs (utf8_root_path_name, &buf);
4679 #endif
4680 MONO_EXIT_GC_SAFE;
4681 if (res == -1)
4682 return DRIVE_UNKNOWN;
4683 #if HOST_DARWIN
4684 return _wapi_get_drive_type (buf.f_fstypename);
4685 #elif defined (_AIX)
4686 return _wapi_get_drive_type (buf.f_basetype);
4687 #else
4688 return _wapi_get_drive_type (buf.f_type);
4689 #endif
4691 #else
4692 static guint32
4693 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4695 guint32 drive_type;
4696 FILE *fp;
4697 gchar buffer [512];
4698 gchar **splitted;
4700 MONO_ENTER_GC_SAFE;
4701 fp = fopen ("/etc/mtab", "rt");
4702 MONO_EXIT_GC_SAFE;
4703 if (fp == NULL) {
4704 MONO_ENTER_GC_SAFE;
4705 fp = fopen ("/etc/mnttab", "rt");
4706 MONO_EXIT_GC_SAFE;
4707 if (fp == NULL)
4708 return(DRIVE_UNKNOWN);
4711 drive_type = DRIVE_NO_ROOT_DIR;
4712 while (1) {
4713 gchar *fgets_res;
4714 MONO_ENTER_GC_SAFE;
4715 fgets_res = fgets (buffer, 512, fp);
4716 MONO_EXIT_GC_SAFE;
4717 if (fgets_res == NULL)
4718 break;
4719 splitted = g_strsplit (buffer, " ", 0);
4720 if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
4721 g_strfreev (splitted);
4722 continue;
4725 /* compare given root_path_name with the one from mtab,
4726 if length of utf8_root_path_name is zero it must be the root dir */
4727 if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
4728 (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
4729 drive_type = _wapi_get_drive_type (*(splitted + 2));
4730 /* it is possible this path might be mounted again with
4731 a known type...keep looking */
4732 if (drive_type != DRIVE_UNKNOWN) {
4733 g_strfreev (splitted);
4734 break;
4738 g_strfreev (splitted);
4741 MONO_ENTER_GC_SAFE;
4742 fclose (fp);
4743 MONO_EXIT_GC_SAFE;
4744 return drive_type;
4746 #endif
4748 guint32
4749 ves_icall_System_IO_DriveInfo_GetDriveType (const gunichar2 *root_path_name, gint32 root_path_name_length, MonoError *error)
4751 // FIXME Check for embedded nuls here or in managed.
4753 gchar *utf8_root_path_name;
4754 guint32 drive_type;
4756 if (root_path_name == NULL) {
4757 utf8_root_path_name = g_strdup (g_get_current_dir());
4758 if (utf8_root_path_name == NULL) {
4759 return(DRIVE_NO_ROOT_DIR);
4762 else {
4763 utf8_root_path_name = mono_unicode_to_external_checked (root_path_name, error);
4764 if (utf8_root_path_name == NULL) {
4765 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
4766 return(DRIVE_NO_ROOT_DIR);
4769 /* strip trailing slash for compare below */
4770 if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) {
4771 utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
4774 drive_type = GetDriveTypeFromPath (utf8_root_path_name);
4775 g_free (utf8_root_path_name);
4777 return (drive_type);
4780 #if defined (HOST_DARWIN) || defined (__linux__) || defined(HOST_BSD) || defined(__FreeBSD_kernel__) || defined(__HAIKU__) || defined(_AIX)
4781 static gchar*
4782 get_fstypename (gchar *utfpath)
4784 #if defined (_AIX)
4785 /* statvfs offers the FS type name, easily, no need to iterate */
4786 struct statvfs stat;
4787 gint statvfs_res;
4789 MONO_ENTER_GC_SAFE;
4790 statvfs_res = statvfs (utfpath, &stat);
4791 MONO_EXIT_GC_SAFE;
4793 if (statvfs_res != -1) {
4794 return g_strdup (stat.f_basetype);
4797 return NULL;
4798 #elif defined (HOST_DARWIN) || defined (__linux__)
4799 struct statfs stat;
4800 #if __linux__
4801 const _wapi_drive_type *current;
4802 #endif
4803 gint statfs_res;
4804 MONO_ENTER_GC_SAFE;
4805 statfs_res = statfs (utfpath, &stat);
4806 MONO_EXIT_GC_SAFE;
4807 if (statfs_res == -1)
4808 return NULL;
4809 #if HOST_DARWIN
4810 return g_strdup (stat.f_fstypename);
4811 #else
4812 current = &_wapi_drive_types[0];
4813 while (current->drive_type != DRIVE_UNKNOWN) {
4814 if (stat.f_type == current->fstypeid)
4815 return g_strdup (current->fstype);
4816 current++;
4818 return NULL;
4819 #endif
4820 #else
4821 return NULL;
4822 #endif
4825 /* Linux has struct statfs which has a different layout */
4826 gboolean
4827 mono_w32file_get_file_system_type (const gunichar2 *path, gunichar2 *fsbuffer, gint fsbuffersize)
4829 gchar *utfpath;
4830 gchar *fstypename;
4831 gboolean status = FALSE;
4832 glong len;
4834 // We only support getting the file system type
4835 if (fsbuffer == NULL)
4836 return 0;
4838 utfpath = mono_unicode_to_external (path);
4839 if ((fstypename = get_fstypename (utfpath)) != NULL){
4840 gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL);
4841 if (ret != NULL && len < fsbuffersize){
4842 memcpy (fsbuffer, ret, len * sizeof (gunichar2));
4843 fsbuffer [len] = 0;
4844 status = TRUE;
4846 if (ret != NULL)
4847 g_free (ret);
4848 g_free (fstypename);
4850 g_free (utfpath);
4851 return status;
4853 #endif
4855 static gboolean
4856 LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4858 FileHandle *filehandle;
4859 gboolean ret;
4860 off_t offset, length;
4862 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
4863 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4864 return FALSE;
4867 if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_FILE) {
4868 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4869 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4870 return FALSE;
4873 if (!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
4874 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);
4875 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4876 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4877 return FALSE;
4880 #ifdef HAVE_LARGE_FILE_SUPPORT
4881 offset = ((gint64)offset_high << 32) | offset_low;
4882 length = ((gint64)length_high << 32) | length_low;
4884 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);
4885 #else
4886 if (offset_high > 0 || length_high > 0) {
4887 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
4888 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4889 return FALSE;
4892 offset = offset_low;
4893 length = length_low;
4895 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);
4896 #endif
4898 ret = _wapi_lock_file_region (((MonoFDHandle*) filehandle)->fd, offset, length);
4900 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4901 return ret;
4904 static gboolean
4905 UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4907 FileHandle *filehandle;
4908 gboolean ret;
4909 off_t offset, length;
4911 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
4912 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4913 return FALSE;
4916 if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_FILE) {
4917 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4918 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4919 return FALSE;
4922 if (!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
4923 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);
4924 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4925 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4926 return FALSE;
4929 #ifdef HAVE_LARGE_FILE_SUPPORT
4930 offset = ((gint64)offset_high << 32) | offset_low;
4931 length = ((gint64)length_high << 32) | length_low;
4932 #else
4933 offset = offset_low;
4934 length = length_low;
4935 #endif
4936 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);
4938 ret = _wapi_unlock_file_region (((MonoFDHandle*) filehandle)->fd, offset, length);
4940 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4941 return ret;
4944 void
4945 mono_w32file_init (void)
4947 MonoFDHandleCallback file_data_callbacks;
4948 memset (&file_data_callbacks, 0, sizeof (file_data_callbacks));
4949 file_data_callbacks.close = file_data_close;
4950 file_data_callbacks.destroy = file_data_destroy;
4952 mono_fdhandle_register (MONO_FDTYPE_FILE, &file_data_callbacks);
4953 mono_fdhandle_register (MONO_FDTYPE_CONSOLE, &file_data_callbacks);
4954 mono_fdhandle_register (MONO_FDTYPE_PIPE, &file_data_callbacks);
4956 mono_coop_mutex_init (&file_share_mutex);
4958 finds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, finds_remove);
4959 mono_coop_mutex_init (&finds_mutex);
4961 #if HOST_DARWIN
4962 libc_handle = mono_dl_open ("/usr/lib/libc.dylib", 0, NULL);
4963 g_assert (libc_handle);
4964 g_free (mono_dl_symbol (libc_handle, "clonefile", (void**)&clonefile_ptr));
4965 #endif
4967 if (g_hasenv ("MONO_STRICT_IO_EMULATION"))
4968 lock_while_writing = TRUE;
4971 void
4972 mono_w32file_cleanup (void)
4974 mono_coop_mutex_destroy (&file_share_mutex);
4976 if (file_share_table)
4977 g_hash_table_destroy (file_share_table);
4979 g_hash_table_destroy (finds);
4980 mono_coop_mutex_destroy (&finds_mutex);
4982 #if HOST_DARWIN
4983 mono_dl_close (libc_handle);
4984 #endif
4987 gboolean
4988 mono_w32file_move (const gunichar2 *path, const gunichar2 *dest, gint32 *error)
4990 gboolean result;
4992 result = MoveFile (path, dest);
4993 if (!result)
4994 *error = mono_w32error_get_last ();
4995 return result;
4998 gboolean
4999 mono_w32file_copy (const gunichar2 *path, const gunichar2 *dest, gboolean overwrite, gint32 *error)
5001 gboolean result;
5003 result = CopyFile (path, dest, !overwrite);
5004 if (!result)
5005 *error = mono_w32error_get_last ();
5007 return result;
5010 gboolean
5011 mono_w32file_replace (const gunichar2 *destination_file_name, const gunichar2 *source_file_name, const gunichar2 *destination_backup_file_name, guint32 flags, gint32 *error)
5013 gboolean result;
5015 result = ReplaceFile (destination_file_name, source_file_name, destination_backup_file_name, flags, NULL, NULL);
5016 if (!result)
5017 *error = mono_w32error_get_last ();
5018 return result;
5021 gint64
5022 mono_w32file_get_file_size (gpointer handle, gint32 *error)
5024 gint64 length;
5025 guint32 length_hi;
5027 length = GetFileSize (handle, &length_hi);
5028 if(length==INVALID_FILE_SIZE) {
5029 *error=mono_w32error_get_last ();
5032 return length | ((gint64)length_hi << 32);
5035 gboolean
5036 mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error)
5038 gboolean result;
5040 result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
5041 if (!result)
5042 *error = mono_w32error_get_last ();
5043 return result;
5046 gboolean
5047 mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error)
5049 gboolean result;
5051 result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
5052 if (!result)
5053 *error = mono_w32error_get_last ();
5054 return result;
5057 gpointer
5058 mono_w32file_get_console_input (void)
5060 return mono_w32file_get_std_handle (STD_INPUT_HANDLE);
5063 gpointer
5064 mono_w32file_get_console_output (void)
5066 return mono_w32file_get_std_handle (STD_OUTPUT_HANDLE);
5069 gpointer
5070 mono_w32file_get_console_error (void)
5072 return mono_w32file_get_std_handle (STD_ERROR_HANDLE);