Fixes #12479 - Fixed issue where time lookups on a symlink (GetLastWrite, Creation...
[mono-project.git] / mono / metadata / w32file-unix.c
blobebb44e6aaef7eb6507c7a3adf7d1ccc0478a2f30
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 #include <utime.h>
27 #ifdef __linux__
28 #include <sys/ioctl.h>
29 #include <linux/fs.h>
30 #include <mono/utils/linux_magic.h>
31 #endif
32 #ifdef _AIX
33 #include <sys/mntctl.h>
34 #include <sys/vmount.h>
35 #endif
36 #include <sys/time.h>
37 #ifdef HAVE_DIRENT_H
38 # include <dirent.h>
39 #endif
40 #if HOST_DARWIN
41 #include <dlfcn.h>
42 #endif
44 #include "w32file.h"
45 #include "w32file-internals.h"
46 #include "w32file-unix-glob.h"
47 #include "w32error.h"
48 #include "fdhandle.h"
49 #include "utils/mono-io-portability.h"
50 #include "utils/mono-logger-internals.h"
51 #include "utils/mono-os-mutex.h"
52 #include "utils/mono-threads.h"
53 #include "utils/mono-threads-api.h"
54 #include "utils/strenc.h"
55 #include "utils/refcount.h"
56 #include "icall-decl.h"
58 #define NANOSECONDS_PER_MICROSECOND 1000LL
59 #define TICKS_PER_MICROSECOND 10L
60 #define TICKS_PER_MILLISECOND 10000L
61 #define TICKS_PER_SECOND 10000000LL
62 #define TICKS_PER_MINUTE 600000000LL
63 #define TICKS_PER_HOUR 36000000000LL
64 #define TICKS_PER_DAY 864000000000LL
66 // Constants to convert Unix times to the API expected by .NET and Windows
67 #define CONVERT_BASE 116444736000000000ULL
69 #define INVALID_HANDLE_VALUE ((gpointer)-1)
71 typedef struct {
72 guint64 device;
73 guint64 inode;
74 guint32 sharemode;
75 guint32 access;
76 guint32 handle_refs;
77 guint32 timestamp;
78 } FileShare;
80 /* Currently used for both FILE, CONSOLE and PIPE handle types.
81 * This may have to change in future. */
82 typedef struct {
83 MonoFDHandle fdhandle;
84 gchar *filename;
85 FileShare *share_info; /* Pointer into shared mem */
86 guint32 security_attributes;
87 guint32 fileaccess;
88 guint32 sharemode;
89 guint32 attrs;
90 } FileHandle;
92 typedef struct {
93 MonoRefCount ref;
94 MonoCoopMutex mutex;
95 gchar **namelist;
96 gchar *dir_part;
97 gint num;
98 gsize count;
99 } FindHandle;
102 * If SHM is disabled, this will point to a hash of FileShare structures, otherwise
103 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
104 * 4MB array.
106 static GHashTable *file_share_table;
107 static MonoCoopMutex file_share_mutex;
109 static GHashTable *finds;
110 static MonoCoopMutex finds_mutex;
112 #if HOST_DARWIN
113 typedef int (*clonefile_fn) (const char *from, const char *to, int flags);
114 static MonoDl *libc_handle;
115 static clonefile_fn clonefile_ptr;
116 #endif
118 static void
119 time_t_to_filetime (time_t timeval, FILETIME *filetime)
121 guint64 ticks;
123 ticks = ((guint64)timeval * 10000000) + CONVERT_BASE;
124 filetime->dwLowDateTime = ticks & 0xFFFFFFFF;
125 filetime->dwHighDateTime = ticks >> 32;
128 static FileHandle*
129 file_data_create (MonoFDType type, gint fd)
131 FileHandle *filehandle;
133 filehandle = g_new0 (FileHandle, 1);
134 mono_fdhandle_init ((MonoFDHandle*) filehandle, type, fd);
136 return filehandle;
139 static gint
140 _wapi_unlink (const gchar *pathname);
142 static void
143 file_share_release (FileShare *share_info);
145 static void
146 file_data_close (MonoFDHandle *fdhandle)
148 FileHandle* filehandle;
150 filehandle = (FileHandle*) fdhandle;
151 g_assert (filehandle);
153 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: closing fd %d", __func__, ((MonoFDHandle*) filehandle)->fd);
155 if (((MonoFDHandle*) filehandle)->type == MONO_FDTYPE_FILE && (filehandle->attrs & FILE_FLAG_DELETE_ON_CLOSE)) {
156 _wapi_unlink (filehandle->filename);
159 if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_CONSOLE || ((MonoFDHandle*) filehandle)->fd > 2) {
160 if (filehandle->share_info) {
161 file_share_release (filehandle->share_info);
162 filehandle->share_info = NULL;
165 MONO_ENTER_GC_SAFE;
166 close (((MonoFDHandle*) filehandle)->fd);
167 MONO_EXIT_GC_SAFE;
171 static void
172 file_data_destroy (MonoFDHandle *fdhandle)
174 FileHandle *filehandle;
176 filehandle = (FileHandle*) fdhandle;
177 g_assert (filehandle);
179 if (filehandle->filename)
180 g_free (filehandle->filename);
182 g_free (filehandle);
185 static void
186 file_share_release (FileShare *share_info)
188 /* Prevent new entries racing with us */
189 mono_coop_mutex_lock (&file_share_mutex);
191 g_assert (share_info->handle_refs > 0);
192 share_info->handle_refs -= 1;
194 if (share_info->handle_refs == 0) {
195 g_hash_table_remove (file_share_table, share_info);
196 // g_free (share_info);
199 mono_coop_mutex_unlock (&file_share_mutex);
202 static gint
203 file_share_equal (gconstpointer ka, gconstpointer kb)
205 const FileShare *s1 = (const FileShare *)ka;
206 const FileShare *s2 = (const FileShare *)kb;
208 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
211 static guint
212 file_share_hash (gconstpointer data)
214 const FileShare *s = (const FileShare *)data;
216 return s->inode;
219 static gboolean
220 file_share_get (guint64 device, guint64 inode, guint32 new_sharemode, guint32 new_access,
221 guint32 *old_sharemode, guint32 *old_access, FileShare **share_info)
223 FileShare *file_share;
224 gboolean exists = FALSE;
226 /* Prevent new entries racing with us */
227 mono_coop_mutex_lock (&file_share_mutex);
229 FileShare tmp;
232 * Instead of allocating a 4MB array, we use a hash table to keep track of this
233 * info. This is needed even if SHM is disabled, to track sharing inside
234 * the current process.
236 if (!file_share_table)
237 file_share_table = g_hash_table_new_full (file_share_hash, file_share_equal, NULL, g_free);
239 tmp.device = device;
240 tmp.inode = inode;
242 file_share = (FileShare *)g_hash_table_lookup (file_share_table, &tmp);
243 if (file_share) {
244 *old_sharemode = file_share->sharemode;
245 *old_access = file_share->access;
246 *share_info = file_share;
248 g_assert (file_share->handle_refs > 0);
249 file_share->handle_refs += 1;
251 exists = TRUE;
252 } else {
253 file_share = g_new0 (FileShare, 1);
255 file_share->device = device;
256 file_share->inode = inode;
257 file_share->sharemode = new_sharemode;
258 file_share->access = new_access;
259 file_share->handle_refs = 1;
260 *share_info = file_share;
262 g_hash_table_insert (file_share_table, file_share, file_share);
265 mono_coop_mutex_unlock (&file_share_mutex);
267 return(exists);
270 static gint
271 _wapi_open (const gchar *pathname, gint flags, mode_t mode)
273 gint fd;
274 gchar *located_filename;
276 if (flags & O_CREAT) {
277 located_filename = mono_portability_find_file (pathname, FALSE);
278 if (located_filename == NULL) {
279 MONO_ENTER_GC_SAFE;
280 fd = open (pathname, flags, mode);
281 MONO_EXIT_GC_SAFE;
282 } else {
283 MONO_ENTER_GC_SAFE;
284 fd = open (located_filename, flags, mode);
285 MONO_EXIT_GC_SAFE;
286 g_free (located_filename);
288 } else {
289 MONO_ENTER_GC_SAFE;
290 fd = open (pathname, flags, mode);
291 MONO_EXIT_GC_SAFE;
292 if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
293 gint saved_errno = errno;
294 located_filename = mono_portability_find_file (pathname, TRUE);
296 if (located_filename == NULL) {
297 errno = saved_errno;
298 return -1;
301 MONO_ENTER_GC_SAFE;
302 fd = open (located_filename, flags, mode);
303 MONO_EXIT_GC_SAFE;
304 g_free (located_filename);
308 return(fd);
311 static gint
312 _wapi_access (const gchar *pathname, gint mode)
314 gint ret;
316 MONO_ENTER_GC_SAFE;
317 ret = access (pathname, mode);
318 MONO_EXIT_GC_SAFE;
319 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
320 gint saved_errno = errno;
321 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
323 if (located_filename == NULL) {
324 errno = saved_errno;
325 return -1;
328 MONO_ENTER_GC_SAFE;
329 ret = access (located_filename, mode);
330 MONO_EXIT_GC_SAFE;
331 g_free (located_filename);
334 return ret;
337 static gint
338 _wapi_chmod (const gchar *pathname, mode_t mode)
340 gint ret;
342 MONO_ENTER_GC_SAFE;
343 ret = chmod (pathname, mode);
344 MONO_EXIT_GC_SAFE;
345 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
346 gint saved_errno = errno;
347 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
349 if (located_filename == NULL) {
350 errno = saved_errno;
351 return -1;
354 MONO_ENTER_GC_SAFE;
355 ret = chmod (located_filename, mode);
356 MONO_EXIT_GC_SAFE;
357 g_free (located_filename);
360 return ret;
363 #ifndef HAVE_STRUCT_TIMEVAL
364 static gint
365 _wapi_utime (const gchar *filename, const struct utimbuf *buf)
367 gint ret;
369 MONO_ENTER_GC_SAFE;
370 ret = utime (filename, buf);
371 MONO_EXIT_GC_SAFE;
372 if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) {
373 gint saved_errno = errno;
374 gchar *located_filename = mono_portability_find_file (filename, TRUE);
376 if (located_filename == NULL) {
377 errno = saved_errno;
378 return -1;
381 MONO_ENTER_GC_SAFE;
382 ret = utime (located_filename, buf);
383 MONO_EXIT_GC_SAFE;
384 g_free (located_filename);
387 return ret;
390 #else
391 static gint
392 _wapi_utimes (const gchar *filename, const struct timeval times[2])
394 gint ret;
396 MONO_ENTER_GC_SAFE;
397 ret = utimes (filename, times);
398 MONO_EXIT_GC_SAFE;
399 if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) {
400 gint saved_errno = errno;
401 gchar *located_filename = mono_portability_find_file (filename, TRUE);
403 if (located_filename == NULL) {
404 errno = saved_errno;
405 return -1;
408 MONO_ENTER_GC_SAFE;
409 ret = utimes (located_filename, times);
410 MONO_EXIT_GC_SAFE;
411 g_free (located_filename);
414 return ret;
416 #endif
418 static gint
419 _wapi_unlink (const gchar *pathname)
421 gint ret;
423 MONO_ENTER_GC_SAFE;
424 ret = unlink (pathname);
425 MONO_EXIT_GC_SAFE;
426 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && IS_PORTABILITY_SET) {
427 gint saved_errno = errno;
428 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
430 if (located_filename == NULL) {
431 errno = saved_errno;
432 return -1;
435 MONO_ENTER_GC_SAFE;
436 ret = unlink (located_filename);
437 MONO_EXIT_GC_SAFE;
438 g_free (located_filename);
441 return ret;
444 static gint
445 _wapi_rename (const gchar *oldpath, const gchar *newpath)
447 gint ret;
448 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
450 if (located_newpath == NULL) {
451 MONO_ENTER_GC_SAFE;
452 ret = rename (oldpath, newpath);
453 MONO_EXIT_GC_SAFE;
454 } else {
455 MONO_ENTER_GC_SAFE;
456 ret = rename (oldpath, located_newpath);
457 MONO_EXIT_GC_SAFE;
459 if (ret == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && IS_PORTABILITY_SET) {
460 gint saved_errno = errno;
461 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
463 if (located_oldpath == NULL) {
464 g_free (located_oldpath);
465 g_free (located_newpath);
467 errno = saved_errno;
468 return -1;
471 MONO_ENTER_GC_SAFE;
472 ret = rename (located_oldpath, located_newpath);
473 MONO_EXIT_GC_SAFE;
474 g_free (located_oldpath);
476 g_free (located_newpath);
479 return ret;
482 static gint
483 _wapi_stat (const gchar *path, struct stat *buf)
485 gint ret;
487 MONO_ENTER_GC_SAFE;
488 ret = stat (path, buf);
489 MONO_EXIT_GC_SAFE;
490 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
491 gint saved_errno = errno;
492 gchar *located_filename = mono_portability_find_file (path, TRUE);
494 if (located_filename == NULL) {
495 errno = saved_errno;
496 return -1;
499 MONO_ENTER_GC_SAFE;
500 ret = stat (located_filename, buf);
501 MONO_EXIT_GC_SAFE;
502 g_free (located_filename);
505 return ret;
508 static gint
509 _wapi_lstat (const gchar *path, struct stat *buf)
511 gint ret;
513 MONO_ENTER_GC_SAFE;
514 ret = lstat (path, buf);
515 MONO_EXIT_GC_SAFE;
516 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
517 gint saved_errno = errno;
518 gchar *located_filename = mono_portability_find_file (path, TRUE);
520 if (located_filename == NULL) {
521 errno = saved_errno;
522 return -1;
525 ret = lstat (located_filename, buf);
526 g_free (located_filename);
529 return ret;
532 static gint
533 _wapi_mkdir (const gchar *pathname, mode_t mode)
535 gint ret;
536 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
538 if (located_filename == NULL) {
539 MONO_ENTER_GC_SAFE;
540 ret = mkdir (pathname, mode);
541 MONO_EXIT_GC_SAFE;
542 } else {
543 MONO_ENTER_GC_SAFE;
544 ret = mkdir (located_filename, mode);
545 MONO_EXIT_GC_SAFE;
546 g_free (located_filename);
549 return ret;
552 static gint
553 _wapi_rmdir (const gchar *pathname)
555 gint ret;
557 MONO_ENTER_GC_SAFE;
558 ret = rmdir (pathname);
559 MONO_EXIT_GC_SAFE;
560 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
561 gint saved_errno = errno;
562 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
564 if (located_filename == NULL) {
565 errno = saved_errno;
566 return -1;
569 MONO_ENTER_GC_SAFE;
570 ret = rmdir (located_filename);
571 MONO_EXIT_GC_SAFE;
572 g_free (located_filename);
575 return ret;
578 static gint
579 _wapi_chdir (const gchar *path)
581 gint ret;
583 MONO_ENTER_GC_SAFE;
584 ret = chdir (path);
585 MONO_EXIT_GC_SAFE;
586 if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
587 gint saved_errno = errno;
588 gchar *located_filename = mono_portability_find_file (path, TRUE);
590 if (located_filename == NULL) {
591 errno = saved_errno;
592 return -1;
595 MONO_ENTER_GC_SAFE;
596 ret = chdir (located_filename);
597 MONO_EXIT_GC_SAFE;
598 g_free (located_filename);
601 return ret;
604 static gchar*
605 _wapi_basename (const gchar *filename)
607 gchar *new_filename = g_strdup (filename), *ret;
609 if (IS_PORTABILITY_SET)
610 g_strdelimit (new_filename, '\\', '/');
612 if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
613 gint len = strlen (new_filename);
615 g_memmove (new_filename, new_filename + 2, len - 2);
616 new_filename[len - 2] = '\0';
619 ret = g_path_get_basename (new_filename);
620 g_free (new_filename);
622 return ret;
625 static gchar*
626 _wapi_dirname (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_dirname (new_filename);
641 g_free (new_filename);
643 return ret;
646 static GDir*
647 _wapi_g_dir_open (const gchar *path, guint flags, GError **gerror)
649 GDir *ret;
651 MONO_ENTER_GC_SAFE;
652 ret = g_dir_open (path, flags, gerror);
653 MONO_EXIT_GC_SAFE;
654 if (ret == NULL && ((*gerror)->code == G_FILE_ERROR_NOENT || (*gerror)->code == G_FILE_ERROR_NOTDIR || (*gerror)->code == G_FILE_ERROR_NAMETOOLONG) && IS_PORTABILITY_SET) {
655 gchar *located_filename = mono_portability_find_file (path, TRUE);
656 GError *tmp_error = NULL;
658 if (located_filename == NULL) {
659 return(NULL);
662 MONO_ENTER_GC_SAFE;
663 ret = g_dir_open (located_filename, flags, &tmp_error);
664 MONO_EXIT_GC_SAFE;
665 g_free (located_filename);
666 if (tmp_error == NULL) {
667 g_clear_error (gerror);
671 return ret;
674 static gint
675 get_errno_from_g_file_error (gint error)
677 switch (error) {
678 #ifdef EACCES
679 case G_FILE_ERROR_ACCES: return EACCES;
680 #endif
681 #ifdef ENAMETOOLONG
682 case G_FILE_ERROR_NAMETOOLONG: return ENAMETOOLONG;
683 #endif
684 #ifdef ENOENT
685 case G_FILE_ERROR_NOENT: return ENOENT;
686 #endif
687 #ifdef ENOTDIR
688 case G_FILE_ERROR_NOTDIR: return ENOTDIR;
689 #endif
690 #ifdef ENXIO
691 case G_FILE_ERROR_NXIO: return ENXIO;
692 #endif
693 #ifdef ENODEV
694 case G_FILE_ERROR_NODEV: return ENODEV;
695 #endif
696 #ifdef EROFS
697 case G_FILE_ERROR_ROFS: return EROFS;
698 #endif
699 #ifdef ETXTBSY
700 case G_FILE_ERROR_TXTBSY: return ETXTBSY;
701 #endif
702 #ifdef EFAULT
703 case G_FILE_ERROR_FAULT: return EFAULT;
704 #endif
705 #ifdef ELOOP
706 case G_FILE_ERROR_LOOP: return ELOOP;
707 #endif
708 #ifdef ENOSPC
709 case G_FILE_ERROR_NOSPC: return ENOSPC;
710 #endif
711 #ifdef ENOMEM
712 case G_FILE_ERROR_NOMEM: return ENOMEM;
713 #endif
714 #ifdef EMFILE
715 case G_FILE_ERROR_MFILE: return EMFILE;
716 #endif
717 #ifdef ENFILE
718 case G_FILE_ERROR_NFILE: return ENFILE;
719 #endif
720 #ifdef EBADF
721 case G_FILE_ERROR_BADF: return EBADF;
722 #endif
723 #ifdef EINVAL
724 case G_FILE_ERROR_INVAL: return EINVAL;
725 #endif
726 #ifdef EPIPE
727 case G_FILE_ERROR_PIPE: return EPIPE;
728 #endif
729 #ifdef EAGAIN
730 case G_FILE_ERROR_AGAIN: return EAGAIN;
731 #endif
732 #ifdef EINTR
733 case G_FILE_ERROR_INTR: return EINTR;
734 #endif
735 #ifdef EIO
736 case G_FILE_ERROR_IO: return EIO;
737 #endif
738 #ifdef EPERM
739 case G_FILE_ERROR_PERM: return EPERM;
740 #endif
741 case G_FILE_ERROR_FAILED: return ERROR_INVALID_PARAMETER;
742 default:
743 g_assert_not_reached ();
747 static gint
748 file_compare (gconstpointer a, gconstpointer b)
750 gchar *astr = *(gchar **) a;
751 gchar *bstr = *(gchar **) b;
753 return strcmp (astr, bstr);
756 /* scandir using glib */
757 static gint
758 _wapi_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
760 GError *gerror = NULL;
761 GDir *dir;
762 GPtrArray *names;
763 gint result;
764 mono_w32file_unix_glob_t glob_buf;
765 gint flags = 0, i;
767 dir = _wapi_g_dir_open (dirname, 0, &gerror);
768 if (dir == NULL) {
769 /* g_dir_open returns ENOENT on directories on which we don't
770 * have read/x permission */
771 gint errnum = get_errno_from_g_file_error (gerror->code);
772 g_error_free (gerror);
773 if (errnum == ENOENT &&
774 !_wapi_access (dirname, F_OK) &&
775 _wapi_access (dirname, R_OK|X_OK)) {
776 errnum = EACCES;
779 errno = errnum;
780 return -1;
783 if (IS_PORTABILITY_CASE) {
784 flags = W32FILE_UNIX_GLOB_IGNORECASE;
787 result = mono_w32file_unix_glob (dir, pattern, flags, &glob_buf);
788 if (g_str_has_suffix (pattern, ".*")) {
789 /* Special-case the patterns ending in '.*', as
790 * windows also matches entries with no extension with
791 * this pattern.
793 * TODO: should this be a MONO_IOMAP option?
795 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
796 gint result2;
798 MONO_ENTER_GC_SAFE;
799 g_dir_rewind (dir);
800 MONO_EXIT_GC_SAFE;
801 result2 = mono_w32file_unix_glob (dir, pattern2, flags | W32FILE_UNIX_GLOB_APPEND | W32FILE_UNIX_GLOB_UNIQUE, &glob_buf);
803 g_free (pattern2);
805 if (result != 0) {
806 result = result2;
810 MONO_ENTER_GC_SAFE;
811 g_dir_close (dir);
812 MONO_EXIT_GC_SAFE;
813 if (glob_buf.gl_pathc == 0) {
814 return(0);
815 } else if (result != 0) {
816 return -1;
819 names = g_ptr_array_new ();
820 for (i = 0; i < glob_buf.gl_pathc; i++) {
821 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
824 mono_w32file_unix_globfree (&glob_buf);
826 result = names->len;
827 if (result > 0) {
828 g_ptr_array_sort (names, file_compare);
829 g_ptr_array_set_size (names, result + 1);
831 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
832 } else {
833 g_ptr_array_free (names, TRUE);
836 return result;
839 static gboolean
840 _wapi_lock_file_region (gint fd, off_t offset, off_t length)
842 struct flock lock_data;
843 gint ret;
845 if (offset < 0 || length < 0) {
846 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
847 return FALSE;
850 lock_data.l_type = F_WRLCK;
851 lock_data.l_whence = SEEK_SET;
852 lock_data.l_start = offset;
853 lock_data.l_len = length;
855 do {
856 ret = fcntl (fd, F_SETLK, &lock_data);
857 } while(ret == -1 && errno == EINTR);
859 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl returns %d", __func__, ret);
861 if (ret == -1) {
863 * if locks are not available (NFS for example),
864 * ignore the error
866 if (errno == ENOLCK
867 #ifdef EOPNOTSUPP
868 || errno == EOPNOTSUPP
869 #endif
870 #ifdef ENOTSUP
871 || errno == ENOTSUP
872 #endif
874 return TRUE;
877 mono_w32error_set_last (ERROR_LOCK_VIOLATION);
878 return FALSE;
881 return TRUE;
884 static gboolean
885 _wapi_unlock_file_region (gint fd, off_t offset, off_t length)
887 struct flock lock_data;
888 gint ret;
890 lock_data.l_type = F_UNLCK;
891 lock_data.l_whence = SEEK_SET;
892 lock_data.l_start = offset;
893 lock_data.l_len = length;
895 do {
896 ret = fcntl (fd, F_SETLK, &lock_data);
897 } while(ret == -1 && errno == EINTR);
899 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl returns %d", __func__, ret);
901 if (ret == -1) {
903 * if locks are not available (NFS for example),
904 * ignore the error
906 if (errno == ENOLCK
907 #ifdef EOPNOTSUPP
908 || errno == EOPNOTSUPP
909 #endif
910 #ifdef ENOTSUP
911 || errno == ENOTSUP
912 #endif
914 return TRUE;
917 mono_w32error_set_last (ERROR_LOCK_VIOLATION);
918 return FALSE;
921 return TRUE;
924 static gboolean lock_while_writing = FALSE;
926 /* Some utility functions.
930 * Check if a file is writable by the current user.
932 * This is is a best effort kind of thing. It assumes a reasonable sane set
933 * of permissions by the underlying OS.
935 * We generally assume that basic unix permission bits are authoritative. Which might not
936 * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc)
938 * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file.
940 * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent
941 * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous
942 * and should not be used with a dynamic runtime.
944 static gboolean
945 is_file_writable (struct stat *st, const gchar *path)
947 gboolean ret;
948 gchar *located_path;
950 #if __APPLE__
951 // OS X Finder "locked" or `ls -lO` "uchg".
952 // This only covers one of several cases where an OS X file could be unwritable through special flags.
953 if (st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE))
954 return 0;
955 #endif
957 /* Is it globally writable? */
958 if (st->st_mode & S_IWOTH)
959 return 1;
961 /* Am I the owner? */
962 if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR))
963 return 1;
965 /* Am I in the same group? */
966 if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP))
967 return 1;
969 located_path = mono_portability_find_file (path, FALSE);
971 /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid
972 * but it's the only sane option we have on unix.
974 MONO_ENTER_GC_SAFE;
975 ret = access (located_path != NULL ? located_path : path, W_OK) == 0;
976 MONO_EXIT_GC_SAFE;
978 g_free (located_path);
980 return ret;
984 static guint32 _wapi_stat_to_file_attributes (const gchar *pathname,
985 struct stat *buf,
986 struct stat *lbuf)
988 guint32 attrs = 0;
989 gchar *filename;
991 /* FIXME: this could definitely be better, but there seems to
992 * be no pattern to the attributes that are set
995 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
996 if (S_ISSOCK (buf->st_mode))
997 buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */
999 filename = _wapi_basename (pathname);
1001 if (S_ISDIR (buf->st_mode)) {
1002 attrs = FILE_ATTRIBUTE_DIRECTORY;
1003 if (!is_file_writable (buf, pathname)) {
1004 attrs |= FILE_ATTRIBUTE_READONLY;
1006 if (filename[0] == '.') {
1007 attrs |= FILE_ATTRIBUTE_HIDDEN;
1009 } else {
1010 if (!is_file_writable (buf, pathname)) {
1011 attrs = FILE_ATTRIBUTE_READONLY;
1013 if (filename[0] == '.') {
1014 attrs |= FILE_ATTRIBUTE_HIDDEN;
1016 } else if (filename[0] == '.') {
1017 attrs = FILE_ATTRIBUTE_HIDDEN;
1018 } else {
1019 attrs = FILE_ATTRIBUTE_NORMAL;
1023 if (lbuf != NULL) {
1024 if (S_ISLNK (lbuf->st_mode)) {
1025 attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
1029 g_free (filename);
1031 return attrs;
1034 static void
1035 _wapi_set_last_error_from_errno (void)
1037 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
1040 static void _wapi_set_last_path_error_from_errno (const gchar *dir,
1041 const gchar *path)
1043 if (errno == ENOENT) {
1044 /* Check the path - if it's a missing directory then
1045 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
1047 gchar *dirname;
1050 if (dir == NULL) {
1051 dirname = _wapi_dirname (path);
1052 } else {
1053 dirname = g_strdup (dir);
1056 if (_wapi_access (dirname, F_OK) == 0) {
1057 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
1058 } else {
1059 mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
1062 g_free (dirname);
1063 } else {
1064 _wapi_set_last_error_from_errno ();
1068 static gboolean
1069 file_read(FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1071 gint ret;
1072 MonoThreadInfo *info = mono_thread_info_current ();
1074 if(bytesread!=NULL) {
1075 *bytesread=0;
1078 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) {
1079 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);
1081 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1082 return(FALSE);
1085 do {
1086 MONO_ENTER_GC_SAFE;
1087 ret = read (((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1088 MONO_EXIT_GC_SAFE;
1089 } while (ret == -1 && errno == EINTR &&
1090 !mono_thread_info_is_interrupt_state (info));
1092 if(ret==-1) {
1093 gint err = errno;
1095 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));
1096 mono_w32error_set_last (mono_w32error_unix_to_win32 (err));
1097 return(FALSE);
1100 if (bytesread != NULL) {
1101 *bytesread = ret;
1104 return(TRUE);
1107 static gboolean
1108 file_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *byteswritten)
1110 gint ret;
1111 off_t current_pos = 0;
1112 MonoThreadInfo *info = mono_thread_info_current ();
1114 if(byteswritten!=NULL) {
1115 *byteswritten=0;
1118 if(!(filehandle->fileaccess & GENERIC_WRITE) && !(filehandle->fileaccess & GENERIC_ALL)) {
1119 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);
1121 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1122 return(FALSE);
1125 if (lock_while_writing) {
1126 /* Need to lock the region we're about to write to,
1127 * because we only do advisory locking on POSIX
1128 * systems
1130 MONO_ENTER_GC_SAFE;
1131 current_pos = lseek (((MonoFDHandle*) filehandle)->fd, (off_t)0, SEEK_CUR);
1132 MONO_EXIT_GC_SAFE;
1133 if (current_pos == -1) {
1134 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror (errno));
1135 _wapi_set_last_error_from_errno ();
1136 return(FALSE);
1139 if (_wapi_lock_file_region (((MonoFDHandle*) filehandle)->fd, current_pos, numbytes) == FALSE) {
1140 /* The error has already been set */
1141 return(FALSE);
1145 do {
1146 MONO_ENTER_GC_SAFE;
1147 ret = write (((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1148 MONO_EXIT_GC_SAFE;
1149 } while (ret == -1 && errno == EINTR &&
1150 !mono_thread_info_is_interrupt_state (info));
1152 if (lock_while_writing) {
1153 _wapi_unlock_file_region (((MonoFDHandle*) filehandle)->fd, current_pos, numbytes);
1156 if (ret == -1) {
1157 if (errno == EINTR) {
1158 ret = 0;
1159 } else {
1160 _wapi_set_last_error_from_errno ();
1162 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));
1164 return(FALSE);
1167 if (byteswritten != NULL) {
1168 *byteswritten = ret;
1170 return(TRUE);
1173 static gboolean file_flush(FileHandle *filehandle)
1175 gint ret;
1177 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1178 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);
1180 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1181 return(FALSE);
1184 MONO_ENTER_GC_SAFE;
1185 ret=fsync(((MonoFDHandle*) filehandle)->fd);
1186 MONO_EXIT_GC_SAFE;
1187 if (ret==-1) {
1188 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));
1190 _wapi_set_last_error_from_errno ();
1191 return(FALSE);
1194 return(TRUE);
1197 static guint32 file_seek(FileHandle *filehandle, gint32 movedistance,
1198 gint32 *highmovedistance, gint method)
1200 gint64 offset, newpos;
1201 gint whence;
1202 guint32 ret;
1204 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
1205 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);
1207 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1208 return(INVALID_SET_FILE_POINTER);
1211 switch(method) {
1212 case FILE_BEGIN:
1213 whence=SEEK_SET;
1214 break;
1215 case FILE_CURRENT:
1216 whence=SEEK_CUR;
1217 break;
1218 case FILE_END:
1219 whence=SEEK_END;
1220 break;
1221 default:
1222 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: invalid seek type %d", __func__, method);
1224 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1225 return(INVALID_SET_FILE_POINTER);
1228 #ifdef HAVE_LARGE_FILE_SUPPORT
1229 if(highmovedistance==NULL) {
1230 offset=movedistance;
1231 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: setting offset to %" G_GINT64_FORMAT " (low %" G_GINT32_FORMAT ")", __func__,
1232 offset, movedistance);
1233 } else {
1234 offset=((gint64) *highmovedistance << 32) | (guint32)movedistance;
1236 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 ")",
1237 __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
1239 #else
1240 offset=movedistance;
1241 #endif
1243 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);
1245 #ifdef HOST_ANDROID
1246 /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */
1247 MONO_ENTER_GC_SAFE;
1248 newpos=lseek64(((MonoFDHandle*) filehandle)->fd, offset, whence);
1249 MONO_EXIT_GC_SAFE;
1250 #else
1251 MONO_ENTER_GC_SAFE;
1252 newpos=lseek(((MonoFDHandle*) filehandle)->fd, offset, whence);
1253 MONO_EXIT_GC_SAFE;
1254 #endif
1255 if(newpos==-1) {
1256 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));
1258 _wapi_set_last_error_from_errno ();
1259 return(INVALID_SET_FILE_POINTER);
1262 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lseek returns %" G_GINT64_FORMAT, __func__, newpos);
1264 #ifdef HAVE_LARGE_FILE_SUPPORT
1265 ret=newpos & 0xFFFFFFFF;
1266 if(highmovedistance!=NULL) {
1267 *highmovedistance=newpos>>32;
1269 #else
1270 ret=newpos;
1271 if(highmovedistance!=NULL) {
1272 /* Accurate, but potentially dodgy :-) */
1273 *highmovedistance=0;
1275 #endif
1277 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);
1279 return(ret);
1282 static gboolean file_setendoffile(FileHandle *filehandle)
1284 struct stat statbuf;
1285 off_t pos;
1286 gint ret;
1287 MonoThreadInfo *info = mono_thread_info_current ();
1289 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1290 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);
1292 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1293 return(FALSE);
1296 /* Find the current file position, and the file length. If
1297 * the file position is greater than the length, write to
1298 * extend the file with a hole. If the file position is less
1299 * than the length, truncate the file.
1302 MONO_ENTER_GC_SAFE;
1303 ret=fstat(((MonoFDHandle*) filehandle)->fd, &statbuf);
1304 MONO_EXIT_GC_SAFE;
1305 if(ret==-1) {
1306 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1308 _wapi_set_last_error_from_errno ();
1309 return(FALSE);
1312 MONO_ENTER_GC_SAFE;
1313 pos=lseek(((MonoFDHandle*) filehandle)->fd, (off_t)0, SEEK_CUR);
1314 MONO_EXIT_GC_SAFE;
1315 if(pos==-1) {
1316 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1318 _wapi_set_last_error_from_errno ();
1319 return(FALSE);
1322 #ifdef FTRUNCATE_DOESNT_EXTEND
1323 off_t size = statbuf.st_size;
1324 /* I haven't bothered to write the configure.ac stuff for this
1325 * because I don't know if any platform needs it. I'm leaving
1326 * this code just in case though
1328 if(pos>size) {
1329 /* Extend the file. Use write() here, because some
1330 * manuals say that ftruncate() behaviour is undefined
1331 * when the file needs extending. The POSIX spec says
1332 * that on XSI-conformant systems it extends, so if
1333 * every system we care about conforms, then we can
1334 * drop this write.
1336 do {
1337 MONO_ENTER_GC_SAFE;
1338 ret = write (((MonoFDHandle*) filehandle)->fd, "", 1);
1339 MONO_EXIT_GC_SAFE;
1340 } while (ret == -1 && errno == EINTR &&
1341 !mono_thread_info_is_interrupt_state (info));
1343 if(ret==-1) {
1344 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));
1346 _wapi_set_last_error_from_errno ();
1347 return(FALSE);
1350 /* And put the file position back after the write */
1351 MONO_ENTER_GC_SAFE;
1352 ret = lseek (((MonoFDHandle*) filehandle)->fd, pos, SEEK_SET);
1353 MONO_EXIT_GC_SAFE;
1354 if (ret == -1) {
1355 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));
1357 _wapi_set_last_error_from_errno ();
1358 return(FALSE);
1361 #endif
1363 /* always truncate, because the extend write() adds an extra
1364 * byte to the end of the file
1366 do {
1367 MONO_ENTER_GC_SAFE;
1368 ret=ftruncate(((MonoFDHandle*) filehandle)->fd, pos);
1369 MONO_EXIT_GC_SAFE;
1371 while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1372 if(ret==-1) {
1373 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d ftruncate failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1375 _wapi_set_last_error_from_errno ();
1376 return(FALSE);
1379 return(TRUE);
1382 static guint32 file_getfilesize(FileHandle *filehandle, guint32 *highsize)
1384 struct stat statbuf;
1385 guint32 size;
1386 gint ret;
1388 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
1389 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);
1391 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1392 return(INVALID_FILE_SIZE);
1395 /* If the file has a size with the low bits 0xFFFFFFFF the
1396 * caller can't tell if this is an error, so clear the error
1397 * value
1399 mono_w32error_set_last (ERROR_SUCCESS);
1401 MONO_ENTER_GC_SAFE;
1402 ret = fstat(((MonoFDHandle*) filehandle)->fd, &statbuf);
1403 MONO_EXIT_GC_SAFE;
1404 if (ret == -1) {
1405 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1407 _wapi_set_last_error_from_errno ();
1408 return(INVALID_FILE_SIZE);
1411 /* fstat indicates block devices as zero-length, so go a different path */
1412 #ifdef BLKGETSIZE64
1413 if (S_ISBLK(statbuf.st_mode)) {
1414 guint64 bigsize;
1415 gint res;
1416 MONO_ENTER_GC_SAFE;
1417 res = ioctl (((MonoFDHandle*) filehandle)->fd, BLKGETSIZE64, &bigsize);
1418 MONO_EXIT_GC_SAFE;
1419 if (res < 0) {
1420 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));
1422 _wapi_set_last_error_from_errno ();
1423 return(INVALID_FILE_SIZE);
1426 size = bigsize & 0xFFFFFFFF;
1427 if (highsize != NULL) {
1428 *highsize = bigsize>>32;
1431 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning block device size %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT,
1432 __func__, size, *highsize);
1434 return(size);
1436 #endif
1438 #ifdef HAVE_LARGE_FILE_SUPPORT
1439 size = statbuf.st_size & 0xFFFFFFFF;
1440 if (highsize != NULL) {
1441 *highsize = statbuf.st_size>>32;
1443 #else
1444 if (highsize != NULL) {
1445 /* Accurate, but potentially dodgy :-) */
1446 *highsize = 0;
1448 size = statbuf.st_size;
1449 #endif
1451 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning size %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT, __func__, size, *highsize);
1453 return(size);
1456 static guint64
1457 convert_unix_filetime_ms (const FILETIME *file_time, const char *ttype)
1459 guint64 t = ((guint64) file_time->dwHighDateTime << 32) + file_time->dwLowDateTime;
1461 /* This is (time_t)0. We can actually go to INT_MIN,
1462 * but this will do for now.
1464 if (t < CONVERT_BASE) {
1465 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: attempt to set %s time too early", __func__, ttype);
1466 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1467 return (FALSE);
1470 return t - CONVERT_BASE;
1473 #ifndef HAVE_STRUCT_TIMEVAL
1474 static guint64
1475 convert_unix_filetime (const FILETIME *file_time, size_t field_size, const char *ttype)
1477 guint64 t = convert_unix_filetime_ms (file_time, ttype);
1479 if (field_size == 4 && (t / 10000000) > INT_MAX) {
1480 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);
1481 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1482 return (FALSE);
1485 return t / 10000000;
1487 #endif
1489 static void convert_stattime_access_to_timeval (struct timeval *dest, struct stat *statbuf)
1491 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
1492 dest->tv_sec = statbuf->st_atimespec.tv_sec;
1493 dest->tv_usec = statbuf->st_atimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND;
1494 #else
1495 dest->tv_sec = statbuf->st_atime;
1496 #if HAVE_STRUCT_STAT_ST_ATIM
1497 dest->tv_usec = statbuf->st_atim.tv_nsec / NANOSECONDS_PER_MICROSECOND;
1498 #endif
1499 #endif
1502 static void convert_stattime_mod_to_timeval (struct timeval *dest, struct stat *statbuf)
1504 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
1505 dest->tv_sec = statbuf->st_mtimespec.tv_sec;
1506 dest->tv_usec = statbuf->st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND;
1507 #else
1508 dest->tv_sec = statbuf->st_mtime;
1509 #if HAVE_STRUCT_STAT_ST_ATIM
1510 dest->tv_usec = statbuf->st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND;
1511 #endif
1512 #endif
1515 static gboolean file_setfiletime(FileHandle *filehandle,
1516 const FILETIME *create_time G_GNUC_UNUSED,
1517 const FILETIME *access_time,
1518 const FILETIME *write_time)
1520 struct stat statbuf;
1521 gint ret;
1523 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1524 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);
1526 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1527 return(FALSE);
1530 if(filehandle->filename == NULL) {
1531 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d unknown filename", __func__, ((MonoFDHandle*) filehandle)->fd);
1533 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1534 return(FALSE);
1537 /* Get the current times, so we can put the same times back in
1538 * the event that one of the FileTime structs is NULL
1540 MONO_ENTER_GC_SAFE;
1541 ret=fstat (((MonoFDHandle*) filehandle)->fd, &statbuf);
1542 MONO_EXIT_GC_SAFE;
1543 if(ret==-1) {
1544 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d fstat failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror(errno));
1546 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1547 return(FALSE);
1550 #ifdef HAVE_STRUCT_TIMEVAL
1551 struct timeval times [2];
1552 memset (times, 0, sizeof (times));
1554 if (access_time) {
1555 guint64 actime = convert_unix_filetime_ms (access_time, "access");
1556 times [0].tv_sec = actime / TICKS_PER_SECOND;
1557 times [0].tv_usec = (actime % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND;
1558 } else {
1559 convert_stattime_access_to_timeval (&times [0], &statbuf);
1562 if (write_time) {
1563 guint64 wtime = convert_unix_filetime_ms (write_time, "write");
1564 times [1].tv_sec = wtime / TICKS_PER_SECOND;
1565 times [1].tv_usec = (wtime % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND;
1566 } else {
1567 convert_stattime_mod_to_timeval (&times [1], &statbuf);
1570 ret = _wapi_utimes (filehandle->filename, times);
1571 #else
1572 struct utimbuf utbuf;
1573 utbuf.actime = access_time ? convert_unix_filetime (access_time, sizeof (utbuf.actime), "access") : statbuf.st_atime;
1574 utbuf.modtime = write_time ? convert_unix_filetime (write_time, sizeof (utbuf.modtime), "write") : statbuf.st_mtime;
1576 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);
1578 ret = _wapi_utime (filehandle->filename, &utbuf);
1579 #endif
1580 if (ret == -1) {
1581 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));
1583 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1584 return(FALSE);
1587 return(TRUE);
1590 static gboolean
1591 console_read(FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1593 gint ret;
1594 MonoThreadInfo *info = mono_thread_info_current ();
1596 if(bytesread!=NULL) {
1597 *bytesread=0;
1600 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) {
1601 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);
1603 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1604 return(FALSE);
1607 do {
1608 MONO_ENTER_GC_SAFE;
1609 ret=read(((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1610 MONO_EXIT_GC_SAFE;
1611 } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1613 if(ret==-1) {
1614 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));
1616 _wapi_set_last_error_from_errno ();
1617 return(FALSE);
1620 if(bytesread!=NULL) {
1621 *bytesread=ret;
1624 return(TRUE);
1627 static gboolean
1628 console_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *byteswritten)
1630 gint ret;
1631 MonoThreadInfo *info = mono_thread_info_current ();
1633 if(byteswritten!=NULL) {
1634 *byteswritten=0;
1637 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1638 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);
1640 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1641 return(FALSE);
1644 do {
1645 MONO_ENTER_GC_SAFE;
1646 ret = write(((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1647 MONO_EXIT_GC_SAFE;
1648 } while (ret == -1 && errno == EINTR &&
1649 !mono_thread_info_is_interrupt_state (info));
1651 if (ret == -1) {
1652 if (errno == EINTR) {
1653 ret = 0;
1654 } else {
1655 _wapi_set_last_error_from_errno ();
1657 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));
1659 return(FALSE);
1662 if(byteswritten!=NULL) {
1663 *byteswritten=ret;
1666 return(TRUE);
1669 static gboolean
1670 pipe_read (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1672 gint ret;
1673 MonoThreadInfo *info = mono_thread_info_current ();
1675 if(bytesread!=NULL) {
1676 *bytesread=0;
1679 if(!(filehandle->fileaccess & (GENERIC_READ | GENERIC_ALL))) {
1680 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);
1682 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1683 return(FALSE);
1686 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);
1688 do {
1689 MONO_ENTER_GC_SAFE;
1690 ret=read(((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1691 MONO_EXIT_GC_SAFE;
1692 } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1694 if (ret == -1) {
1695 if (errno == EINTR) {
1696 ret = 0;
1697 } else {
1698 _wapi_set_last_error_from_errno ();
1700 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));
1702 return(FALSE);
1706 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: read %d bytes from pipe %d", __func__, ret, ((MonoFDHandle*) filehandle)->fd);
1708 if(bytesread!=NULL) {
1709 *bytesread=ret;
1712 return(TRUE);
1715 static gboolean
1716 pipe_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *byteswritten)
1718 gint ret;
1719 MonoThreadInfo *info = mono_thread_info_current ();
1721 if(byteswritten!=NULL) {
1722 *byteswritten=0;
1725 if(!(filehandle->fileaccess & (GENERIC_WRITE | GENERIC_ALL))) {
1726 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);
1728 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1729 return(FALSE);
1732 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);
1734 do {
1735 MONO_ENTER_GC_SAFE;
1736 ret = write (((MonoFDHandle*) filehandle)->fd, buffer, numbytes);
1737 MONO_EXIT_GC_SAFE;
1738 } while (ret == -1 && errno == EINTR &&
1739 !mono_thread_info_is_interrupt_state (info));
1741 if (ret == -1) {
1742 if (errno == EINTR) {
1743 ret = 0;
1744 } else {
1745 _wapi_set_last_error_from_errno ();
1747 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));
1749 return(FALSE);
1752 if(byteswritten!=NULL) {
1753 *byteswritten=ret;
1756 return(TRUE);
1759 static gint convert_flags(guint32 fileaccess, guint32 createmode)
1761 gint flags=0;
1763 switch(fileaccess) {
1764 case GENERIC_READ:
1765 flags=O_RDONLY;
1766 break;
1767 case GENERIC_WRITE:
1768 flags=O_WRONLY;
1769 break;
1770 case GENERIC_READ|GENERIC_WRITE:
1771 flags=O_RDWR;
1772 break;
1773 default:
1774 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unknown access type 0x%" PRIx32, __func__,
1775 fileaccess);
1776 break;
1779 switch(createmode) {
1780 case CREATE_NEW:
1781 flags|=O_CREAT|O_EXCL;
1782 break;
1783 case CREATE_ALWAYS:
1784 flags|=O_CREAT|O_TRUNC;
1785 break;
1786 case OPEN_EXISTING:
1787 break;
1788 case OPEN_ALWAYS:
1789 flags|=O_CREAT;
1790 break;
1791 case TRUNCATE_EXISTING:
1792 flags|=O_TRUNC;
1793 break;
1794 default:
1795 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Unknown create mode 0x%" PRIx32, __func__,
1796 createmode);
1797 break;
1800 return(flags);
1803 #if 0 /* unused */
1804 static mode_t convert_perms(guint32 sharemode)
1806 mode_t perms=0600;
1808 if(sharemode&FILE_SHARE_READ) {
1809 perms|=044;
1811 if(sharemode&FILE_SHARE_WRITE) {
1812 perms|=022;
1815 return(perms);
1817 #endif
1819 static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode,
1820 guint32 fileaccess,
1821 FileShare **share_info)
1823 gboolean file_already_shared;
1824 guint32 file_existing_share, file_existing_access;
1826 file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info);
1828 if (file_already_shared) {
1829 /* The reference to this share info was incremented
1830 * when we looked it up, so be careful to put it back
1831 * if we conclude we can't use this file.
1833 if (file_existing_share == 0) {
1834 /* Quick and easy, no possibility to share */
1835 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);
1837 file_share_release (*share_info);
1838 *share_info = NULL;
1840 return(FALSE);
1843 if (((file_existing_share == FILE_SHARE_READ) &&
1844 (fileaccess != GENERIC_READ)) ||
1845 ((file_existing_share == FILE_SHARE_WRITE) &&
1846 (fileaccess != GENERIC_WRITE))) {
1847 /* New access mode doesn't match up */
1848 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);
1850 file_share_release (*share_info);
1851 *share_info = NULL;
1853 return(FALSE);
1856 if (((file_existing_access & GENERIC_READ) &&
1857 !(sharemode & FILE_SHARE_READ)) ||
1858 ((file_existing_access & GENERIC_WRITE) &&
1859 !(sharemode & FILE_SHARE_WRITE))) {
1860 /* New share mode doesn't match up */
1861 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);
1863 file_share_release (*share_info);
1864 *share_info = NULL;
1866 return(FALSE);
1868 } else {
1869 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: New file!", __func__);
1872 return(TRUE);
1876 static gboolean
1877 share_allows_delete (struct stat *statbuf, FileShare **share_info)
1879 gboolean file_already_shared;
1880 guint32 file_existing_share, file_existing_access;
1882 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);
1884 if (file_already_shared) {
1885 /* The reference to this share info was incremented
1886 * when we looked it up, so be careful to put it back
1887 * if we conclude we can't use this file.
1889 if (file_existing_share == 0) {
1890 /* Quick and easy, no possibility to share */
1891 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);
1893 file_share_release (*share_info);
1894 *share_info = NULL;
1896 return(FALSE);
1899 if (!(file_existing_share & FILE_SHARE_DELETE)) {
1900 /* New access mode doesn't match up */
1901 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);
1903 file_share_release (*share_info);
1904 *share_info = NULL;
1906 return(FALSE);
1908 } else {
1909 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: New file!", __func__);
1912 return(TRUE);
1915 gpointer
1916 mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs)
1918 FileHandle *filehandle;
1919 MonoFDType type;
1920 gint flags=convert_flags(fileaccess, createmode);
1921 /*mode_t perms=convert_perms(sharemode);*/
1922 /* we don't use sharemode, because that relates to sharing of
1923 * the file when the file is open and is already handled by
1924 * other code, perms instead are the on-disk permissions and
1925 * this is a sane default.
1927 mode_t perms=0666;
1928 gchar *filename;
1929 gint fd, ret;
1930 struct stat statbuf;
1932 if (attrs & FILE_ATTRIBUTE_TEMPORARY)
1933 perms = 0600;
1935 if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
1936 mono_w32error_set_last (ERROR_ENCRYPTION_FAILED);
1937 return INVALID_HANDLE_VALUE;
1940 if (name == NULL) {
1941 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
1943 mono_w32error_set_last (ERROR_INVALID_NAME);
1944 return(INVALID_HANDLE_VALUE);
1947 filename = mono_unicode_to_external (name);
1948 if (filename == NULL) {
1949 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
1951 mono_w32error_set_last (ERROR_INVALID_NAME);
1952 return(INVALID_HANDLE_VALUE);
1955 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Opening %s with share 0x%" PRIx32 " and access 0x%" PRIx32, __func__,
1956 filename, sharemode, fileaccess);
1958 fd = _wapi_open (filename, flags, perms);
1960 /* If we were trying to open a directory with write permissions
1961 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1962 * EISDIR. However, this is a bit bogus because calls to
1963 * manipulate the directory (e.g. mono_w32file_set_times) will still work on
1964 * the directory because they use other API calls
1965 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1966 * to open the directory again without write permission.
1968 if (fd == -1 && errno == EISDIR)
1970 /* Try again but don't try to make it writable */
1971 fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
1974 if (fd == -1) {
1975 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Error opening file %s: %s", __func__, filename, g_strerror(errno));
1976 _wapi_set_last_path_error_from_errno (NULL, filename);
1977 g_free (filename);
1979 return(INVALID_HANDLE_VALUE);
1982 MONO_ENTER_GC_SAFE;
1983 ret = fstat (fd, &statbuf);
1984 MONO_EXIT_GC_SAFE;
1985 if (ret == -1) {
1986 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fstat error of file %s: %s", __func__, filename, g_strerror (errno));
1987 _wapi_set_last_error_from_errno ();
1988 MONO_ENTER_GC_SAFE;
1989 close (fd);
1990 MONO_EXIT_GC_SAFE;
1992 return(INVALID_HANDLE_VALUE);
1995 #ifndef S_ISFIFO
1996 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
1997 #endif
1998 if (S_ISFIFO (statbuf.st_mode)) {
1999 type = MONO_FDTYPE_PIPE;
2000 /* maintain invariant that pipes have no filename */
2001 g_free (filename);
2002 filename = NULL;
2003 } else if (S_ISCHR (statbuf.st_mode)) {
2004 type = MONO_FDTYPE_CONSOLE;
2005 } else {
2006 type = MONO_FDTYPE_FILE;
2009 filehandle = file_data_create (type, fd);
2010 filehandle->filename = filename;
2011 filehandle->fileaccess = fileaccess;
2012 filehandle->sharemode = sharemode;
2013 filehandle->attrs = attrs;
2015 if (!share_allows_open (&statbuf, filehandle->sharemode, filehandle->fileaccess, &filehandle->share_info)) {
2016 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2017 MONO_ENTER_GC_SAFE;
2018 close (((MonoFDHandle*) filehandle)->fd);
2019 MONO_EXIT_GC_SAFE;
2021 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2022 return (INVALID_HANDLE_VALUE);
2024 if (!filehandle->share_info) {
2025 /* No space, so no more files can be opened */
2026 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: No space in the share table", __func__);
2028 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
2029 MONO_ENTER_GC_SAFE;
2030 close (((MonoFDHandle*) filehandle)->fd);
2031 MONO_EXIT_GC_SAFE;
2033 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2034 return(INVALID_HANDLE_VALUE);
2037 #ifdef HAVE_POSIX_FADVISE
2038 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) {
2039 MONO_ENTER_GC_SAFE;
2040 posix_fadvise (((MonoFDHandle*) filehandle)->fd, 0, 0, POSIX_FADV_SEQUENTIAL);
2041 MONO_EXIT_GC_SAFE;
2043 if (attrs & FILE_FLAG_RANDOM_ACCESS) {
2044 MONO_ENTER_GC_SAFE;
2045 posix_fadvise (((MonoFDHandle*) filehandle)->fd, 0, 0, POSIX_FADV_RANDOM);
2046 MONO_EXIT_GC_SAFE;
2048 #endif
2050 #ifdef F_RDAHEAD
2051 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN) {
2052 MONO_ENTER_GC_SAFE;
2053 fcntl(((MonoFDHandle*) filehandle)->fd, F_RDAHEAD, 1);
2054 MONO_EXIT_GC_SAFE;
2056 #endif
2058 mono_fdhandle_insert ((MonoFDHandle*) filehandle);
2060 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: returning handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd));
2062 return GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd);
2065 gboolean
2066 mono_w32file_close (gpointer handle)
2068 if (!mono_fdhandle_close (GPOINTER_TO_INT (handle))) {
2069 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2070 return FALSE;
2073 return TRUE;
2076 gboolean mono_w32file_delete(const gunichar2 *name)
2078 gchar *filename;
2079 gint retval;
2080 gboolean ret = FALSE;
2081 #if 0
2082 struct stat statbuf;
2083 FileShare *shareinfo;
2084 #endif
2086 if(name==NULL) {
2087 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2089 mono_w32error_set_last (ERROR_INVALID_NAME);
2090 return(FALSE);
2093 filename=mono_unicode_to_external(name);
2094 if(filename==NULL) {
2095 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
2097 mono_w32error_set_last (ERROR_INVALID_NAME);
2098 return(FALSE);
2101 #if 0
2102 /* Check to make sure sharing allows us to open the file for
2103 * writing. See bug 323389.
2105 * Do the checks that don't need an open file descriptor, for
2106 * simplicity's sake. If we really have to do the full checks
2107 * then we can implement that later.
2109 if (_wapi_stat (filename, &statbuf) < 0) {
2110 _wapi_set_last_path_error_from_errno (NULL, filename);
2111 g_free (filename);
2112 return(FALSE);
2115 if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
2116 &shareinfo) == FALSE) {
2117 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2118 g_free (filename);
2119 return FALSE;
2121 if (shareinfo)
2122 file_share_release (shareinfo);
2123 #endif
2125 retval = _wapi_unlink (filename);
2127 if (retval == -1) {
2128 /* On linux, calling unlink on an non-existing file in a read-only mount will fail with EROFS.
2129 * The expected behavior is for this function to return FALSE and not trigger an exception.
2130 * To work around this behavior, we stat the file on failure.
2132 * This was supposedly fixed on kernel 3.0 [1] but we could reproduce it with Ubuntu 16.04 which has kernel 4.4.
2133 * We can't remove this workaround until the early 2020's when most Android deviced will have a fix.
2134 * [1] https://github.com/torvalds/linux/commit/50338b889dc504c69e0cb316ac92d1b9e51f3c8a
2136 if (errno == EROFS) {
2137 MonoIOStat stat;
2138 if (mono_w32file_get_attributes_ex (name, &stat)) //The file exists, so must be due the RO file system
2139 errno = EROFS;
2141 _wapi_set_last_path_error_from_errno (NULL, filename);
2142 } else {
2143 ret = TRUE;
2146 g_free(filename);
2148 return(ret);
2151 static gboolean
2152 MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
2154 gchar *utf8_name, *utf8_dest_name;
2155 gint result, errno_copy;
2156 struct stat stat_src, stat_dest;
2157 gboolean ret = FALSE;
2158 FileShare *shareinfo;
2160 if(name==NULL) {
2161 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2163 mono_w32error_set_last (ERROR_INVALID_NAME);
2164 return(FALSE);
2167 utf8_name = mono_unicode_to_external (name);
2168 if (utf8_name == NULL) {
2169 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
2171 mono_w32error_set_last (ERROR_INVALID_NAME);
2172 return FALSE;
2175 if(dest_name==NULL) {
2176 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2178 g_free (utf8_name);
2179 mono_w32error_set_last (ERROR_INVALID_NAME);
2180 return(FALSE);
2183 utf8_dest_name = mono_unicode_to_external (dest_name);
2184 if (utf8_dest_name == NULL) {
2185 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
2187 g_free (utf8_name);
2188 mono_w32error_set_last (ERROR_INVALID_NAME);
2189 return FALSE;
2193 * In C# land we check for the existence of src, but not for dest.
2194 * We check it here and return the failure if dest exists and is not
2195 * the same file as src.
2197 if (_wapi_stat (utf8_name, &stat_src) < 0) {
2198 if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
2199 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
2200 g_free (utf8_name);
2201 g_free (utf8_dest_name);
2202 return FALSE;
2206 if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
2207 if (stat_dest.st_dev != stat_src.st_dev ||
2208 stat_dest.st_ino != stat_src.st_ino) {
2209 g_free (utf8_name);
2210 g_free (utf8_dest_name);
2211 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2212 return FALSE;
2216 /* Check to make that we have delete sharing permission.
2217 * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
2219 * Do the checks that don't need an open file descriptor, for
2220 * simplicity's sake. If we really have to do the full checks
2221 * then we can implement that later.
2223 if (share_allows_delete (&stat_src, &shareinfo) == FALSE) {
2224 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2225 return FALSE;
2227 if (shareinfo) {
2228 file_share_release (shareinfo);
2229 shareinfo = NULL;
2232 result = _wapi_rename (utf8_name, utf8_dest_name);
2233 errno_copy = errno;
2235 if (result == -1) {
2236 switch(errno_copy) {
2237 case EEXIST:
2238 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2239 break;
2241 case EXDEV:
2242 /* Ignore here, it is dealt with below */
2243 break;
2245 case ENOENT:
2246 /* We already know src exists. Must be dest that doesn't exist. */
2247 _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name);
2248 break;
2250 default:
2251 _wapi_set_last_error_from_errno ();
2255 g_free (utf8_name);
2256 g_free (utf8_dest_name);
2258 if (result != 0 && errno_copy == EXDEV) {
2259 gint32 copy_error;
2261 if (S_ISDIR (stat_src.st_mode)) {
2262 mono_w32error_set_last (ERROR_NOT_SAME_DEVICE);
2263 return FALSE;
2265 /* Try a copy to the new location, and delete the source */
2266 if (!mono_w32file_copy (name, dest_name, FALSE, &copy_error)) {
2267 /* mono_w32file_copy will set the error */
2268 return(FALSE);
2271 return(mono_w32file_delete (name));
2274 if (result == 0) {
2275 ret = TRUE;
2278 return(ret);
2281 static gboolean
2282 write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_errors)
2284 gint remain, n;
2285 gchar *buf, *wbuf;
2286 gint buf_size = st_src->st_blksize;
2287 MonoThreadInfo *info = mono_thread_info_current ();
2289 buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
2290 buf = (gchar *) g_malloc (buf_size);
2292 for (;;) {
2293 MONO_ENTER_GC_SAFE;
2294 remain = read (src_fd, buf, buf_size);
2295 MONO_EXIT_GC_SAFE;
2296 if (remain < 0) {
2297 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2298 continue;
2300 if (report_errors)
2301 _wapi_set_last_error_from_errno ();
2303 g_free (buf);
2304 return FALSE;
2306 if (remain == 0) {
2307 break;
2310 wbuf = buf;
2311 while (remain > 0) {
2312 MONO_ENTER_GC_SAFE;
2313 n = write (dest_fd, wbuf, remain);
2314 MONO_EXIT_GC_SAFE;
2315 if (n < 0) {
2316 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2317 continue;
2319 if (report_errors)
2320 _wapi_set_last_error_from_errno ();
2321 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: write failed.", __func__);
2322 g_free (buf);
2323 return FALSE;
2326 remain -= n;
2327 wbuf += n;
2331 g_free (buf);
2332 return TRUE ;
2335 #if HOST_DARWIN
2336 static int
2337 _wapi_clonefile(const char *from, const char *to, int flags)
2339 gchar *located_from, *located_to;
2340 int ret;
2342 g_assert (clonefile_ptr != NULL);
2344 located_from = mono_portability_find_file (from, FALSE);
2345 located_to = mono_portability_find_file (to, FALSE);
2347 MONO_ENTER_GC_SAFE;
2348 ret = clonefile_ptr (
2349 located_from == NULL ? from : located_from,
2350 located_to == NULL ? to : located_to,
2351 flags);
2352 MONO_EXIT_GC_SAFE;
2354 g_free (located_from);
2355 g_free (located_to);
2357 return ret;
2359 #endif
2361 static gboolean
2362 CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists)
2364 gchar *utf8_src, *utf8_dest;
2365 struct stat st, dest_st;
2366 gboolean ret = TRUE;
2368 if(name==NULL) {
2369 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
2371 mono_w32error_set_last (ERROR_INVALID_NAME);
2372 return(FALSE);
2375 utf8_src = mono_unicode_to_external (name);
2376 if (utf8_src == NULL) {
2377 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of source returned NULL",
2378 __func__);
2380 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2381 return(FALSE);
2384 if(dest_name==NULL) {
2385 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: dest is NULL", __func__);
2387 g_free (utf8_src);
2388 mono_w32error_set_last (ERROR_INVALID_NAME);
2389 return(FALSE);
2392 utf8_dest = mono_unicode_to_external (dest_name);
2393 if (utf8_dest == NULL) {
2394 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of dest returned NULL",
2395 __func__);
2397 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2399 g_free (utf8_src);
2401 return(FALSE);
2404 gint src_fd, dest_fd;
2405 gint ret_utime;
2406 gint syscall_res;
2408 src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2409 if (src_fd < 0) {
2410 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2412 g_free (utf8_src);
2413 g_free (utf8_dest);
2415 return(FALSE);
2418 MONO_ENTER_GC_SAFE;
2419 syscall_res = fstat (src_fd, &st);
2420 MONO_EXIT_GC_SAFE;
2421 if (syscall_res < 0) {
2422 _wapi_set_last_error_from_errno ();
2424 g_free (utf8_src);
2425 g_free (utf8_dest);
2426 MONO_ENTER_GC_SAFE;
2427 close (src_fd);
2428 MONO_EXIT_GC_SAFE;
2430 return(FALSE);
2433 if (!_wapi_stat (utf8_dest, &dest_st)) {
2434 /* Before trying to open/create the dest, we need to report a 'file busy'
2435 * error if src and dest are actually the same file. We do the check here to take
2436 * advantage of the IOMAP capability */
2437 if (st.st_dev == dest_st.st_dev && st.st_ino == dest_st.st_ino) {
2438 g_free (utf8_src);
2439 g_free (utf8_dest);
2440 MONO_ENTER_GC_SAFE;
2441 close (src_fd);
2442 MONO_EXIT_GC_SAFE;
2444 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2445 return (FALSE);
2448 /* Take advantage of the fact that we already know the file exists and bail out
2449 * early */
2450 if (fail_if_exists) {
2451 g_free (utf8_src);
2452 g_free (utf8_dest);
2453 MONO_ENTER_GC_SAFE;
2454 close (src_fd);
2455 MONO_EXIT_GC_SAFE;
2457 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2458 return (FALSE);
2461 #if HOST_DARWIN
2462 /* If we attempt to use clonefile API we need to unlink the destination file
2463 * first */
2464 if (clonefile_ptr != NULL) {
2466 /* Bail out if the destination is read-only */
2467 if (!is_file_writable (&dest_st, utf8_dest)) {
2468 g_free (utf8_src);
2469 g_free (utf8_dest);
2470 MONO_ENTER_GC_SAFE;
2471 close (src_fd);
2472 MONO_EXIT_GC_SAFE;
2474 mono_w32error_set_last (ERROR_ACCESS_DENIED);
2475 return (FALSE);
2478 _wapi_unlink (utf8_dest);
2480 #endif
2483 #if HOST_DARWIN
2484 if (clonefile_ptr != NULL) {
2485 ret = _wapi_clonefile (utf8_src, utf8_dest, 0);
2486 if (ret == 0 || (errno != ENOTSUP && errno != EXDEV)) {
2487 g_free (utf8_src);
2488 g_free (utf8_dest);
2489 MONO_ENTER_GC_SAFE;
2490 close (src_fd);
2491 MONO_EXIT_GC_SAFE;
2493 if (ret == 0) {
2494 return (TRUE);
2495 } else {
2496 _wapi_set_last_error_from_errno ();
2497 return (FALSE);
2501 #endif
2503 if (fail_if_exists) {
2504 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
2505 } else {
2506 /* FIXME: it kinda sucks that this code path potentially scans
2507 * the directory twice due to the weird mono_w32error_set_last()
2508 * behavior. */
2509 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2510 if (dest_fd < 0) {
2511 /* The file does not exist, try creating it */
2512 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
2513 } else {
2514 /* Apparently this error is set if we
2515 * overwrite the dest file
2517 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2520 if (dest_fd < 0) {
2521 _wapi_set_last_error_from_errno ();
2523 g_free (utf8_src);
2524 g_free (utf8_dest);
2525 MONO_ENTER_GC_SAFE;
2526 close (src_fd);
2527 MONO_EXIT_GC_SAFE;
2529 return(FALSE);
2532 if (!write_file (src_fd, dest_fd, &st, TRUE))
2533 ret = FALSE;
2535 close (src_fd);
2536 close (dest_fd);
2538 #ifdef HAVE_STRUCT_TIMEVAL
2539 struct timeval times [2];
2540 memset (times, 0, sizeof (times));
2542 convert_stattime_access_to_timeval (&times [0], &st);
2543 convert_stattime_mod_to_timeval (&times [1], &st);
2545 ret_utime = _wapi_utimes (utf8_dest, times);
2546 #else
2547 struct utimbuf dest_time;
2548 dest_time.modtime = st.st_mtime;
2549 dest_time.actime = st.st_atime;
2551 ret_utime = _wapi_utime (utf8_dest, &dest_time);
2552 #endif
2553 if (ret_utime == -1)
2554 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: file [%s] utime failed: %s", __func__, utf8_dest, g_strerror(errno));
2556 g_free (utf8_src);
2557 g_free (utf8_dest);
2559 return ret;
2562 static gchar*
2563 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2565 gchar *utf8_ret;
2567 if (arg == NULL) {
2568 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: %s is NULL", __func__, arg_name);
2569 mono_w32error_set_last (ERROR_INVALID_NAME);
2570 return NULL;
2573 utf8_ret = mono_unicode_to_external (arg);
2574 if (utf8_ret == NULL) {
2575 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion of %s returned NULL",
2576 __func__, arg_name);
2577 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2578 return NULL;
2581 return utf8_ret;
2584 static gboolean
2585 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved)
2587 gint result, backup_fd = -1,replaced_fd = -1;
2588 gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2589 struct stat stBackup;
2590 gboolean ret = FALSE;
2592 if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2593 return FALSE;
2594 if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2595 goto replace_cleanup;
2596 if (backupFileName != NULL) {
2597 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2598 goto replace_cleanup;
2601 if (utf8_backupFileName) {
2602 // Open the backup file for read so we can restore the file if an error occurs.
2603 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2604 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2605 if (result == -1)
2606 goto replace_cleanup;
2609 result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2610 if (result == -1) {
2611 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2612 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2613 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2614 replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2615 stBackup.st_mode);
2617 if (replaced_fd == -1)
2618 goto replace_cleanup;
2620 write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2623 goto replace_cleanup;
2626 ret = TRUE;
2628 replace_cleanup:
2629 g_free (utf8_replacedFileName);
2630 g_free (utf8_replacementFileName);
2631 g_free (utf8_backupFileName);
2632 if (backup_fd != -1) {
2633 MONO_ENTER_GC_SAFE;
2634 close (backup_fd);
2635 MONO_EXIT_GC_SAFE;
2637 if (replaced_fd != -1) {
2638 MONO_ENTER_GC_SAFE;
2639 close (replaced_fd);
2640 MONO_EXIT_GC_SAFE;
2642 return ret;
2645 static gpointer
2646 _wapi_stdhandle_create (gint fd, const gchar *name)
2648 gint flags;
2649 FileHandle *filehandle;
2651 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: creating standard handle type %s, fd %d", __func__, name, fd);
2653 /* Check if fd is valid */
2654 do {
2655 flags = fcntl(fd, F_GETFL);
2656 } while (flags == -1 && errno == EINTR);
2658 if (flags == -1) {
2659 /* Invalid fd. Not really much point checking for EBADF
2660 * specifically
2662 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl error on fd %d: %s", __func__, fd, g_strerror(errno));
2664 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
2665 return INVALID_HANDLE_VALUE;
2668 filehandle = file_data_create (MONO_FDTYPE_CONSOLE, fd);
2669 filehandle->filename = g_strdup(name);
2671 switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) {
2672 case O_RDONLY:
2673 filehandle->fileaccess = GENERIC_READ;
2674 break;
2675 case O_WRONLY:
2676 filehandle->fileaccess = GENERIC_WRITE;
2677 break;
2678 case O_RDWR:
2679 filehandle->fileaccess = GENERIC_READ | GENERIC_WRITE;
2680 break;
2681 default:
2682 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Can't figure out flags 0x%x", __func__, flags);
2683 filehandle->fileaccess = 0;
2684 break;
2687 /* some default security attributes might be needed */
2688 filehandle->security_attributes = 0;
2690 /* Apparently input handles can't be written to. (I don't
2691 * know if output or error handles can't be read from.)
2693 if (fd == 0)
2694 filehandle->fileaccess &= ~GENERIC_WRITE;
2696 filehandle->sharemode = 0;
2697 filehandle->attrs = 0;
2699 if (!mono_fdhandle_try_insert ((MonoFDHandle*) filehandle)) {
2700 /* we raced between 2 invocations of _wapi_stdhandle_create */
2701 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2702 return GINT_TO_POINTER(fd);
2705 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: returning handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd));
2707 return GINT_TO_POINTER(((MonoFDHandle*) filehandle)->fd);
2710 enum {
2711 STD_INPUT_HANDLE = -10,
2712 STD_OUTPUT_HANDLE = -11,
2713 STD_ERROR_HANDLE = -12,
2716 static gpointer
2717 mono_w32file_get_std_handle (gint stdhandle)
2719 FileHandle **filehandle;
2720 gint fd;
2721 const gchar *name;
2723 switch(stdhandle) {
2724 case STD_INPUT_HANDLE:
2725 fd = 0;
2726 name = "<stdin>";
2727 break;
2729 case STD_OUTPUT_HANDLE:
2730 fd = 1;
2731 name = "<stdout>";
2732 break;
2734 case STD_ERROR_HANDLE:
2735 fd = 2;
2736 name = "<stderr>";
2737 break;
2739 default:
2740 g_assert_not_reached ();
2743 if (!mono_fdhandle_lookup_and_ref(fd, (MonoFDHandle**) &filehandle)) {
2744 gpointer handle;
2746 handle = _wapi_stdhandle_create (fd, name);
2747 if (handle == INVALID_HANDLE_VALUE) {
2748 mono_w32error_set_last (ERROR_NO_MORE_FILES);
2749 return INVALID_HANDLE_VALUE;
2753 return GINT_TO_POINTER (fd);
2756 static gboolean
2757 mono_w32file_read_or_write (gboolean read, gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, gint32 *win32error)
2759 MONO_REQ_GC_UNSAFE_MODE;
2761 FileHandle *filehandle;
2762 gboolean ret = FALSE;
2764 gboolean const ref = mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle);
2765 if (!ref) {
2766 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2767 goto exit;
2770 switch (((MonoFDHandle*) filehandle)->type) {
2771 case MONO_FDTYPE_FILE:
2772 ret = (read ? file_read : file_write) (filehandle, buffer, numbytes, bytesread);
2773 break;
2774 case MONO_FDTYPE_CONSOLE:
2775 ret = (read ? console_read : console_write) (filehandle, buffer, numbytes, bytesread);
2776 break;
2777 case MONO_FDTYPE_PIPE:
2778 ret = (read ? pipe_read : pipe_write) (filehandle, buffer, numbytes, bytesread);
2779 break;
2780 default:
2781 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2782 break;
2785 exit:
2786 if (ref)
2787 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2788 if (!ret)
2789 *win32error = mono_w32error_get_last ();
2790 return ret;
2793 gboolean
2794 mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread, gint32 *win32error)
2796 return mono_w32file_read_or_write (TRUE, handle, buffer, numbytes, bytesread, win32error);
2799 gboolean
2800 mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, gint32 *win32error)
2802 return mono_w32file_read_or_write (FALSE, handle, (gpointer)buffer, numbytes, byteswritten, win32error);
2805 gboolean
2806 mono_w32file_flush (gpointer handle)
2808 FileHandle *filehandle;
2809 gboolean ret;
2811 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2812 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2813 return FALSE;
2816 switch (((MonoFDHandle*) filehandle)->type) {
2817 case MONO_FDTYPE_FILE:
2818 ret = file_flush(filehandle);
2819 break;
2820 default:
2821 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2822 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2823 return FALSE;
2826 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2827 return ret;
2830 gboolean
2831 mono_w32file_truncate (gpointer handle)
2833 FileHandle *filehandle;
2834 gboolean ret;
2836 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2837 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2838 return FALSE;
2841 switch (((MonoFDHandle*) filehandle)->type) {
2842 case MONO_FDTYPE_FILE:
2843 ret = file_setendoffile(filehandle);
2844 break;
2845 default:
2846 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2847 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2848 return FALSE;
2851 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2852 return ret;
2855 guint32
2856 mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method)
2858 FileHandle *filehandle;
2859 guint32 ret;
2861 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2862 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2863 return INVALID_SET_FILE_POINTER;
2866 switch (((MonoFDHandle*) filehandle)->type) {
2867 case MONO_FDTYPE_FILE:
2868 ret = file_seek(filehandle, movedistance, highmovedistance, method);
2869 break;
2870 default:
2871 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2872 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2873 return INVALID_SET_FILE_POINTER;
2876 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2877 return ret;
2880 gint
2881 mono_w32file_get_type(gpointer handle)
2883 FileHandle *filehandle;
2884 gint ret;
2886 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2887 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2888 return FILE_TYPE_UNKNOWN;
2891 switch (((MonoFDHandle*) filehandle)->type) {
2892 case MONO_FDTYPE_FILE:
2893 ret = FILE_TYPE_DISK;
2894 break;
2895 case MONO_FDTYPE_CONSOLE:
2896 ret = FILE_TYPE_CHAR;
2897 break;
2898 case MONO_FDTYPE_PIPE:
2899 ret = FILE_TYPE_PIPE;
2900 break;
2901 default:
2902 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2903 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2904 return FILE_TYPE_UNKNOWN;
2907 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2908 return ret;
2911 static guint32
2912 GetFileSize(gpointer handle, guint32 *highsize)
2914 FileHandle *filehandle;
2915 guint32 ret;
2917 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2918 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2919 return INVALID_FILE_SIZE;
2922 switch (((MonoFDHandle*) filehandle)->type) {
2923 case MONO_FDTYPE_FILE:
2924 ret = file_getfilesize(filehandle, highsize);
2925 break;
2926 default:
2927 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2928 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2929 return INVALID_FILE_SIZE;
2932 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2933 return ret;
2936 gboolean
2937 mono_w32file_set_times(gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time)
2939 FileHandle *filehandle;
2940 gboolean ret;
2942 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
2943 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2944 return FALSE;
2947 switch (((MonoFDHandle*) filehandle)->type) {
2948 case MONO_FDTYPE_FILE:
2949 ret = file_setfiletime(filehandle, create_time, access_time, write_time);
2950 break;
2951 default:
2952 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2953 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2954 return FALSE;
2957 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
2958 return ret;
2961 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2962 * January 1 1601 GMT
2965 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2967 static const guint16 mon_yday[2][13]={
2968 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2969 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2972 gboolean
2973 mono_w32file_filetime_to_systemtime(const FILETIME *file_time, SYSTEMTIME *system_time)
2975 gint64 file_ticks, totaldays, rem, y;
2976 const guint16 *ip;
2978 if(system_time==NULL) {
2979 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: system_time NULL", __func__);
2981 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2982 return(FALSE);
2985 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2986 file_time->dwLowDateTime;
2988 /* Really compares if file_ticks>=0x8000000000000000
2989 * (LLONG_MAX+1) but we're working with a signed value for the
2990 * year and day calculation to work later
2992 if(file_ticks<0) {
2993 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: file_time too big", __func__);
2995 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2996 return(FALSE);
2999 totaldays=(file_ticks / TICKS_PER_DAY);
3000 rem = file_ticks % TICKS_PER_DAY;
3001 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT " rem: %" G_GINT64_FORMAT, __func__,
3002 totaldays, rem);
3004 system_time->wHour=rem/TICKS_PER_HOUR;
3005 rem %= TICKS_PER_HOUR;
3006 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Hour: %d rem: %" G_GINT64_FORMAT, __func__,
3007 system_time->wHour, rem);
3009 system_time->wMinute = rem / TICKS_PER_MINUTE;
3010 rem %= TICKS_PER_MINUTE;
3011 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Minute: %d rem: %" G_GINT64_FORMAT, __func__,
3012 system_time->wMinute, rem);
3014 system_time->wSecond = rem / TICKS_PER_SECOND;
3015 rem %= TICKS_PER_SECOND;
3016 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Second: %d rem: %" G_GINT64_FORMAT, __func__,
3017 system_time->wSecond, rem);
3019 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
3020 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Milliseconds: %d", __func__,
3021 system_time->wMilliseconds);
3023 /* January 1, 1601 was a Monday, according to Emacs calendar */
3024 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
3025 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Day of week: %d", __func__, system_time->wDayOfWeek);
3027 /* This algorithm to find year and month given days from epoch
3028 * from glibc
3030 y=1601;
3032 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
3033 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
3035 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
3036 /* Guess a corrected year, assuming 365 days per year */
3037 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
3038 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__,
3039 totaldays, yg, y);
3040 g_message("%s: LEAPS(yg): %li LEAPS(y): %li", __func__,
3041 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
3043 /* Adjust days and y to match the guessed year. */
3044 totaldays -= ((yg - y) * 365
3045 + LEAPS_THRU_END_OF (yg - 1)
3046 - LEAPS_THRU_END_OF (y - 1));
3047 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT,
3048 __func__, totaldays);
3049 y = yg;
3050 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: y: %" G_GINT64_FORMAT, __func__, y);
3053 system_time->wYear = y;
3054 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Year: %d", __func__, system_time->wYear);
3056 ip = mon_yday[isleap(y)];
3058 for(y=11; totaldays < ip[y]; --y) {
3059 continue;
3061 totaldays-=ip[y];
3062 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: totaldays: %" G_GINT64_FORMAT, __func__, totaldays);
3064 system_time->wMonth = y + 1;
3065 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Month: %d", __func__, system_time->wMonth);
3067 system_time->wDay = totaldays + 1;
3068 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Day: %d", __func__, system_time->wDay);
3070 return(TRUE);
3073 static void
3074 findhandle_destroy (gpointer data)
3076 FindHandle *findhandle;
3078 findhandle = (FindHandle*) data;
3079 g_assert (findhandle);
3081 mono_coop_mutex_destroy (&findhandle->mutex);
3083 if (findhandle->namelist)
3084 g_strfreev (findhandle->namelist);
3085 if (findhandle->dir_part)
3086 g_free (findhandle->dir_part);
3088 g_free (findhandle);
3091 static FindHandle*
3092 findhandle_create (void)
3094 FindHandle* findhandle;
3096 findhandle = g_new0 (FindHandle, 1);
3097 mono_refcount_init (findhandle, findhandle_destroy);
3099 mono_coop_mutex_init (&findhandle->mutex);
3101 return findhandle;
3104 static void
3105 findhandle_insert (FindHandle *findhandle)
3107 mono_coop_mutex_lock (&finds_mutex);
3109 if (g_hash_table_lookup_extended (finds, (gpointer) findhandle, NULL, NULL))
3110 g_error("%s: duplicate Find handle %p", __func__, (gpointer) findhandle);
3112 g_hash_table_insert (finds, (gpointer) findhandle, findhandle);
3114 mono_coop_mutex_unlock (&finds_mutex);
3117 static gboolean
3118 findhandle_lookup_and_ref (gpointer handle, FindHandle **findhandle)
3120 mono_coop_mutex_lock (&finds_mutex);
3122 if (!g_hash_table_lookup_extended (finds, handle, NULL, (gpointer*) findhandle)) {
3123 mono_coop_mutex_unlock (&finds_mutex);
3124 return FALSE;
3127 mono_refcount_inc (*findhandle);
3129 mono_coop_mutex_unlock (&finds_mutex);
3131 return TRUE;
3134 static void
3135 findhandle_unref (FindHandle *findhandle)
3137 mono_refcount_dec (findhandle);
3140 static gboolean
3141 findhandle_close (gpointer handle)
3143 FindHandle *findhandle;
3144 gboolean removed;
3146 mono_coop_mutex_lock (&finds_mutex);
3148 if (!g_hash_table_lookup_extended (finds, handle, NULL, (gpointer*) &findhandle)) {
3149 mono_coop_mutex_unlock (&finds_mutex);
3151 return FALSE;
3154 removed = g_hash_table_remove (finds, (gpointer) findhandle);
3155 g_assert (removed);
3157 mono_coop_mutex_unlock (&finds_mutex);
3159 return TRUE;
3162 static void
3163 finds_remove (gpointer data)
3165 FindHandle* findhandle;
3167 findhandle = (FindHandle*) data;
3168 g_assert (findhandle);
3170 mono_refcount_dec (findhandle);
3173 gpointer
3174 mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data)
3176 FindHandle *findhandle;
3177 gchar *utf8_pattern = NULL, *dir_part, *entry_part, **namelist;
3178 gint result;
3180 if (pattern == NULL) {
3181 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: pattern is NULL", __func__);
3183 mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
3184 return(INVALID_HANDLE_VALUE);
3187 utf8_pattern = mono_unicode_to_external (pattern);
3188 if (utf8_pattern == NULL) {
3189 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
3191 mono_w32error_set_last (ERROR_INVALID_NAME);
3192 return(INVALID_HANDLE_VALUE);
3195 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: looking for [%s]", __func__, utf8_pattern);
3197 /* Figure out which bit of the pattern is the directory */
3198 dir_part = _wapi_dirname (utf8_pattern);
3199 entry_part = _wapi_basename (utf8_pattern);
3201 #if 0
3202 /* Don't do this check for now, it breaks if directories
3203 * really do have metachars in their names (see bug 58116).
3204 * FIXME: Figure out a better solution to keep some checks...
3206 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3207 mono_w32error_set_last (ERROR_INVALID_NAME);
3208 g_free (dir_part);
3209 g_free (entry_part);
3210 g_free (utf8_pattern);
3211 return(INVALID_HANDLE_VALUE);
3213 #endif
3215 /* The pattern can specify a directory or a set of files.
3217 * The pattern can have wildcard characters ? and *, but only
3218 * in the section after the last directory delimiter. (Return
3219 * ERROR_INVALID_NAME if there are wildcards in earlier path
3220 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3221 * means "match one character", "??" seems to mean "match one
3222 * or two characters", "???" seems to mean "match one, two or
3223 * three characters", etc. Windows will also try and match
3224 * the mangled "short name" of files, so 8 character patterns
3225 * with wildcards will show some surprising results.
3227 * All the written documentation I can find says that '?'
3228 * should only match one character, and doesn't mention '??',
3229 * '???' etc. I'm going to assume that the strict behaviour
3230 * (ie '???' means three and only three characters) is the
3231 * correct one, because that lets me use fnmatch(3) rather
3232 * than mess around with regexes.
3235 namelist = NULL;
3236 result = _wapi_io_scandir (dir_part, entry_part,
3237 &namelist);
3239 if (result == 0) {
3240 /* No files, which windows seems to call
3241 * FILE_NOT_FOUND
3243 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
3244 g_free (utf8_pattern);
3245 g_free (entry_part);
3246 g_free (dir_part);
3247 g_strfreev (namelist);
3248 return (INVALID_HANDLE_VALUE);
3251 if (result < 0) {
3252 _wapi_set_last_path_error_from_errno (dir_part, NULL);
3253 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: scandir error: %s", __func__, g_strerror (errno));
3254 g_free (utf8_pattern);
3255 g_free (entry_part);
3256 g_free (dir_part);
3257 g_strfreev (namelist);
3258 return (INVALID_HANDLE_VALUE);
3261 g_free (utf8_pattern);
3262 g_free (entry_part);
3264 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Got %d matches", __func__, result);
3266 findhandle = findhandle_create ();
3267 findhandle->namelist = namelist;
3268 findhandle->dir_part = dir_part;
3269 findhandle->num = result;
3270 findhandle->count = 0;
3272 findhandle_insert (findhandle);
3274 if (!mono_w32file_find_next ((gpointer) findhandle, find_data)) {
3275 mono_w32file_find_close ((gpointer) findhandle);
3276 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3277 return INVALID_HANDLE_VALUE;
3280 return (gpointer) findhandle;
3283 gboolean
3284 mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data)
3286 FindHandle *findhandle;
3287 struct stat buf, linkbuf;
3288 gint result;
3289 gchar *filename;
3290 gchar *utf8_filename, *utf8_basename;
3291 gunichar2 *utf16_basename;
3292 time_t create_time;
3293 glong bytes;
3294 gboolean ret = FALSE;
3296 if (!findhandle_lookup_and_ref (handle, &findhandle)) {
3297 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3298 return FALSE;
3301 mono_coop_mutex_lock (&findhandle->mutex);
3303 retry:
3304 if (findhandle->count >= findhandle->num) {
3305 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3306 goto cleanup;
3309 /* stat next match */
3311 filename = g_build_filename (findhandle->dir_part, findhandle->namelist[findhandle->count ++], NULL);
3313 result = _wapi_stat (filename, &buf);
3314 if (result == -1 && errno == ENOENT) {
3315 /* Might be a dangling symlink */
3316 result = _wapi_lstat (filename, &buf);
3319 if (result != 0) {
3320 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: stat failed: %s", __func__, filename);
3322 g_free (filename);
3323 goto retry;
3326 result = _wapi_lstat (filename, &linkbuf);
3327 if (result != 0) {
3328 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: lstat failed: %s", __func__, filename);
3330 g_free (filename);
3331 goto retry;
3334 utf8_filename = mono_utf8_from_external (filename);
3335 if (utf8_filename == NULL) {
3336 /* We couldn't turn this filename into utf8 (eg the
3337 * encoding of the name wasn't convertible), so just
3338 * ignore it.
3340 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
3342 g_free (filename);
3343 goto retry;
3345 g_free (filename);
3347 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Found [%s]", __func__, utf8_filename);
3349 /* fill data block */
3351 if (buf.st_mtime < buf.st_ctime)
3352 create_time = buf.st_mtime;
3353 else
3354 create_time = buf.st_ctime;
3356 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
3358 time_t_to_filetime (create_time, &find_data->ftCreationTime);
3359 time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3360 time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3362 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3363 find_data->nFileSizeHigh = 0;
3364 find_data->nFileSizeLow = 0;
3365 } else {
3366 find_data->nFileSizeHigh = buf.st_size >> 32;
3367 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3370 find_data->dwReserved0 = 0;
3371 find_data->dwReserved1 = 0;
3373 utf8_basename = _wapi_basename (utf8_filename);
3374 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3375 NULL);
3376 if(utf16_basename==NULL) {
3377 g_free (utf8_basename);
3378 g_free (utf8_filename);
3379 goto retry;
3381 ret = TRUE;
3383 /* utf16 is 2 * utf8 */
3384 bytes *= 2;
3386 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3388 /* Truncating a utf16 string like this might leave the last
3389 * gchar incomplete
3391 memcpy (find_data->cFileName, utf16_basename,
3392 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3394 find_data->cAlternateFileName [0] = 0; /* not used */
3396 g_free (utf8_basename);
3397 g_free (utf8_filename);
3398 g_free (utf16_basename);
3400 cleanup:
3401 mono_coop_mutex_unlock (&findhandle->mutex);
3403 findhandle_unref (findhandle);
3405 return(ret);
3408 gboolean
3409 mono_w32file_find_close (gpointer handle)
3411 if (!findhandle_close (handle)) {
3412 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3413 return FALSE;
3416 return TRUE;
3419 gboolean
3420 mono_w32file_create_directory (const gunichar2 *name)
3422 gchar *utf8_name;
3423 gint result;
3425 if (name == NULL) {
3426 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3428 mono_w32error_set_last (ERROR_INVALID_NAME);
3429 return(FALSE);
3432 utf8_name = mono_unicode_to_external (name);
3433 if (utf8_name == NULL) {
3434 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
3436 mono_w32error_set_last (ERROR_INVALID_NAME);
3437 return FALSE;
3440 result = _wapi_mkdir (utf8_name, 0777);
3442 if (result == 0) {
3443 g_free (utf8_name);
3444 return TRUE;
3447 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3448 g_free (utf8_name);
3449 return FALSE;
3452 gboolean
3453 mono_w32file_remove_directory (const gunichar2 *name)
3455 gchar *utf8_name;
3456 gint result;
3458 if (name == NULL) {
3459 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3461 mono_w32error_set_last (ERROR_INVALID_NAME);
3462 return(FALSE);
3465 utf8_name = mono_unicode_to_external (name);
3466 if (utf8_name == NULL) {
3467 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
3469 mono_w32error_set_last (ERROR_INVALID_NAME);
3470 return FALSE;
3473 result = _wapi_rmdir (utf8_name);
3474 if (result == -1) {
3475 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3476 g_free (utf8_name);
3478 return(FALSE);
3480 g_free (utf8_name);
3482 return(TRUE);
3485 guint32
3486 mono_w32file_get_attributes (const gunichar2 *name)
3488 gchar *utf8_name;
3489 struct stat buf, linkbuf;
3490 gint result;
3491 guint32 ret;
3493 if (name == NULL) {
3494 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3496 mono_w32error_set_last (ERROR_INVALID_NAME);
3497 return(FALSE);
3500 utf8_name = mono_unicode_to_external (name);
3501 if (utf8_name == NULL) {
3502 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
3504 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3505 return (INVALID_FILE_ATTRIBUTES);
3508 result = _wapi_stat (utf8_name, &buf);
3509 if (result == -1 && (errno == ENOENT || errno == ELOOP)) {
3510 /* Might be a dangling symlink... */
3511 result = _wapi_lstat (utf8_name, &buf);
3514 if (result != 0) {
3515 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3516 g_free (utf8_name);
3517 return (INVALID_FILE_ATTRIBUTES);
3520 result = _wapi_lstat (utf8_name, &linkbuf);
3521 if (result != 0) {
3522 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3523 g_free (utf8_name);
3524 return (INVALID_FILE_ATTRIBUTES);
3527 ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3529 g_free (utf8_name);
3531 return(ret);
3534 gboolean
3535 mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat)
3537 gchar *utf8_name;
3539 struct stat buf, linkbuf;
3540 gint result;
3542 if (name == NULL) {
3543 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3545 mono_w32error_set_last (ERROR_INVALID_NAME);
3546 return(FALSE);
3549 utf8_name = mono_unicode_to_external (name);
3550 if (utf8_name == NULL) {
3551 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
3553 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3554 return FALSE;
3557 result = _wapi_stat (utf8_name, &buf);
3558 if (result == -1 && errno == ENOENT) {
3559 /* Might be a dangling symlink... */
3560 result = _wapi_lstat (utf8_name, &buf);
3563 if (result != 0) {
3564 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3565 g_free (utf8_name);
3566 return FALSE;
3569 result = _wapi_lstat (utf8_name, &linkbuf);
3570 if (result != 0) {
3571 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3572 g_free (utf8_name);
3573 return(FALSE);
3576 /* fill stat block */
3578 stat->attributes = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3579 stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size;
3581 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
3582 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))
3583 stat->creation_time = linkbuf.st_mtimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3584 else
3585 stat->creation_time = linkbuf.st_ctimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_ctimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3587 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;
3588 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;
3589 #elif HAVE_STRUCT_STAT_ST_ATIM
3590 if (linkbuf.st_mtime < linkbuf.st_ctime || (linkbuf.st_mtime == linkbuf.st_ctime && linkbuf.st_mtim.tv_nsec < linkbuf.st_ctim.tv_nsec))
3591 stat->creation_time = linkbuf.st_mtime * TICKS_PER_SECOND + (linkbuf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3592 else
3593 stat->creation_time = linkbuf.st_ctime * TICKS_PER_SECOND + (linkbuf.st_ctim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3595 stat->last_access_time = linkbuf.st_atime * TICKS_PER_SECOND + (linkbuf.st_atim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3596 stat->last_write_time = linkbuf.st_mtime * TICKS_PER_SECOND + (linkbuf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE;
3597 #else
3598 stat->creation_time = (((guint64) (linkbuf.st_mtime < linkbuf.st_ctime ? linkbuf.st_mtime : linkbuf.st_ctime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3599 stat->last_access_time = (((guint64) (linkbuf.st_atime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3600 stat->last_write_time = (((guint64) (linkbuf.st_mtime)) * TICKS_PER_SECOND) + CONVERT_BASE;
3601 #endif
3603 g_free (utf8_name);
3604 return TRUE;
3607 gboolean
3608 mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs)
3610 /* FIXME: think of something clever to do on unix */
3611 gchar *utf8_name;
3612 struct stat buf;
3613 gint result;
3616 * Currently we only handle one *internal* case, with a value that is
3617 * not standard: 0x80000000, which means `set executable bit'
3620 if (name == NULL) {
3621 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: name is NULL", __func__);
3623 mono_w32error_set_last (ERROR_INVALID_NAME);
3624 return(FALSE);
3627 utf8_name = mono_unicode_to_external (name);
3628 if (utf8_name == NULL) {
3629 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
3631 mono_w32error_set_last (ERROR_INVALID_NAME);
3632 return FALSE;
3635 result = _wapi_stat (utf8_name, &buf);
3636 if (result == -1 && errno == ENOENT) {
3637 /* Might be a dangling symlink... */
3638 result = _wapi_lstat (utf8_name, &buf);
3641 if (result != 0) {
3642 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3643 g_free (utf8_name);
3644 return FALSE;
3647 /* Contrary to the documentation, ms allows NORMAL to be
3648 * specified along with other attributes, so dont bother to
3649 * catch that case here.
3651 if (attrs & FILE_ATTRIBUTE_READONLY) {
3652 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP));
3653 } else {
3654 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR);
3657 /* Ignore the other attributes for now */
3659 if (attrs & 0x80000000){
3660 mode_t exec_mask = 0;
3662 if ((buf.st_mode & S_IRUSR) != 0)
3663 exec_mask |= S_IXUSR;
3665 if ((buf.st_mode & S_IRGRP) != 0)
3666 exec_mask |= S_IXGRP;
3668 if ((buf.st_mode & S_IROTH) != 0)
3669 exec_mask |= S_IXOTH;
3671 MONO_ENTER_GC_SAFE;
3672 result = chmod (utf8_name, buf.st_mode | exec_mask);
3673 MONO_EXIT_GC_SAFE;
3675 /* Don't bother to reset executable (might need to change this
3676 * policy)
3679 g_free (utf8_name);
3681 return(TRUE);
3684 guint32
3685 mono_w32file_get_cwd (guint32 length, gunichar2 *buffer)
3687 gunichar2 *utf16_path;
3688 glong count;
3689 gsize bytes;
3691 if (getcwd ((gchar*)buffer, length) == NULL) {
3692 if (errno == ERANGE) { /*buffer length is not big enough */
3693 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*/
3694 if (path == NULL)
3695 return 0;
3696 utf16_path = mono_unicode_from_external (path, &bytes);
3697 g_free (utf16_path);
3698 g_free (path);
3699 return (bytes/2)+1;
3701 _wapi_set_last_error_from_errno ();
3702 return 0;
3705 utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3706 count = (bytes/2)+1;
3707 g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3709 /* Add the terminator */
3710 memset (buffer, '\0', bytes+2);
3711 memcpy (buffer, utf16_path, bytes);
3713 g_free (utf16_path);
3715 return count;
3718 gboolean
3719 mono_w32file_set_cwd (const gunichar2 *path)
3721 gchar *utf8_path;
3722 gboolean result;
3724 if (path == NULL) {
3725 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3726 return(FALSE);
3729 utf8_path = mono_unicode_to_external (path);
3730 if (_wapi_chdir (utf8_path) != 0) {
3731 _wapi_set_last_error_from_errno ();
3732 result = FALSE;
3734 else
3735 result = TRUE;
3737 g_free (utf8_path);
3738 return result;
3741 gboolean
3742 mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size)
3744 FileHandle *read_filehandle, *write_filehandle;
3745 gint filedes[2];
3746 gint ret;
3748 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Creating pipe", __func__);
3750 MONO_ENTER_GC_SAFE;
3751 ret=pipe (filedes);
3752 MONO_EXIT_GC_SAFE;
3753 if (ret==-1) {
3754 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Error creating pipe: (%d) %s",
3755 __func__, errno, g_strerror (errno));
3757 _wapi_set_last_error_from_errno ();
3758 return FALSE;
3761 /* filedes[0] is open for reading, filedes[1] for writing */
3763 read_filehandle = file_data_create (MONO_FDTYPE_PIPE, filedes[0]);
3764 read_filehandle->fileaccess = GENERIC_READ;
3766 write_filehandle = file_data_create (MONO_FDTYPE_PIPE, filedes[1]);
3767 write_filehandle->fileaccess = GENERIC_WRITE;
3769 mono_fdhandle_insert ((MonoFDHandle*) read_filehandle);
3770 mono_fdhandle_insert ((MonoFDHandle*) write_filehandle);
3772 *readpipe = GINT_TO_POINTER(((MonoFDHandle*) read_filehandle)->fd);
3773 *writepipe = GINT_TO_POINTER(((MonoFDHandle*) write_filehandle)->fd);
3775 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: Returning pipe: read handle %p, write handle %p",
3776 __func__, GINT_TO_POINTER(((MonoFDHandle*) read_filehandle)->fd), GINT_TO_POINTER(((MonoFDHandle*) write_filehandle)->fd));
3778 return(TRUE);
3781 #ifdef HAVE_GETFSSTAT
3782 /* Darwin has getfsstat */
3783 gint32
3784 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3786 struct statfs *stats;
3787 gint size, n, i;
3788 gunichar2 *dir;
3789 glong length, total = 0;
3790 gint syscall_res;
3792 MONO_ENTER_GC_SAFE;
3793 n = getfsstat (NULL, 0, MNT_NOWAIT);
3794 MONO_EXIT_GC_SAFE;
3795 if (n == -1)
3796 return 0;
3797 size = n * sizeof (struct statfs);
3798 stats = (struct statfs *) g_malloc (size);
3799 if (stats == NULL)
3800 return 0;
3801 MONO_ENTER_GC_SAFE;
3802 syscall_res = getfsstat (stats, size, MNT_NOWAIT);
3803 MONO_EXIT_GC_SAFE;
3804 if (syscall_res == -1){
3805 g_free (stats);
3806 return 0;
3808 for (i = 0; i < n; i++){
3809 dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL);
3810 if (total + length < len){
3811 memcpy (buf + total, dir, sizeof (gunichar2) * length);
3812 buf [total+length] = 0;
3814 g_free (dir);
3815 total += length + 1;
3817 if (total < len)
3818 buf [total] = 0;
3819 total++;
3820 g_free (stats);
3821 return total;
3823 #elif _AIX
3824 gint32
3825 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3827 struct vmount *mounts;
3828 // ret will first be the errno cond, then no of structs
3829 int needsize, ret, total;
3830 gunichar2 *dir;
3831 glong length;
3832 total = 0;
3834 MONO_ENTER_GC_SAFE;
3835 ret = mntctl (MCTL_QUERY, sizeof(needsize), &needsize);
3836 MONO_EXIT_GC_SAFE;
3837 if (ret == -1)
3838 return 0;
3839 mounts = (struct vmount *) g_malloc (needsize);
3840 if (mounts == NULL)
3841 return 0;
3842 MONO_ENTER_GC_SAFE;
3843 ret = mntctl (MCTL_QUERY, needsize, mounts);
3844 MONO_EXIT_GC_SAFE;
3845 if (ret == -1) {
3846 g_free (mounts);
3847 return 0;
3850 for (int i = 0; i < ret; i++) {
3851 dir = g_utf8_to_utf16 (vmt2dataptr(mounts, VMT_STUB), -1, NULL, &length, NULL);
3852 if (total + length < len){
3853 memcpy (buf + total, dir, sizeof (gunichar2) * length);
3854 buf [total+length] = 0;
3856 g_free (dir);
3857 total += length + 1;
3858 mounts = (void*)mounts + mounts->vmt_length; // next!
3860 if (total < len)
3861 buf [total] = 0;
3862 total++;
3863 g_free (mounts);
3864 return total;
3866 #else
3867 /* In-place octal sequence replacement */
3868 static void
3869 unescape_octal (gchar *str)
3871 gchar *rptr;
3872 gchar *wptr;
3874 if (str == NULL)
3875 return;
3877 rptr = wptr = str;
3878 while (*rptr != '\0') {
3879 if (*rptr == '\\') {
3880 gchar c;
3881 rptr++;
3882 c = (*(rptr++) - '0') << 6;
3883 c += (*(rptr++) - '0') << 3;
3884 c += *(rptr++) - '0';
3885 *wptr++ = c;
3886 } else if (wptr != rptr) {
3887 *wptr++ = *rptr++;
3888 } else {
3889 rptr++; wptr++;
3892 *wptr = '\0';
3894 static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf);
3896 #if __linux__
3897 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
3898 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
3899 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
3901 typedef struct
3903 glong total;
3904 guint32 buffer_index;
3905 guint32 mountpoint_index;
3906 guint32 field_number;
3907 guint32 allocated_size;
3908 guint32 fsname_index;
3909 guint32 fstype_index;
3910 gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1];
3911 gchar *mountpoint_allocated;
3912 gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER];
3913 gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3914 gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3915 ssize_t nbytes;
3916 gchar delimiter;
3917 gboolean check_mount_source;
3918 } LinuxMountInfoParseState;
3920 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3921 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3922 static void append_to_mountpoint (LinuxMountInfoParseState *state);
3923 static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3925 gint32
3926 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3928 gint fd;
3929 gint32 ret = 0;
3930 LinuxMountInfoParseState state;
3931 gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL;
3933 memset (buf, 0, len * sizeof (gunichar2));
3934 MONO_ENTER_GC_SAFE;
3935 fd = open ("/proc/self/mountinfo", O_RDONLY);
3936 MONO_EXIT_GC_SAFE;
3937 if (fd != -1)
3938 parser = GetLogicalDriveStrings_MountInfo;
3939 else {
3940 MONO_ENTER_GC_SAFE;
3941 fd = open ("/proc/mounts", O_RDONLY);
3942 MONO_EXIT_GC_SAFE;
3943 if (fd != -1)
3944 parser = GetLogicalDriveStrings_Mounts;
3947 if (!parser) {
3948 ret = GetLogicalDriveStrings_Mtab (len, buf);
3949 goto done_and_out;
3952 memset (&state, 0, sizeof (LinuxMountInfoParseState));
3953 state.field_number = 1;
3954 state.delimiter = ' ';
3956 while (1) {
3957 MONO_ENTER_GC_SAFE;
3958 state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER);
3959 MONO_EXIT_GC_SAFE;
3960 if (!(state.nbytes > 0))
3961 break;
3962 state.buffer_index = 0;
3964 while ((*parser)(len, buf, &state)) {
3965 if (state.buffer [state.buffer_index] == '\n') {
3966 gboolean quit = add_drive_string (len, buf, &state);
3967 state.field_number = 1;
3968 state.buffer_index++;
3969 if (state.mountpoint_allocated) {
3970 g_free (state.mountpoint_allocated);
3971 state.mountpoint_allocated = NULL;
3973 if (quit) {
3974 ret = state.total;
3975 goto done_and_out;
3980 ret = state.total;
3982 done_and_out:
3983 if (fd != -1) {
3984 MONO_ENTER_GC_SAFE;
3985 close (fd);
3986 MONO_EXIT_GC_SAFE;
3988 return ret;
3991 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
3993 gchar *ptr;
3995 if (state->field_number == 1)
3996 state->check_mount_source = TRUE;
3998 while (state->buffer_index < (guint32)state->nbytes) {
3999 if (state->buffer [state->buffer_index] == state->delimiter) {
4000 state->field_number++;
4001 switch (state->field_number) {
4002 case 2:
4003 state->mountpoint_index = 0;
4004 break;
4006 case 3:
4007 if (state->mountpoint_allocated)
4008 state->mountpoint_allocated [state->mountpoint_index] = 0;
4009 else
4010 state->mountpoint [state->mountpoint_index] = 0;
4011 break;
4013 default:
4014 ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index);
4015 if (ptr)
4016 state->buffer_index = (ptr - (gchar*)state->buffer) - 1;
4017 else
4018 state->buffer_index = state->nbytes;
4019 return TRUE;
4021 state->buffer_index++;
4022 continue;
4023 } else if (state->buffer [state->buffer_index] == '\n')
4024 return TRUE;
4026 switch (state->field_number) {
4027 case 1:
4028 if (state->check_mount_source) {
4029 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4030 /* We can ignore the rest, it's a device
4031 * path */
4032 state->check_mount_source = FALSE;
4033 state->fsname [state->fsname_index++] = '/';
4034 break;
4036 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4037 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4039 break;
4041 case 2:
4042 append_to_mountpoint (state);
4043 break;
4045 case 3:
4046 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4047 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4048 break;
4051 state->buffer_index++;
4054 return FALSE;
4057 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4059 while (state->buffer_index < (guint32)state->nbytes) {
4060 if (state->buffer [state->buffer_index] == state->delimiter) {
4061 state->field_number++;
4062 switch (state->field_number) {
4063 case 5:
4064 state->mountpoint_index = 0;
4065 break;
4067 case 6:
4068 if (state->mountpoint_allocated)
4069 state->mountpoint_allocated [state->mountpoint_index] = 0;
4070 else
4071 state->mountpoint [state->mountpoint_index] = 0;
4072 break;
4074 case 7:
4075 state->delimiter = '-';
4076 break;
4078 case 8:
4079 state->delimiter = ' ';
4080 break;
4082 case 10:
4083 state->check_mount_source = TRUE;
4084 break;
4086 state->buffer_index++;
4087 continue;
4088 } else if (state->buffer [state->buffer_index] == '\n')
4089 return TRUE;
4091 switch (state->field_number) {
4092 case 5:
4093 append_to_mountpoint (state);
4094 break;
4096 case 9:
4097 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4098 state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4099 break;
4101 case 10:
4102 if (state->check_mount_source) {
4103 if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4104 /* We can ignore the rest, it's a device
4105 * path */
4106 state->check_mount_source = FALSE;
4107 state->fsname [state->fsname_index++] = '/';
4108 break;
4110 if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4111 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4113 break;
4116 state->buffer_index++;
4119 return FALSE;
4122 static void
4123 append_to_mountpoint (LinuxMountInfoParseState *state)
4125 gchar ch = state->buffer [state->buffer_index];
4126 if (state->mountpoint_allocated) {
4127 if (state->mountpoint_index >= state->allocated_size) {
4128 guint32 newsize = (state->allocated_size << 1) + 1;
4129 gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar));
4131 memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index);
4132 g_free (state->mountpoint_allocated);
4133 state->mountpoint_allocated = newbuf;
4134 state->allocated_size = newsize;
4136 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4137 } else {
4138 if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) {
4139 state->allocated_size = (state->mountpoint_index << 1) + 1;
4140 state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar));
4141 memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index);
4142 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4143 } else
4144 state->mountpoint [state->mountpoint_index++] = ch;
4148 static gboolean
4149 add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4151 gboolean quit = FALSE;
4152 gboolean ignore_entry;
4154 if (state->fsname_index == 1 && state->fsname [0] == '/')
4155 ignore_entry = FALSE;
4156 else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 ||
4157 memcmp ("aufs", state->fstype, state->fstype_index) == 0) {
4158 /* Don't ignore overlayfs and aufs - these might be used on Docker
4159 * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */
4160 ignore_entry = FALSE;
4161 } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) {
4162 ignore_entry = TRUE;
4163 } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) {
4164 /* Ignore GNOME's gvfs */
4165 if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0)
4166 ignore_entry = TRUE;
4167 else
4168 ignore_entry = FALSE;
4169 } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0)
4170 ignore_entry = FALSE;
4171 else
4172 ignore_entry = TRUE;
4174 if (!ignore_entry) {
4175 gunichar2 *dir;
4176 glong length;
4177 gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint;
4179 unescape_octal (mountpoint);
4180 dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL);
4181 if (state->total + length + 1 > len) {
4182 quit = TRUE;
4183 state->total = len * 2;
4184 } else {
4185 length++;
4186 memcpy (buf + state->total, dir, sizeof (gunichar2) * length);
4187 state->total += length;
4189 g_free (dir);
4191 state->fsname_index = 0;
4192 state->fstype_index = 0;
4194 return quit;
4196 #else
4197 gint32
4198 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
4200 return GetLogicalDriveStrings_Mtab (len, buf);
4202 #endif
4203 static gint32
4204 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf)
4206 FILE *fp;
4207 gunichar2 *ptr, *dir;
4208 glong length, total = 0;
4209 gchar buffer [512];
4210 gchar **splitted;
4212 memset (buf, 0, sizeof (gunichar2) * (len + 1));
4213 buf [0] = '/';
4214 buf [1] = 0;
4215 buf [2] = 0;
4217 /* Sigh, mntent and friends don't work well.
4218 * It stops on the first line that doesn't begin with a '/'.
4219 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4220 MONO_ENTER_GC_SAFE;
4221 fp = fopen ("/etc/mtab", "rt");
4222 MONO_EXIT_GC_SAFE;
4223 if (fp == NULL) {
4224 MONO_ENTER_GC_SAFE;
4225 fp = fopen ("/etc/mnttab", "rt");
4226 MONO_EXIT_GC_SAFE;
4227 if (fp == NULL)
4228 return 1;
4231 ptr = buf;
4232 while (1) {
4233 gchar *fgets_res;
4234 MONO_ENTER_GC_SAFE;
4235 fgets_res = fgets (buffer, 512, fp);
4236 MONO_EXIT_GC_SAFE;
4237 if (!fgets_res)
4238 break;
4239 if (*buffer != '/')
4240 continue;
4242 splitted = g_strsplit (buffer, " ", 0);
4243 if (!*splitted || !*(splitted + 1)) {
4244 g_strfreev (splitted);
4245 continue;
4248 unescape_octal (*(splitted + 1));
4249 dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
4250 g_strfreev (splitted);
4251 if (total + length + 1 > len) {
4252 MONO_ENTER_GC_SAFE;
4253 fclose (fp);
4254 MONO_EXIT_GC_SAFE;
4255 g_free (dir);
4256 return len * 2; /* guess */
4259 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4260 g_free (dir);
4261 total += length + 1;
4264 MONO_ENTER_GC_SAFE;
4265 fclose (fp);
4266 MONO_EXIT_GC_SAFE;
4267 return total;
4268 /* Commented out, does not work with my mtab!!! - Gonz */
4269 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4271 FILE *fp;
4272 struct mntent *mnt;
4273 gunichar2 *ptr, *dir;
4274 glong len, total = 0;
4277 MONO_ENTER_GC_SAFE;
4278 fp = setmntent ("/etc/mtab", "rt");
4279 MONO_EXIT_GC_SAFE;
4280 if (fp == NULL) {
4281 MONO_ENTER_GC_SAFE;
4282 fp = setmntent ("/etc/mnttab", "rt");
4283 MONO_EXIT_GC_SAFE;
4284 if (fp == NULL)
4285 return;
4288 ptr = buf;
4289 while (1) {
4290 MONO_ENTER_GC_SAFE;
4291 mnt = getmntent (fp);
4292 MONO_EXIT_GC_SAFE;
4293 if (mnt == NULL)
4294 break;
4295 g_print ("GOT %s\n", mnt->mnt_dir);
4296 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4297 if (total + len + 1 > len) {
4298 MONO_ENTER_GC_SAFE;
4299 endmntent (fp);
4300 MONO_EXIT_GC_SAFE;
4301 return len * 2; /* guess */
4304 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4305 g_free (dir);
4306 total += len + 1;
4309 MONO_ENTER_GC_SAFE;
4310 endmntent (fp);
4311 MONO_EXIT_GC_SAFE;
4312 return total;
4314 #endif
4316 #endif
4318 #ifndef PLATFORM_NO_DRIVEINFO
4319 gboolean
4320 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)
4322 g_assert (free_bytes_avail);
4323 g_assert (total_number_of_bytes);
4324 g_assert (total_number_of_free_bytes);
4326 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
4327 #ifdef HAVE_STATVFS
4328 struct statvfs fsstat;
4329 #elif defined(HAVE_STATFS)
4330 struct statfs fsstat;
4331 #endif
4332 gboolean isreadonly;
4333 gchar *utf8_path_name;
4334 gint ret;
4335 unsigned long block_size;
4337 if (path_name == NULL) {
4338 utf8_path_name = g_strdup (g_get_current_dir());
4339 if (utf8_path_name == NULL) {
4340 mono_w32error_set_last (ERROR_DIRECTORY);
4341 return(FALSE);
4344 else {
4345 utf8_path_name = mono_unicode_to_external (path_name);
4346 if (utf8_path_name == NULL) {
4347 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
4349 mono_w32error_set_last (ERROR_INVALID_NAME);
4350 return(FALSE);
4354 do {
4355 #ifdef HAVE_STATVFS
4356 MONO_ENTER_GC_SAFE;
4357 ret = statvfs (utf8_path_name, &fsstat);
4358 MONO_EXIT_GC_SAFE;
4359 isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
4360 block_size = fsstat.f_frsize;
4361 #elif defined(HAVE_STATFS)
4362 MONO_ENTER_GC_SAFE;
4363 ret = statfs (utf8_path_name, &fsstat);
4364 MONO_EXIT_GC_SAFE;
4365 #if defined (MNT_RDONLY)
4366 isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
4367 #elif defined (MS_RDONLY)
4368 isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY);
4369 #endif
4370 block_size = fsstat.f_bsize;
4371 #endif
4372 } while(ret == -1 && errno == EINTR);
4374 g_free(utf8_path_name);
4376 if (ret == -1) {
4377 _wapi_set_last_error_from_errno ();
4378 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: statvfs failed: %s", __func__, g_strerror (errno));
4379 return(FALSE);
4382 /* total number of free bytes for non-root */
4383 if (isreadonly)
4384 *free_bytes_avail = 0;
4385 else
4386 *free_bytes_avail = block_size * (guint64)fsstat.f_bavail;
4388 /* total number of bytes available for non-root */
4389 *total_number_of_bytes = block_size * (guint64)fsstat.f_blocks;
4391 /* total number of bytes available for root */
4392 if (isreadonly)
4393 *total_number_of_free_bytes = 0;
4394 else
4395 *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree;
4396 #endif
4397 return(TRUE);
4399 #endif // PLATFORM_NO_DRIVEINFO
4402 * General Unix support
4404 typedef struct {
4405 guint32 drive_type;
4406 #if __linux__
4407 // http://man7.org/linux/man-pages/man2/statfs.2.html
4409 // The __fsword_t type used for various fields in the statfs structure
4410 // definition is a glibc internal type, not intended for public use.
4411 // This leaves the programmer in a bit of a conundrum when trying to
4412 // copy or compare these fields to local variables in a program. Using
4413 // unsigned int for such variables suffices on most systems.
4415 // Let's hope "most" is enough, and that it works with other libc.
4416 unsigned fstypeid;
4417 #endif
4418 const gchar* fstype;
4419 } _wapi_drive_type;
4421 static const _wapi_drive_type _wapi_drive_types[] = {
4422 #if HOST_DARWIN
4423 { DRIVE_REMOTE, "afp" },
4424 { DRIVE_REMOTE, "afpfs" },
4425 { DRIVE_REMOTE, "autofs" },
4426 { DRIVE_CDROM, "cddafs" },
4427 { DRIVE_CDROM, "cd9660" },
4428 { DRIVE_RAMDISK, "devfs" },
4429 { DRIVE_RAMDISK, "nullfs" },
4430 { DRIVE_FIXED, "exfat" },
4431 { DRIVE_RAMDISK, "fdesc" },
4432 { DRIVE_REMOTE, "ftp" },
4433 { DRIVE_FIXED, "hfs" },
4434 { DRIVE_FIXED, "apfs" },
4435 { DRIVE_REMOTE, "kbfuse" },
4436 { DRIVE_FIXED, "msdos" },
4437 { DRIVE_REMOTE, "nfs" },
4438 { DRIVE_FIXED, "ntfs" },
4439 { DRIVE_REMOTE, "smbfs" },
4440 { DRIVE_FIXED, "udf" },
4441 { DRIVE_REMOTE, "webdav" },
4442 { DRIVE_FIXED, "ufsd_NTFS"},
4443 { DRIVE_UNKNOWN, NULL }
4444 #elif __linux__
4445 { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"},
4446 { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"},
4447 { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"},
4448 { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"},
4449 { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"},
4450 { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" },
4451 { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"},
4452 { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"},
4453 { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"},
4454 { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"},
4455 { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"},
4456 { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"},
4457 { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"},
4458 { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"},
4459 { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"},
4460 { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"},
4461 { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"},
4462 { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"},
4463 { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"},
4464 { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"},
4465 { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"},
4466 { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"},
4467 { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"},
4468 { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"},
4469 { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"},
4470 { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"},
4471 { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"},
4472 { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"},
4473 { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"},
4474 { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"},
4475 { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"},
4476 { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"},
4477 { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"},
4478 { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"},
4479 { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"},
4480 { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"},
4481 { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"},
4482 { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"},
4483 { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"},
4484 { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"},
4485 { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"},
4486 { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"},
4487 { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"},
4488 { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"},
4489 { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"},
4490 { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"},
4491 { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"},
4492 { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"},
4493 { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"},
4494 { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"},
4495 { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"},
4496 { DRIVE_FIXED, UFS_MAGIC, "ufs"},
4497 { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"},
4498 { DRIVE_FIXED, UFS2_MAGIC, "ufs2"},
4499 { DRIVE_FIXED, UFS_CIGAM, "ufs"},
4500 { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"},
4501 { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"},
4502 { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"},
4503 { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"},
4504 { DRIVE_FIXED, V9FS_MAGIC, "9p"},
4505 { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"},
4506 { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"},
4507 { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"},
4508 { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"},
4509 { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"},
4510 { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"},
4511 { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"},
4512 { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"},
4513 { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"},
4514 { DRIVE_FIXED, OMFS_MAGIC, "omfs"},
4515 { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"},
4516 { DRIVE_UNKNOWN, 0, NULL}
4517 #else
4518 { DRIVE_RAMDISK, "ramfs" },
4519 { DRIVE_RAMDISK, "tmpfs" },
4520 { DRIVE_RAMDISK, "proc" },
4521 { DRIVE_RAMDISK, "sysfs" },
4522 { DRIVE_RAMDISK, "debugfs" },
4523 { DRIVE_RAMDISK, "devpts" },
4524 { DRIVE_RAMDISK, "securityfs" },
4525 { DRIVE_RAMDISK, "procfs" }, // AIX procfs
4526 { DRIVE_RAMDISK, "namefs" }, // AIX soft mounts
4527 { DRIVE_RAMDISK, "nullfs" },
4528 { DRIVE_CDROM, "iso9660" },
4529 { DRIVE_CDROM, "cdrfs" }, // AIX ISO9660 CDs
4530 { DRIVE_CDROM, "udfs" }, // AIX UDF CDs
4531 { DRIVE_CDROM, "QOPT" }, // IBM i CD mount
4532 { DRIVE_FIXED, "ext2" },
4533 { DRIVE_FIXED, "ext3" },
4534 { DRIVE_FIXED, "ext4" },
4535 { DRIVE_FIXED, "sysv" },
4536 { DRIVE_FIXED, "reiserfs" },
4537 { DRIVE_FIXED, "ufs" },
4538 { DRIVE_FIXED, "vfat" },
4539 { DRIVE_FIXED, "msdos" },
4540 { DRIVE_FIXED, "udf" },
4541 { DRIVE_FIXED, "hfs" },
4542 { DRIVE_FIXED, "hpfs" },
4543 { DRIVE_FIXED, "qnx4" },
4544 { DRIVE_FIXED, "ntfs" },
4545 { DRIVE_FIXED, "ntfs-3g" },
4546 { DRIVE_FIXED, "jfs" }, // IBM JFS
4547 { DRIVE_FIXED, "jfs2" }, // IBM JFS (AIX defalt filesystem)
4548 { DRIVE_FIXED, "EPFS" }, // IBM i IFS (root and QOpenSys)
4549 { DRIVE_FIXED, "EPFSP" }, // IBM i auxiliary storage pool FS
4550 { DRIVE_FIXED, "QSYS" }, // IBM i native system libraries
4551 { DRIVE_FIXED, "QDLS" }, // IBM i legacy S/36 directories
4552 { DRIVE_REMOTE, "smbfs" },
4553 { DRIVE_REMOTE, "fuse" },
4554 { DRIVE_REMOTE, "nfs" },
4555 { DRIVE_REMOTE, "nfs4" },
4556 { DRIVE_REMOTE, "cifs" },
4557 { DRIVE_REMOTE, "ncpfs" },
4558 { DRIVE_REMOTE, "coda" },
4559 { DRIVE_REMOTE, "afs" },
4560 { DRIVE_REMOTE, "nfs3" },
4561 { DRIVE_REMOTE, "stnfs" }, // AIX "short-term" NFS
4562 { DRIVE_REMOTE, "autofs" }, // AIX automounter NFS
4563 { DRIVE_REMOTE, "cachefs" }, // AIX cached NFS
4564 { DRIVE_REMOTE, "NFS" }, // IBM i NFS
4565 { DRIVE_REMOTE, "QNETC" }, // IBM i CIFS
4566 { DRIVE_REMOTE, "QRFS" }, // IBM i native remote FS
4567 { DRIVE_UNKNOWN, NULL }
4568 #endif
4571 #if __linux__
4572 static guint32 _wapi_get_drive_type(unsigned f_type)
4574 const _wapi_drive_type *current;
4576 current = &_wapi_drive_types[0];
4577 while (current->drive_type != DRIVE_UNKNOWN) {
4578 if (current->fstypeid == f_type)
4579 return current->drive_type;
4580 current++;
4583 return DRIVE_UNKNOWN;
4585 #else
4586 static guint32 _wapi_get_drive_type(const gchar* fstype)
4588 const _wapi_drive_type *current;
4590 current = &_wapi_drive_types[0];
4591 while (current->drive_type != DRIVE_UNKNOWN) {
4592 if (strcmp (current->fstype, fstype) == 0)
4593 return current->drive_type;
4595 current++;
4598 return DRIVE_UNKNOWN;
4600 #endif
4602 #if defined (HOST_DARWIN) || defined (__linux__) || defined (_AIX)
4603 static guint32
4604 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4606 #if defined (_AIX)
4607 struct statvfs buf;
4608 #else
4609 struct statfs buf;
4610 #endif
4611 gint res;
4613 MONO_ENTER_GC_SAFE;
4614 #if defined (_AIX)
4615 res = statvfs (utf8_root_path_name, &buf);
4616 #else
4617 res = statfs (utf8_root_path_name, &buf);
4618 #endif
4619 MONO_EXIT_GC_SAFE;
4620 if (res == -1)
4621 return DRIVE_UNKNOWN;
4622 #if HOST_DARWIN
4623 return _wapi_get_drive_type (buf.f_fstypename);
4624 #elif defined (_AIX)
4625 return _wapi_get_drive_type (buf.f_basetype);
4626 #else
4627 return _wapi_get_drive_type (buf.f_type);
4628 #endif
4630 #else
4631 static guint32
4632 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4634 guint32 drive_type;
4635 FILE *fp;
4636 gchar buffer [512];
4637 gchar **splitted;
4639 MONO_ENTER_GC_SAFE;
4640 fp = fopen ("/etc/mtab", "rt");
4641 MONO_EXIT_GC_SAFE;
4642 if (fp == NULL) {
4643 MONO_ENTER_GC_SAFE;
4644 fp = fopen ("/etc/mnttab", "rt");
4645 MONO_EXIT_GC_SAFE;
4646 if (fp == NULL)
4647 return(DRIVE_UNKNOWN);
4650 drive_type = DRIVE_NO_ROOT_DIR;
4651 while (1) {
4652 gchar *fgets_res;
4653 MONO_ENTER_GC_SAFE;
4654 fgets_res = fgets (buffer, 512, fp);
4655 MONO_EXIT_GC_SAFE;
4656 if (fgets_res == NULL)
4657 break;
4658 splitted = g_strsplit (buffer, " ", 0);
4659 if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
4660 g_strfreev (splitted);
4661 continue;
4664 /* compare given root_path_name with the one from mtab,
4665 if length of utf8_root_path_name is zero it must be the root dir */
4666 if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
4667 (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
4668 drive_type = _wapi_get_drive_type (*(splitted + 2));
4669 /* it is possible this path might be mounted again with
4670 a known type...keep looking */
4671 if (drive_type != DRIVE_UNKNOWN) {
4672 g_strfreev (splitted);
4673 break;
4677 g_strfreev (splitted);
4680 MONO_ENTER_GC_SAFE;
4681 fclose (fp);
4682 MONO_EXIT_GC_SAFE;
4683 return drive_type;
4685 #endif
4687 guint32
4688 ves_icall_System_IO_DriveInfo_GetDriveType (const gunichar2 *root_path_name, gint32 root_path_name_length, MonoError *error)
4690 // FIXME Check for embedded nuls here or in managed.
4692 gchar *utf8_root_path_name;
4693 guint32 drive_type;
4695 if (root_path_name == NULL) {
4696 utf8_root_path_name = g_strdup (g_get_current_dir());
4697 if (utf8_root_path_name == NULL) {
4698 return(DRIVE_NO_ROOT_DIR);
4701 else {
4702 utf8_root_path_name = mono_unicode_to_external (root_path_name);
4703 if (utf8_root_path_name == NULL) {
4704 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: unicode conversion returned NULL", __func__);
4705 return(DRIVE_NO_ROOT_DIR);
4708 /* strip trailing slash for compare below */
4709 if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) {
4710 utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
4713 drive_type = GetDriveTypeFromPath (utf8_root_path_name);
4714 g_free (utf8_root_path_name);
4716 return (drive_type);
4719 #if defined (HOST_DARWIN) || defined (__linux__) || defined(HOST_BSD) || defined(__FreeBSD_kernel__) || defined(__HAIKU__) || defined(_AIX)
4720 static gchar*
4721 get_fstypename (gchar *utfpath)
4723 #if defined (_AIX)
4724 /* statvfs offers the FS type name, easily, no need to iterate */
4725 struct statvfs stat;
4726 gint statvfs_res;
4728 MONO_ENTER_GC_SAFE;
4729 statvfs_res = statvfs (utfpath, &stat);
4730 MONO_EXIT_GC_SAFE;
4732 if (statvfs_res != -1) {
4733 return g_strdup (stat.f_basetype);
4736 return NULL;
4737 #elif defined (HOST_DARWIN) || defined (__linux__)
4738 struct statfs stat;
4739 #if __linux__
4740 const _wapi_drive_type *current;
4741 #endif
4742 gint statfs_res;
4743 MONO_ENTER_GC_SAFE;
4744 statfs_res = statfs (utfpath, &stat);
4745 MONO_EXIT_GC_SAFE;
4746 if (statfs_res == -1)
4747 return NULL;
4748 #if HOST_DARWIN
4749 return g_strdup (stat.f_fstypename);
4750 #else
4751 current = &_wapi_drive_types[0];
4752 while (current->drive_type != DRIVE_UNKNOWN) {
4753 if (stat.f_type == current->fstypeid)
4754 return g_strdup (current->fstype);
4755 current++;
4757 return NULL;
4758 #endif
4759 #else
4760 return NULL;
4761 #endif
4764 /* Linux has struct statfs which has a different layout */
4765 gboolean
4766 mono_w32file_get_file_system_type (const gunichar2 *path, gunichar2 *fsbuffer, gint fsbuffersize)
4768 gchar *utfpath;
4769 gchar *fstypename;
4770 gboolean status = FALSE;
4771 glong len;
4773 // We only support getting the file system type
4774 if (fsbuffer == NULL)
4775 return 0;
4777 utfpath = mono_unicode_to_external (path);
4778 if ((fstypename = get_fstypename (utfpath)) != NULL){
4779 gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL);
4780 if (ret != NULL && len < fsbuffersize){
4781 memcpy (fsbuffer, ret, len * sizeof (gunichar2));
4782 fsbuffer [len] = 0;
4783 status = TRUE;
4785 if (ret != NULL)
4786 g_free (ret);
4787 g_free (fstypename);
4789 g_free (utfpath);
4790 return status;
4792 #endif
4794 static gboolean
4795 LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4797 FileHandle *filehandle;
4798 gboolean ret;
4799 off_t offset, length;
4801 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
4802 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4803 return FALSE;
4806 if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_FILE) {
4807 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4808 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4809 return FALSE;
4812 if (!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
4813 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);
4814 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4815 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4816 return FALSE;
4819 #ifdef HAVE_LARGE_FILE_SUPPORT
4820 offset = ((gint64)offset_high << 32) | offset_low;
4821 length = ((gint64)length_high << 32) | length_low;
4823 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);
4824 #else
4825 if (offset_high > 0 || length_high > 0) {
4826 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
4827 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4828 return FALSE;
4831 offset = offset_low;
4832 length = length_low;
4834 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);
4835 #endif
4837 ret = _wapi_lock_file_region (((MonoFDHandle*) filehandle)->fd, offset, length);
4839 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4840 return ret;
4843 static gboolean
4844 UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4846 FileHandle *filehandle;
4847 gboolean ret;
4848 off_t offset, length;
4850 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle), (MonoFDHandle**) &filehandle)) {
4851 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4852 return FALSE;
4855 if (((MonoFDHandle*) filehandle)->type != MONO_FDTYPE_FILE) {
4856 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4857 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4858 return FALSE;
4861 if (!(filehandle->fileaccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_ALL))) {
4862 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);
4863 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4864 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4865 return FALSE;
4868 #ifdef HAVE_LARGE_FILE_SUPPORT
4869 offset = ((gint64)offset_high << 32) | offset_low;
4870 length = ((gint64)length_high << 32) | length_low;
4871 #else
4872 offset = offset_low;
4873 length = length_low;
4874 #endif
4875 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);
4877 ret = _wapi_unlock_file_region (((MonoFDHandle*) filehandle)->fd, offset, length);
4879 mono_fdhandle_unref ((MonoFDHandle*) filehandle);
4880 return ret;
4883 void
4884 mono_w32file_init (void)
4886 MonoFDHandleCallback file_data_callbacks;
4887 memset (&file_data_callbacks, 0, sizeof (file_data_callbacks));
4888 file_data_callbacks.close = file_data_close;
4889 file_data_callbacks.destroy = file_data_destroy;
4891 mono_fdhandle_register (MONO_FDTYPE_FILE, &file_data_callbacks);
4892 mono_fdhandle_register (MONO_FDTYPE_CONSOLE, &file_data_callbacks);
4893 mono_fdhandle_register (MONO_FDTYPE_PIPE, &file_data_callbacks);
4895 mono_coop_mutex_init (&file_share_mutex);
4897 finds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, finds_remove);
4898 mono_coop_mutex_init (&finds_mutex);
4900 #if HOST_DARWIN
4901 libc_handle = mono_dl_open ("/usr/lib/libc.dylib", 0, NULL);
4902 g_assert (libc_handle);
4903 g_free (mono_dl_symbol (libc_handle, "clonefile", (void**)&clonefile_ptr));
4904 #endif
4906 if (g_hasenv ("MONO_STRICT_IO_EMULATION"))
4907 lock_while_writing = TRUE;
4910 void
4911 mono_w32file_cleanup (void)
4913 mono_coop_mutex_destroy (&file_share_mutex);
4915 if (file_share_table)
4916 g_hash_table_destroy (file_share_table);
4918 g_hash_table_destroy (finds);
4919 mono_coop_mutex_destroy (&finds_mutex);
4921 #if HOST_DARWIN
4922 mono_dl_close (libc_handle);
4923 #endif
4926 gboolean
4927 mono_w32file_move (const gunichar2 *path, const gunichar2 *dest, gint32 *error)
4929 gboolean result;
4931 result = MoveFile (path, dest);
4932 if (!result)
4933 *error = mono_w32error_get_last ();
4934 return result;
4937 gboolean
4938 mono_w32file_copy (const gunichar2 *path, const gunichar2 *dest, gboolean overwrite, gint32 *error)
4940 gboolean result;
4942 result = CopyFile (path, dest, !overwrite);
4943 if (!result)
4944 *error = mono_w32error_get_last ();
4946 return result;
4949 gboolean
4950 mono_w32file_replace (const gunichar2 *destination_file_name, const gunichar2 *source_file_name, const gunichar2 *destination_backup_file_name, guint32 flags, gint32 *error)
4952 gboolean result;
4954 result = ReplaceFile (destination_file_name, source_file_name, destination_backup_file_name, flags, NULL, NULL);
4955 if (!result)
4956 *error = mono_w32error_get_last ();
4957 return result;
4960 gint64
4961 mono_w32file_get_file_size (gpointer handle, gint32 *error)
4963 gint64 length;
4964 guint32 length_hi;
4966 length = GetFileSize (handle, &length_hi);
4967 if(length==INVALID_FILE_SIZE) {
4968 *error=mono_w32error_get_last ();
4971 return length | ((gint64)length_hi << 32);
4974 gboolean
4975 mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error)
4977 gboolean result;
4979 result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
4980 if (!result)
4981 *error = mono_w32error_get_last ();
4982 return result;
4985 gboolean
4986 mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error)
4988 gboolean result;
4990 result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
4991 if (!result)
4992 *error = mono_w32error_get_last ();
4993 return result;
4996 gpointer
4997 mono_w32file_get_console_input (void)
4999 return mono_w32file_get_std_handle (STD_INPUT_HANDLE);
5002 gpointer
5003 mono_w32file_get_console_output (void)
5005 return mono_w32file_get_std_handle (STD_OUTPUT_HANDLE);
5008 gpointer
5009 mono_w32file_get_console_error (void)
5011 return mono_w32file_get_std_handle (STD_ERROR_HANDLE);