14 #ifdef HAVE_SYS_STATVFS_H
15 #include <sys/statvfs.h>
17 #if defined(HAVE_SYS_STATFS_H)
18 #include <sys/statfs.h>
20 #if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
21 #include <sys/param.h>
22 #include <sys/mount.h>
24 #include <sys/types.h>
30 #include <sys/ioctl.h>
32 #include <mono/utils/linux_magic.h>
35 #include <sys/mntctl.h>
36 #include <sys/vmount.h>
47 #include "w32file-internals.h"
48 #include "w32file-unix-glob.h"
51 #include "utils/mono-error-internals.h"
52 #include "utils/mono-io-portability.h"
53 #include "utils/mono-logger-internals.h"
54 #include "utils/mono-os-mutex.h"
55 #include "utils/mono-threads.h"
56 #include "utils/mono-threads-api.h"
57 #include "utils/strenc-internals.h"
58 #include "utils/strenc.h"
59 #include "utils/refcount.h"
60 #include "icall-decl.h"
61 #include "utils/mono-errno.h"
63 #define NANOSECONDS_PER_MICROSECOND 1000LL
64 #define TICKS_PER_MICROSECOND 10L
65 #define TICKS_PER_MILLISECOND 10000L
66 #define TICKS_PER_SECOND 10000000LL
67 #define TICKS_PER_MINUTE 600000000LL
68 #define TICKS_PER_HOUR 36000000000LL
69 #define TICKS_PER_DAY 864000000000LL
71 // Constants to convert Unix times to the API expected by .NET and Windows
72 #define CONVERT_BASE 116444736000000000ULL
74 #define INVALID_HANDLE_VALUE ((gpointer)-1)
85 /* Currently used for both FILE, CONSOLE and PIPE handle types.
86 * This may have to change in future. */
88 MonoFDHandle fdhandle
;
90 FileShare
*share_info
; /* Pointer into shared mem */
91 guint32 security_attributes
;
107 * If SHM is disabled, this will point to a hash of FileShare structures, otherwise
108 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
111 static GHashTable
*file_share_table
;
112 static MonoCoopMutex file_share_mutex
;
114 static GHashTable
*finds
;
115 static MonoCoopMutex finds_mutex
;
118 typedef int (*clonefile_fn
) (const char *from
, const char *to
, int flags
);
119 static MonoDl
*libc_handle
;
120 static clonefile_fn clonefile_ptr
;
124 time_t_to_filetime (time_t timeval
, FILETIME
*filetime
)
128 ticks
= ((guint64
)timeval
* 10000000) + CONVERT_BASE
;
129 filetime
->dwLowDateTime
= ticks
& 0xFFFFFFFF;
130 filetime
->dwHighDateTime
= ticks
>> 32;
134 file_data_create (MonoFDType type
, gint fd
)
136 FileHandle
*filehandle
;
138 filehandle
= g_new0 (FileHandle
, 1);
139 mono_fdhandle_init ((MonoFDHandle
*) filehandle
, type
, fd
);
145 _wapi_unlink (const gchar
*pathname
);
148 file_share_release (FileShare
*share_info
);
151 file_data_close (MonoFDHandle
*fdhandle
)
153 FileHandle
* filehandle
;
155 filehandle
= (FileHandle
*) fdhandle
;
156 g_assert (filehandle
);
158 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: closing fd %d", __func__
, ((MonoFDHandle
*) filehandle
)->fd
);
160 if (((MonoFDHandle
*) filehandle
)->type
== MONO_FDTYPE_FILE
&& (filehandle
->attrs
& FILE_FLAG_DELETE_ON_CLOSE
)) {
161 _wapi_unlink (filehandle
->filename
);
164 if (((MonoFDHandle
*) filehandle
)->type
!= MONO_FDTYPE_CONSOLE
|| ((MonoFDHandle
*) filehandle
)->fd
> 2) {
165 if (filehandle
->share_info
) {
166 file_share_release (filehandle
->share_info
);
167 filehandle
->share_info
= NULL
;
171 close (((MonoFDHandle
*) filehandle
)->fd
);
177 file_data_destroy (MonoFDHandle
*fdhandle
)
179 FileHandle
*filehandle
;
181 filehandle
= (FileHandle
*) fdhandle
;
182 g_assert (filehandle
);
184 if (filehandle
->filename
)
185 g_free (filehandle
->filename
);
191 file_share_release (FileShare
*share_info
)
193 /* Prevent new entries racing with us */
194 mono_coop_mutex_lock (&file_share_mutex
);
196 g_assert (share_info
->handle_refs
> 0);
197 share_info
->handle_refs
-= 1;
199 if (share_info
->handle_refs
== 0) {
200 g_hash_table_remove (file_share_table
, share_info
);
201 // g_free (share_info);
204 mono_coop_mutex_unlock (&file_share_mutex
);
208 file_share_equal (gconstpointer ka
, gconstpointer kb
)
210 const FileShare
*s1
= (const FileShare
*)ka
;
211 const FileShare
*s2
= (const FileShare
*)kb
;
213 return (s1
->device
== s2
->device
&& s1
->inode
== s2
->inode
) ? 1 : 0;
217 file_share_hash (gconstpointer data
)
219 const FileShare
*s
= (const FileShare
*)data
;
225 file_share_get (guint64 device
, guint64 inode
, guint32 new_sharemode
, guint32 new_access
,
226 guint32
*old_sharemode
, guint32
*old_access
, FileShare
**share_info
)
228 FileShare
*file_share
;
229 gboolean exists
= FALSE
;
231 /* Prevent new entries racing with us */
232 mono_coop_mutex_lock (&file_share_mutex
);
237 * Instead of allocating a 4MB array, we use a hash table to keep track of this
238 * info. This is needed even if SHM is disabled, to track sharing inside
239 * the current process.
241 if (!file_share_table
)
242 file_share_table
= g_hash_table_new_full (file_share_hash
, file_share_equal
, NULL
, g_free
);
247 file_share
= (FileShare
*)g_hash_table_lookup (file_share_table
, &tmp
);
249 *old_sharemode
= file_share
->sharemode
;
250 *old_access
= file_share
->access
;
251 *share_info
= file_share
;
253 g_assert (file_share
->handle_refs
> 0);
254 file_share
->handle_refs
+= 1;
258 file_share
= g_new0 (FileShare
, 1);
260 file_share
->device
= device
;
261 file_share
->inode
= inode
;
262 file_share
->sharemode
= new_sharemode
;
263 file_share
->access
= new_access
;
264 file_share
->handle_refs
= 1;
265 *share_info
= file_share
;
267 g_hash_table_insert (file_share_table
, file_share
, file_share
);
270 mono_coop_mutex_unlock (&file_share_mutex
);
276 _wapi_open (const gchar
*pathname
, gint flags
, mode_t mode
)
279 gchar
*located_filename
;
281 if (flags
& O_CREAT
) {
282 located_filename
= mono_portability_find_file (pathname
, FALSE
);
283 if (located_filename
== NULL
) {
285 fd
= open (pathname
, flags
, mode
);
289 fd
= open (located_filename
, flags
, mode
);
291 g_free (located_filename
);
295 fd
= open (pathname
, flags
, mode
);
297 if (fd
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
298 gint saved_errno
= errno
;
299 located_filename
= mono_portability_find_file (pathname
, TRUE
);
301 if (located_filename
== NULL
) {
302 mono_set_errno (saved_errno
);
307 fd
= open (located_filename
, flags
, mode
);
309 g_free (located_filename
);
317 _wapi_access (const gchar
*pathname
, gint mode
)
322 ret
= access (pathname
, mode
);
324 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
325 gint saved_errno
= errno
;
326 gchar
*located_filename
= mono_portability_find_file (pathname
, TRUE
);
328 if (located_filename
== NULL
) {
329 mono_set_errno (saved_errno
);
334 ret
= access (located_filename
, mode
);
336 g_free (located_filename
);
343 _wapi_chmod (const gchar
*pathname
, mode_t mode
)
348 #if defined(HAVE_CHMOD)
349 ret
= chmod (pathname
, mode
);
354 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
355 gint saved_errno
= errno
;
356 gchar
*located_filename
= mono_portability_find_file (pathname
, TRUE
);
358 if (located_filename
== NULL
) {
359 mono_set_errno (saved_errno
);
363 #if defined(HAVE_CHMOD)
365 ret
= chmod (located_filename
, mode
);
370 g_free (located_filename
);
376 #ifndef HAVE_STRUCT_TIMEVAL
378 _wapi_utime (const gchar
*filename
, const struct utimbuf
*buf
)
384 ret
= utime (filename
, buf
);
386 if (ret
== -1 && errno
== ENOENT
&& IS_PORTABILITY_SET
) {
387 gint saved_errno
= errno
;
388 gchar
*located_filename
= mono_portability_find_file (filename
, TRUE
);
390 if (located_filename
== NULL
) {
391 mono_set_errno (saved_errno
);
396 ret
= utime (located_filename
, buf
);
398 g_free (located_filename
);
407 _wapi_utimes (const gchar
*filename
, const struct timeval times
[2])
413 ret
= utimes (filename
, times
);
415 if (ret
== -1 && errno
== ENOENT
&& IS_PORTABILITY_SET
) {
416 gint saved_errno
= errno
;
417 gchar
*located_filename
= mono_portability_find_file (filename
, TRUE
);
419 if (located_filename
== NULL
) {
420 mono_set_errno (saved_errno
);
425 ret
= utimes (located_filename
, times
);
427 g_free (located_filename
);
436 _wapi_unlink (const gchar
*pathname
)
441 ret
= unlink (pathname
);
443 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
|| errno
== EISDIR
) && IS_PORTABILITY_SET
) {
444 gint saved_errno
= errno
;
445 gchar
*located_filename
= mono_portability_find_file (pathname
, TRUE
);
447 if (located_filename
== NULL
) {
448 mono_set_errno (saved_errno
);
453 ret
= unlink (located_filename
);
455 g_free (located_filename
);
462 _wapi_rename (const gchar
*oldpath
, const gchar
*newpath
)
465 gchar
*located_newpath
= mono_portability_find_file (newpath
, FALSE
);
467 if (located_newpath
== NULL
) {
469 ret
= rename (oldpath
, newpath
);
473 ret
= rename (oldpath
, located_newpath
);
476 if (ret
== -1 && (errno
== EISDIR
|| errno
== ENAMETOOLONG
|| errno
== ENOENT
|| errno
== ENOTDIR
|| errno
== EXDEV
) && IS_PORTABILITY_SET
) {
477 gint saved_errno
= errno
;
478 gchar
*located_oldpath
= mono_portability_find_file (oldpath
, TRUE
);
480 if (located_oldpath
== NULL
) {
481 g_free (located_oldpath
);
482 g_free (located_newpath
);
484 mono_set_errno (saved_errno
);
489 ret
= rename (located_oldpath
, located_newpath
);
491 g_free (located_oldpath
);
493 g_free (located_newpath
);
500 _wapi_stat (const gchar
*path
, struct stat
*buf
)
505 ret
= stat (path
, buf
);
507 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
508 gint saved_errno
= errno
;
509 gchar
*located_filename
= mono_portability_find_file (path
, TRUE
);
511 if (located_filename
== NULL
) {
512 mono_set_errno (saved_errno
);
517 ret
= stat (located_filename
, buf
);
519 g_free (located_filename
);
526 _wapi_lstat (const gchar
*path
, struct stat
*buf
)
532 ret
= lstat (path
, buf
);
534 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
535 gint saved_errno
= errno
;
536 gchar
*located_filename
= mono_portability_find_file (path
, TRUE
);
538 if (located_filename
== NULL
) {
539 mono_set_errno (saved_errno
);
543 ret
= lstat (located_filename
, buf
);
544 g_free (located_filename
);
554 _wapi_mkdir (const gchar
*pathname
, mode_t mode
)
557 gchar
*located_filename
= mono_portability_find_file (pathname
, FALSE
);
559 if (located_filename
== NULL
) {
561 ret
= mkdir (pathname
, mode
);
565 ret
= mkdir (located_filename
, mode
);
567 g_free (located_filename
);
574 _wapi_rmdir (const gchar
*pathname
)
579 ret
= rmdir (pathname
);
581 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
|| errno
== ENAMETOOLONG
) && IS_PORTABILITY_SET
) {
582 gint saved_errno
= errno
;
583 gchar
*located_filename
= mono_portability_find_file (pathname
, TRUE
);
585 if (located_filename
== NULL
) {
586 mono_set_errno (saved_errno
);
591 ret
= rmdir (located_filename
);
593 g_free (located_filename
);
600 _wapi_chdir (const gchar
*path
)
607 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
|| errno
== ENAMETOOLONG
) && IS_PORTABILITY_SET
) {
608 gint saved_errno
= errno
;
609 gchar
*located_filename
= mono_portability_find_file (path
, TRUE
);
611 if (located_filename
== NULL
) {
612 mono_set_errno (saved_errno
);
617 ret
= chdir (located_filename
);
619 g_free (located_filename
);
626 _wapi_basename (const gchar
*filename
)
628 gchar
*new_filename
= g_strdup (filename
), *ret
;
630 if (IS_PORTABILITY_SET
)
631 g_strdelimit (new_filename
, '\\', '/');
633 if (IS_PORTABILITY_DRIVE
&& g_ascii_isalpha (new_filename
[0]) && (new_filename
[1] == ':')) {
634 gint len
= strlen (new_filename
);
636 g_memmove (new_filename
, new_filename
+ 2, len
- 2);
637 new_filename
[len
- 2] = '\0';
640 ret
= g_path_get_basename (new_filename
);
641 g_free (new_filename
);
647 _wapi_dirname (const gchar
*filename
)
649 gchar
*new_filename
= g_strdup (filename
), *ret
;
651 if (IS_PORTABILITY_SET
)
652 g_strdelimit (new_filename
, '\\', '/');
654 if (IS_PORTABILITY_DRIVE
&& g_ascii_isalpha (new_filename
[0]) && (new_filename
[1] == ':')) {
655 gint len
= strlen (new_filename
);
657 g_memmove (new_filename
, new_filename
+ 2, len
- 2);
658 new_filename
[len
- 2] = '\0';
661 ret
= g_path_get_dirname (new_filename
);
662 g_free (new_filename
);
668 _wapi_g_dir_open (const gchar
*path
, guint flags
, GError
**gerror
)
673 ret
= g_dir_open (path
, flags
, gerror
);
675 if (ret
== NULL
&& ((*gerror
)->code
== G_FILE_ERROR_NOENT
|| (*gerror
)->code
== G_FILE_ERROR_NOTDIR
|| (*gerror
)->code
== G_FILE_ERROR_NAMETOOLONG
) && IS_PORTABILITY_SET
) {
676 gchar
*located_filename
= mono_portability_find_file (path
, TRUE
);
677 GError
*tmp_error
= NULL
;
679 if (located_filename
== NULL
) {
684 ret
= g_dir_open (located_filename
, flags
, &tmp_error
);
686 g_free (located_filename
);
687 if (tmp_error
== NULL
) {
688 g_clear_error (gerror
);
696 get_errno_from_g_file_error (gint error
)
700 case G_FILE_ERROR_ACCES
: return EACCES
;
703 case G_FILE_ERROR_NAMETOOLONG
: return ENAMETOOLONG
;
706 case G_FILE_ERROR_NOENT
: return ENOENT
;
709 case G_FILE_ERROR_NOTDIR
: return ENOTDIR
;
712 case G_FILE_ERROR_NXIO
: return ENXIO
;
715 case G_FILE_ERROR_NODEV
: return ENODEV
;
718 case G_FILE_ERROR_ROFS
: return EROFS
;
721 case G_FILE_ERROR_TXTBSY
: return ETXTBSY
;
724 case G_FILE_ERROR_FAULT
: return EFAULT
;
727 case G_FILE_ERROR_LOOP
: return ELOOP
;
730 case G_FILE_ERROR_NOSPC
: return ENOSPC
;
733 case G_FILE_ERROR_NOMEM
: return ENOMEM
;
736 case G_FILE_ERROR_MFILE
: return EMFILE
;
739 case G_FILE_ERROR_NFILE
: return ENFILE
;
742 case G_FILE_ERROR_BADF
: return EBADF
;
745 case G_FILE_ERROR_INVAL
: return EINVAL
;
748 case G_FILE_ERROR_PIPE
: return EPIPE
;
751 case G_FILE_ERROR_AGAIN
: return EAGAIN
;
754 case G_FILE_ERROR_INTR
: return EINTR
;
757 case G_FILE_ERROR_IO
: return EIO
;
760 case G_FILE_ERROR_PERM
: return EPERM
;
762 case G_FILE_ERROR_FAILED
: return ERROR_INVALID_PARAMETER
;
764 g_assert_not_reached ();
769 file_compare (gconstpointer a
, gconstpointer b
)
771 gchar
*astr
= *(gchar
**) a
;
772 gchar
*bstr
= *(gchar
**) b
;
774 return strcmp (astr
, bstr
);
777 /* scandir using glib */
779 _wapi_io_scandir (const gchar
*dirname
, const gchar
*pattern
, gchar
***namelist
)
781 GError
*gerror
= NULL
;
785 mono_w32file_unix_glob_t glob_buf
;
788 dir
= _wapi_g_dir_open (dirname
, 0, &gerror
);
790 /* g_dir_open returns ENOENT on directories on which we don't
791 * have read/x permission */
792 gint errnum
= get_errno_from_g_file_error (gerror
->code
);
793 g_error_free (gerror
);
794 if (errnum
== ENOENT
&&
795 !_wapi_access (dirname
, F_OK
) &&
796 _wapi_access (dirname
, R_OK
|X_OK
)) {
800 mono_set_errno (errnum
);
804 if (IS_PORTABILITY_CASE
) {
805 flags
= W32FILE_UNIX_GLOB_IGNORECASE
;
808 result
= mono_w32file_unix_glob (dir
, pattern
, flags
, &glob_buf
);
809 if (g_str_has_suffix (pattern
, ".*")) {
810 /* Special-case the patterns ending in '.*', as
811 * windows also matches entries with no extension with
814 * TODO: should this be a MONO_IOMAP option?
816 gchar
*pattern2
= g_strndup (pattern
, strlen (pattern
) - 2);
822 result2
= mono_w32file_unix_glob (dir
, pattern2
, flags
| W32FILE_UNIX_GLOB_APPEND
| W32FILE_UNIX_GLOB_UNIQUE
, &glob_buf
);
834 if (glob_buf
.gl_pathc
== 0) {
836 } else if (result
!= 0) {
840 names
= g_ptr_array_new ();
841 for (i
= 0; i
< glob_buf
.gl_pathc
; i
++) {
842 g_ptr_array_add (names
, g_strdup (glob_buf
.gl_pathv
[i
]));
845 mono_w32file_unix_globfree (&glob_buf
);
849 g_ptr_array_sort (names
, file_compare
);
850 g_ptr_array_set_size (names
, result
+ 1);
852 *namelist
= (gchar
**) g_ptr_array_free (names
, FALSE
);
854 g_ptr_array_free (names
, TRUE
);
861 _wapi_lock_file_region (gint fd
, off_t offset
, off_t length
)
863 struct flock lock_data
;
866 if (offset
< 0 || length
< 0) {
867 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
871 lock_data
.l_type
= F_WRLCK
;
872 lock_data
.l_whence
= SEEK_SET
;
873 lock_data
.l_start
= offset
;
874 lock_data
.l_len
= length
;
877 ret
= fcntl (fd
, F_SETLK
, &lock_data
);
878 } while(ret
== -1 && errno
== EINTR
);
880 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fcntl returns %d", __func__
, ret
);
884 * if locks are not available (NFS for example),
889 || errno
== EOPNOTSUPP
898 mono_w32error_set_last (ERROR_LOCK_VIOLATION
);
906 _wapi_unlock_file_region (gint fd
, off_t offset
, off_t length
)
908 struct flock lock_data
;
911 lock_data
.l_type
= F_UNLCK
;
912 lock_data
.l_whence
= SEEK_SET
;
913 lock_data
.l_start
= offset
;
914 lock_data
.l_len
= length
;
917 ret
= fcntl (fd
, F_SETLK
, &lock_data
);
918 } while(ret
== -1 && errno
== EINTR
);
920 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fcntl returns %d", __func__
, ret
);
924 * if locks are not available (NFS for example),
929 || errno
== EOPNOTSUPP
938 mono_w32error_set_last (ERROR_LOCK_VIOLATION
);
945 static gboolean lock_while_writing
= FALSE
;
947 /* Some utility functions.
951 * Check if a file is writable by the current user.
953 * This is is a best effort kind of thing. It assumes a reasonable sane set
954 * of permissions by the underlying OS.
956 * We generally assume that basic unix permission bits are authoritative. Which might not
957 * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc)
959 * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file.
961 * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent
962 * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous
963 * and should not be used with a dynamic runtime.
966 is_file_writable (struct stat
*st
, const gchar
*path
)
972 // OS X Finder "locked" or `ls -lO` "uchg".
973 // This only covers one of several cases where an OS X file could be unwritable through special flags.
974 if (st
->st_flags
& (UF_IMMUTABLE
|SF_IMMUTABLE
))
978 /* Is it globally writable? */
979 if (st
->st_mode
& S_IWOTH
)
982 /* Am I the owner? */
983 if ((st
->st_uid
== geteuid ()) && (st
->st_mode
& S_IWUSR
))
986 /* Am I in the same group? */
987 if ((st
->st_gid
== getegid ()) && (st
->st_mode
& S_IWGRP
))
990 located_path
= mono_portability_find_file (path
, FALSE
);
992 /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid
993 * but it's the only sane option we have on unix.
996 ret
= access (located_path
!= NULL
? located_path
: path
, W_OK
) == 0;
999 g_free (located_path
);
1005 static guint32
_wapi_stat_to_file_attributes (const gchar
*pathname
,
1012 /* FIXME: this could definitely be better, but there seems to
1013 * be no pattern to the attributes that are set
1016 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
1017 if (S_ISSOCK (buf
->st_mode
))
1018 buf
->st_mode
&= ~S_IFSOCK
; /* don't consider socket protection */
1020 filename
= _wapi_basename (pathname
);
1022 if (S_ISDIR (buf
->st_mode
)) {
1023 attrs
= FILE_ATTRIBUTE_DIRECTORY
;
1024 if (!is_file_writable (buf
, pathname
)) {
1025 attrs
|= FILE_ATTRIBUTE_READONLY
;
1027 if (filename
[0] == '.') {
1028 attrs
|= FILE_ATTRIBUTE_HIDDEN
;
1031 if (!is_file_writable (buf
, pathname
)) {
1032 attrs
= FILE_ATTRIBUTE_READONLY
;
1034 if (filename
[0] == '.') {
1035 attrs
|= FILE_ATTRIBUTE_HIDDEN
;
1037 } else if (filename
[0] == '.') {
1038 attrs
= FILE_ATTRIBUTE_HIDDEN
;
1040 attrs
= FILE_ATTRIBUTE_NORMAL
;
1045 if (S_ISLNK (lbuf
->st_mode
)) {
1046 attrs
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1056 _wapi_set_last_error_from_errno (void)
1058 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
1061 static void _wapi_set_last_path_error_from_errno (const gchar
*dir
,
1064 if (errno
== ENOENT
) {
1065 /* Check the path - if it's a missing directory then
1066 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
1072 dirname
= _wapi_dirname (path
);
1074 dirname
= g_strdup (dir
);
1077 if (_wapi_access (dirname
, F_OK
) == 0) {
1078 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1080 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1085 _wapi_set_last_error_from_errno ();
1090 file_read(FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
)
1093 MonoThreadInfo
*info
= mono_thread_info_current ();
1095 if(bytesread
!=NULL
) {
1099 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_ALL
))) {
1100 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1102 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1108 ret
= read (((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1110 } while (ret
== -1 && errno
== EINTR
&&
1111 !mono_thread_info_is_interrupt_state (info
));
1116 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: read of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(err
));
1117 mono_w32error_set_last (mono_w32error_unix_to_win32 (err
));
1121 if (bytesread
!= NULL
) {
1129 file_write (FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*byteswritten
)
1132 off_t current_pos
= 0;
1133 MonoThreadInfo
*info
= mono_thread_info_current ();
1135 if(byteswritten
!=NULL
) {
1139 if(!(filehandle
->fileaccess
& GENERIC_WRITE
) && !(filehandle
->fileaccess
& GENERIC_ALL
)) {
1140 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1142 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1146 if (lock_while_writing
) {
1147 /* Need to lock the region we're about to write to,
1148 * because we only do advisory locking on POSIX
1152 current_pos
= lseek (((MonoFDHandle
*) filehandle
)->fd
, (off_t
)0, SEEK_CUR
);
1154 if (current_pos
== -1) {
1155 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d lseek failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror (errno
));
1156 _wapi_set_last_error_from_errno ();
1160 if (_wapi_lock_file_region (((MonoFDHandle
*) filehandle
)->fd
, current_pos
, numbytes
) == FALSE
) {
1161 /* The error has already been set */
1168 ret
= write (((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1170 } while (ret
== -1 && errno
== EINTR
&&
1171 !mono_thread_info_is_interrupt_state (info
));
1173 if (lock_while_writing
) {
1174 _wapi_unlock_file_region (((MonoFDHandle
*) filehandle
)->fd
, current_pos
, numbytes
);
1178 if (errno
== EINTR
) {
1181 _wapi_set_last_error_from_errno ();
1183 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: write of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1188 if (byteswritten
!= NULL
) {
1189 *byteswritten
= ret
;
1194 static gboolean
file_flush(FileHandle
*filehandle
)
1198 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1199 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1201 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1206 ret
=fsync(((MonoFDHandle
*) filehandle
)->fd
);
1209 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fsync of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1211 _wapi_set_last_error_from_errno ();
1218 static guint32
file_seek(FileHandle
*filehandle
, gint32 movedistance
,
1219 gint32
*highmovedistance
, gint method
)
1221 gint64 offset
, newpos
;
1225 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_ALL
))) {
1226 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1228 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1229 return(INVALID_SET_FILE_POINTER
);
1243 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: invalid seek type %d", __func__
, method
);
1245 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1246 return(INVALID_SET_FILE_POINTER
);
1249 #ifdef HAVE_LARGE_FILE_SUPPORT
1250 if(highmovedistance
==NULL
) {
1251 offset
=movedistance
;
1252 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: setting offset to %" G_GINT64_FORMAT
" (low %" G_GINT32_FORMAT
")", __func__
,
1253 offset
, movedistance
);
1255 offset
=((gint64
) *highmovedistance
<< 32) | (guint32
)movedistance
;
1257 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: setting offset to %" G_GINT64_FORMAT
" 0x%" PRIx64
" (high %" G_GINT32_FORMAT
" 0x%" PRIx32
", low %" G_GINT32_FORMAT
" 0x%" PRIx32
")",
1258 __func__
, offset
, offset
, *highmovedistance
, *highmovedistance
, movedistance
, movedistance
);
1261 offset
=movedistance
;
1264 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: moving fd %d by %" G_GINT64_FORMAT
" bytes from %d", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, offset
, whence
);
1267 /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */
1269 newpos
=lseek64(((MonoFDHandle
*) filehandle
)->fd
, offset
, whence
);
1273 newpos
=lseek(((MonoFDHandle
*) filehandle
)->fd
, offset
, whence
);
1277 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: lseek on fd %d returned error %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1279 _wapi_set_last_error_from_errno ();
1280 return(INVALID_SET_FILE_POINTER
);
1283 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: lseek returns %" G_GINT64_FORMAT
, __func__
, newpos
);
1285 #ifdef HAVE_LARGE_FILE_SUPPORT
1286 ret
=newpos
& 0xFFFFFFFF;
1287 if(highmovedistance
!=NULL
) {
1288 *highmovedistance
=newpos
>>32;
1292 if(highmovedistance
!=NULL
) {
1293 /* Accurate, but potentially dodgy :-) */
1294 *highmovedistance
=0;
1298 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: move of fd %d returning %" G_GUINT32_FORMAT
"/%" G_GINT32_FORMAT
, __func__
, ((MonoFDHandle
*) filehandle
)->fd
, ret
, highmovedistance
==NULL
?0:*highmovedistance
);
1303 static gboolean
file_setendoffile(FileHandle
*filehandle
)
1305 struct stat statbuf
;
1308 MonoThreadInfo
*info
= mono_thread_info_current ();
1310 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1311 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1313 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1317 /* Find the current file position, and the file length. If
1318 * the file position is greater than the length, write to
1319 * extend the file with a hole. If the file position is less
1320 * than the length, truncate the file.
1324 ret
=fstat(((MonoFDHandle
*) filehandle
)->fd
, &statbuf
);
1327 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d fstat failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1329 _wapi_set_last_error_from_errno ();
1334 pos
=lseek(((MonoFDHandle
*) filehandle
)->fd
, (off_t
)0, SEEK_CUR
);
1337 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d lseek failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1339 _wapi_set_last_error_from_errno ();
1343 #ifdef FTRUNCATE_DOESNT_EXTEND
1344 off_t size
= statbuf
.st_size
;
1345 /* I haven't bothered to write the configure.ac stuff for this
1346 * because I don't know if any platform needs it. I'm leaving
1347 * this code just in case though
1350 /* Extend the file. Use write() here, because some
1351 * manuals say that ftruncate() behaviour is undefined
1352 * when the file needs extending. The POSIX spec says
1353 * that on XSI-conformant systems it extends, so if
1354 * every system we care about conforms, then we can
1359 ret
= write (((MonoFDHandle
*) filehandle
)->fd
, "", 1);
1361 } while (ret
== -1 && errno
== EINTR
&&
1362 !mono_thread_info_is_interrupt_state (info
));
1365 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d extend write failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1367 _wapi_set_last_error_from_errno ();
1371 /* And put the file position back after the write */
1373 ret
= lseek (((MonoFDHandle
*) filehandle
)->fd
, pos
, SEEK_SET
);
1376 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d second lseek failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1378 _wapi_set_last_error_from_errno ();
1384 /* always truncate, because the extend write() adds an extra
1385 * byte to the end of the file
1389 ret
=ftruncate(((MonoFDHandle
*) filehandle
)->fd
, pos
);
1392 while (ret
==-1 && errno
==EINTR
&& !mono_thread_info_is_interrupt_state (info
));
1394 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d ftruncate failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1396 _wapi_set_last_error_from_errno ();
1403 static guint32
file_getfilesize(FileHandle
*filehandle
, guint32
*highsize
)
1405 struct stat statbuf
;
1409 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_ALL
))) {
1410 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1412 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1413 return(INVALID_FILE_SIZE
);
1416 /* If the file has a size with the low bits 0xFFFFFFFF the
1417 * caller can't tell if this is an error, so clear the error
1420 mono_w32error_set_last (ERROR_SUCCESS
);
1423 ret
= fstat(((MonoFDHandle
*) filehandle
)->fd
, &statbuf
);
1426 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d fstat failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1428 _wapi_set_last_error_from_errno ();
1429 return(INVALID_FILE_SIZE
);
1432 /* fstat indicates block devices as zero-length, so go a different path */
1434 if (S_ISBLK(statbuf
.st_mode
)) {
1438 res
= ioctl (((MonoFDHandle
*) filehandle
)->fd
, BLKGETSIZE64
, &bigsize
);
1441 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d ioctl BLKGETSIZE64 failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1443 _wapi_set_last_error_from_errno ();
1444 return(INVALID_FILE_SIZE
);
1447 size
= bigsize
& 0xFFFFFFFF;
1448 if (highsize
!= NULL
) {
1449 *highsize
= bigsize
>>32;
1452 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Returning block device size %" G_GUINT32_FORMAT
"/%" G_GUINT32_FORMAT
,
1453 __func__
, size
, *highsize
);
1459 #ifdef HAVE_LARGE_FILE_SUPPORT
1460 size
= statbuf
.st_size
& 0xFFFFFFFF;
1461 if (highsize
!= NULL
) {
1462 *highsize
= statbuf
.st_size
>>32;
1465 if (highsize
!= NULL
) {
1466 /* Accurate, but potentially dodgy :-) */
1469 size
= statbuf
.st_size
;
1472 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Returning size %" G_GUINT32_FORMAT
"/%" G_GUINT32_FORMAT
, __func__
, size
, *highsize
);
1478 convert_unix_filetime_ms (const FILETIME
*file_time
, const char *ttype
)
1480 guint64 t
= ((guint64
) file_time
->dwHighDateTime
<< 32) + file_time
->dwLowDateTime
;
1482 /* This is (time_t)0. We can actually go to INT_MIN,
1483 * but this will do for now.
1485 if (t
< CONVERT_BASE
) {
1486 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: attempt to set %s time too early", __func__
, ttype
);
1487 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1491 return t
- CONVERT_BASE
;
1494 #ifndef HAVE_STRUCT_TIMEVAL
1496 convert_unix_filetime (const FILETIME
*file_time
, size_t field_size
, const char *ttype
)
1498 guint64 t
= convert_unix_filetime_ms (file_time
, ttype
);
1500 if (field_size
== 4 && (t
/ 10000000) > INT_MAX
) {
1501 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: attempt to set %s time that is too big for a 32bits time_t", __func__
, ttype
);
1502 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1506 return t
/ 10000000;
1510 static void convert_stattime_access_to_timeval (struct timeval
*dest
, struct stat
*statbuf
)
1512 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
1513 dest
->tv_sec
= statbuf
->st_atimespec
.tv_sec
;
1514 dest
->tv_usec
= statbuf
->st_atimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
;
1516 dest
->tv_sec
= statbuf
->st_atime
;
1517 #if HAVE_STRUCT_STAT_ST_ATIM
1518 dest
->tv_usec
= statbuf
->st_atim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
;
1523 static void convert_stattime_mod_to_timeval (struct timeval
*dest
, struct stat
*statbuf
)
1525 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
1526 dest
->tv_sec
= statbuf
->st_mtimespec
.tv_sec
;
1527 dest
->tv_usec
= statbuf
->st_mtimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
;
1529 dest
->tv_sec
= statbuf
->st_mtime
;
1530 #if HAVE_STRUCT_STAT_ST_ATIM
1531 dest
->tv_usec
= statbuf
->st_mtim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
;
1536 static gboolean
file_setfiletime(FileHandle
*filehandle
,
1537 const FILETIME
*create_time G_GNUC_UNUSED
,
1538 const FILETIME
*access_time
,
1539 const FILETIME
*write_time
)
1541 struct stat statbuf
;
1544 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1545 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1547 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1551 if(filehandle
->filename
== NULL
) {
1552 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d unknown filename", __func__
, ((MonoFDHandle
*) filehandle
)->fd
);
1554 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
1558 /* Get the current times, so we can put the same times back in
1559 * the event that one of the FileTime structs is NULL
1562 ret
=fstat (((MonoFDHandle
*) filehandle
)->fd
, &statbuf
);
1565 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d fstat failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1567 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1571 #ifdef HAVE_STRUCT_TIMEVAL
1572 struct timeval times
[2];
1573 memset (times
, 0, sizeof (times
));
1576 guint64 actime
= convert_unix_filetime_ms (access_time
, "access");
1577 times
[0].tv_sec
= actime
/ TICKS_PER_SECOND
;
1578 times
[0].tv_usec
= (actime
% TICKS_PER_SECOND
) / TICKS_PER_MICROSECOND
;
1580 convert_stattime_access_to_timeval (×
[0], &statbuf
);
1584 guint64 wtime
= convert_unix_filetime_ms (write_time
, "write");
1585 times
[1].tv_sec
= wtime
/ TICKS_PER_SECOND
;
1586 times
[1].tv_usec
= (wtime
% TICKS_PER_SECOND
) / TICKS_PER_MICROSECOND
;
1588 convert_stattime_mod_to_timeval (×
[1], &statbuf
);
1591 ret
= _wapi_utimes (filehandle
->filename
, times
);
1593 struct utimbuf utbuf
;
1594 utbuf
.actime
= access_time
? convert_unix_filetime (access_time
, sizeof (utbuf
.actime
), "access") : statbuf
.st_atime
;
1595 utbuf
.modtime
= write_time
? convert_unix_filetime (write_time
, sizeof (utbuf
.modtime
), "write") : statbuf
.st_mtime
;
1597 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: setting fd %d access %ld write %ld", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, utbuf
.actime
, utbuf
.modtime
);
1599 ret
= _wapi_utime (filehandle
->filename
, &utbuf
);
1602 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d [%s] utime failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->filename
, g_strerror(errno
));
1604 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1612 console_read(FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
)
1615 MonoThreadInfo
*info
= mono_thread_info_current ();
1617 if(bytesread
!=NULL
) {
1621 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_ALL
))) {
1622 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1624 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1630 ret
=read(((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1632 } while (ret
==-1 && errno
==EINTR
&& !mono_thread_info_is_interrupt_state (info
));
1635 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: read of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1637 _wapi_set_last_error_from_errno ();
1641 if(bytesread
!=NULL
) {
1649 console_write (FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*byteswritten
)
1652 MonoThreadInfo
*info
= mono_thread_info_current ();
1654 if(byteswritten
!=NULL
) {
1658 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1659 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1661 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1667 ret
= write(((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1669 } while (ret
== -1 && errno
== EINTR
&&
1670 !mono_thread_info_is_interrupt_state (info
));
1673 if (errno
== EINTR
) {
1676 _wapi_set_last_error_from_errno ();
1678 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: write of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1683 if(byteswritten
!=NULL
) {
1691 pipe_read (FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
)
1694 MonoThreadInfo
*info
= mono_thread_info_current ();
1696 if(bytesread
!=NULL
) {
1700 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_ALL
))) {
1701 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1703 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1707 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: reading up to %" G_GUINT32_FORMAT
" bytes from pipe %d", __func__
, numbytes
, ((MonoFDHandle
*) filehandle
)->fd
);
1711 ret
=read(((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1713 } while (ret
==-1 && errno
==EINTR
&& !mono_thread_info_is_interrupt_state (info
));
1716 if (errno
== EINTR
) {
1719 _wapi_set_last_error_from_errno ();
1721 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: read of fd %d error: %s", __func__
,((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1727 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: read %d bytes from pipe %d", __func__
, ret
, ((MonoFDHandle
*) filehandle
)->fd
);
1729 if(bytesread
!=NULL
) {
1737 pipe_write (FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*byteswritten
)
1740 MonoThreadInfo
*info
= mono_thread_info_current ();
1742 if(byteswritten
!=NULL
) {
1746 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1747 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1749 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1753 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: writing up to %" G_GUINT32_FORMAT
" bytes to pipe %d", __func__
, numbytes
, ((MonoFDHandle
*) filehandle
)->fd
);
1757 ret
= write (((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1759 } while (ret
== -1 && errno
== EINTR
&&
1760 !mono_thread_info_is_interrupt_state (info
));
1763 if (errno
== EINTR
) {
1766 _wapi_set_last_error_from_errno ();
1768 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: write of fd %d error: %s", __func__
,((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1773 if(byteswritten
!=NULL
) {
1780 static gint
convert_flags(guint32 fileaccess
, guint32 createmode
)
1784 switch(fileaccess
) {
1791 case GENERIC_READ
|GENERIC_WRITE
:
1795 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Unknown access type 0x%" PRIx32
, __func__
,
1800 switch(createmode
) {
1802 flags
|=O_CREAT
|O_EXCL
;
1805 flags
|=O_CREAT
|O_TRUNC
;
1812 case TRUNCATE_EXISTING
:
1816 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Unknown create mode 0x%" PRIx32
, __func__
,
1825 static mode_t
convert_perms(guint32 sharemode
)
1829 if(sharemode
&FILE_SHARE_READ
) {
1832 if(sharemode
&FILE_SHARE_WRITE
) {
1840 static gboolean
share_allows_open (struct stat
*statbuf
, guint32 sharemode
,
1842 FileShare
**share_info
)
1844 gboolean file_already_shared
;
1845 guint32 file_existing_share
, file_existing_access
;
1847 file_already_shared
= file_share_get (statbuf
->st_dev
, statbuf
->st_ino
, sharemode
, fileaccess
, &file_existing_share
, &file_existing_access
, share_info
);
1849 if (file_already_shared
) {
1850 /* The reference to this share info was incremented
1851 * when we looked it up, so be careful to put it back
1852 * if we conclude we can't use this file.
1854 if ((file_existing_share
== FILE_SHARE_NONE
) || (sharemode
== FILE_SHARE_NONE
)) {
1855 /* Quick and easy, no possibility to share */
1856 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Share mode prevents open: requested access: 0x%" PRIx32
", file has sharing = NONE", __func__
, fileaccess
);
1858 file_share_release (*share_info
);
1864 if (((file_existing_share
== FILE_SHARE_READ
) &&
1865 (fileaccess
!= GENERIC_READ
)) ||
1866 ((file_existing_share
== FILE_SHARE_WRITE
) &&
1867 (fileaccess
!= GENERIC_WRITE
))) {
1868 /* New access mode doesn't match up */
1869 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Share mode prevents open: requested access: 0x%" PRIx32
", file has sharing: 0x%" PRIx32
, __func__
, fileaccess
, file_existing_share
);
1871 file_share_release (*share_info
);
1877 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: New file!", __func__
);
1885 share_allows_delete (struct stat
*statbuf
, FileShare
**share_info
)
1887 gboolean file_already_shared
;
1888 guint32 file_existing_share
, file_existing_access
;
1890 file_already_shared
= file_share_get (statbuf
->st_dev
, statbuf
->st_ino
, FILE_SHARE_DELETE
, GENERIC_READ
, &file_existing_share
, &file_existing_access
, share_info
);
1892 if (file_already_shared
) {
1893 /* The reference to this share info was incremented
1894 * when we looked it up, so be careful to put it back
1895 * if we conclude we can't use this file.
1897 if (file_existing_share
== 0) {
1898 /* Quick and easy, no possibility to share */
1899 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Share mode prevents open: requested access: 0x%" PRIx32
", file has sharing = NONE", __func__
, (*share_info
)->access
);
1901 file_share_release (*share_info
);
1907 if (!(file_existing_share
& FILE_SHARE_DELETE
)) {
1908 /* New access mode doesn't match up */
1909 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Share mode prevents open: requested access: 0x%" PRIx32
", file has sharing: 0x%" PRIx32
, __func__
, (*share_info
)->access
, file_existing_share
);
1911 file_share_release (*share_info
);
1917 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: New file!", __func__
);
1924 mono_w32file_create(const gunichar2
*name
, guint32 fileaccess
, guint32 sharemode
, guint32 createmode
, guint32 attrs
)
1926 FileHandle
*filehandle
;
1928 gint flags
=convert_flags(fileaccess
, createmode
);
1929 /*mode_t perms=convert_perms(sharemode);*/
1930 /* we don't use sharemode, because that relates to sharing of
1931 * the file when the file is open and is already handled by
1932 * other code, perms instead are the on-disk permissions and
1933 * this is a sane default.
1938 struct stat statbuf
;
1941 if (attrs
& FILE_ATTRIBUTE_TEMPORARY
)
1944 if (attrs
& FILE_ATTRIBUTE_ENCRYPTED
){
1945 mono_w32error_set_last (ERROR_ENCRYPTION_FAILED
);
1946 return INVALID_HANDLE_VALUE
;
1950 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
1952 mono_w32error_set_last (ERROR_INVALID_NAME
);
1953 return(INVALID_HANDLE_VALUE
);
1956 filename
= mono_unicode_to_external_checked (name
, error
);
1957 if (filename
== NULL
) {
1958 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
1960 mono_error_cleanup (error
);
1961 mono_w32error_set_last (ERROR_INVALID_NAME
);
1962 return(INVALID_HANDLE_VALUE
);
1965 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Opening %s with share 0x%" PRIx32
" and access 0x%" PRIx32
, __func__
,
1966 filename
, sharemode
, fileaccess
);
1968 fd
= _wapi_open (filename
, flags
, perms
);
1970 /* If we were trying to open a directory with write permissions
1971 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1972 * EISDIR. However, this is a bit bogus because calls to
1973 * manipulate the directory (e.g. mono_w32file_set_times) will still work on
1974 * the directory because they use other API calls
1975 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1976 * to open the directory again without write permission.
1978 if (fd
== -1 && errno
== EISDIR
)
1980 /* Try again but don't try to make it writable */
1981 fd
= _wapi_open (filename
, flags
& ~(O_RDWR
|O_WRONLY
), perms
);
1985 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Error opening file %s: %s", __func__
, filename
, g_strerror(errno
));
1986 _wapi_set_last_path_error_from_errno (NULL
, filename
);
1989 return(INVALID_HANDLE_VALUE
);
1993 ret
= fstat (fd
, &statbuf
);
1996 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fstat error of file %s: %s", __func__
, filename
, g_strerror (errno
));
1997 _wapi_set_last_error_from_errno ();
2002 return(INVALID_HANDLE_VALUE
);
2006 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
2008 if (S_ISFIFO (statbuf
.st_mode
)) {
2009 type
= MONO_FDTYPE_PIPE
;
2010 /* maintain invariant that pipes have no filename */
2013 } else if (S_ISCHR (statbuf
.st_mode
)) {
2014 type
= MONO_FDTYPE_CONSOLE
;
2016 type
= MONO_FDTYPE_FILE
;
2019 filehandle
= file_data_create (type
, fd
);
2020 filehandle
->filename
= filename
;
2021 filehandle
->fileaccess
= fileaccess
;
2022 filehandle
->sharemode
= sharemode
;
2023 filehandle
->attrs
= attrs
;
2025 if (!share_allows_open (&statbuf
, filehandle
->sharemode
, filehandle
->fileaccess
, &filehandle
->share_info
)) {
2026 mono_w32error_set_last (ERROR_SHARING_VIOLATION
);
2028 close (((MonoFDHandle
*) filehandle
)->fd
);
2031 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2032 return (INVALID_HANDLE_VALUE
);
2034 if (!filehandle
->share_info
) {
2035 /* No space, so no more files can be opened */
2036 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: No space in the share table", __func__
);
2038 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES
);
2040 close (((MonoFDHandle
*) filehandle
)->fd
);
2043 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2044 return(INVALID_HANDLE_VALUE
);
2047 #ifdef HAVE_POSIX_FADVISE
2048 if (attrs
& FILE_FLAG_SEQUENTIAL_SCAN
) {
2050 posix_fadvise (((MonoFDHandle
*) filehandle
)->fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
2053 if (attrs
& FILE_FLAG_RANDOM_ACCESS
) {
2055 posix_fadvise (((MonoFDHandle
*) filehandle
)->fd
, 0, 0, POSIX_FADV_RANDOM
);
2061 if (attrs
& FILE_FLAG_SEQUENTIAL_SCAN
) {
2063 fcntl(((MonoFDHandle
*) filehandle
)->fd
, F_RDAHEAD
, 1);
2068 mono_fdhandle_insert ((MonoFDHandle
*) filehandle
);
2070 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: returning handle %p", __func__
, GINT_TO_POINTER(((MonoFDHandle
*) filehandle
)->fd
));
2072 return GINT_TO_POINTER(((MonoFDHandle
*) filehandle
)->fd
);
2076 mono_w32file_cancel (gpointer handle
)
2078 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2083 mono_w32file_close (gpointer handle
)
2085 if (!mono_fdhandle_close (GPOINTER_TO_INT (handle
))) {
2086 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2093 gboolean
mono_w32file_delete(const gunichar2
*name
)
2097 gboolean ret
= FALSE
;
2100 struct stat statbuf
;
2101 FileShare
*shareinfo
;
2105 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
2107 mono_w32error_set_last (ERROR_INVALID_NAME
);
2111 filename
= mono_unicode_to_external_checked (name
, error
);
2112 if(filename
==NULL
) {
2113 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
2115 mono_error_cleanup (error
);
2116 mono_w32error_set_last (ERROR_INVALID_NAME
);
2121 /* Check to make sure sharing allows us to open the file for
2122 * writing. See bug 323389.
2124 * Do the checks that don't need an open file descriptor, for
2125 * simplicity's sake. If we really have to do the full checks
2126 * then we can implement that later.
2128 if (_wapi_stat (filename
, &statbuf
) < 0) {
2129 _wapi_set_last_path_error_from_errno (NULL
, filename
);
2134 if (share_allows_open (&statbuf
, 0, GENERIC_WRITE
,
2135 &shareinfo
) == FALSE
) {
2136 mono_w32error_set_last (ERROR_SHARING_VIOLATION
);
2141 file_share_release (shareinfo
);
2144 retval
= _wapi_unlink (filename
);
2147 /* On linux, calling unlink on an non-existing file in a read-only mount will fail with EROFS.
2148 * The expected behavior is for this function to return FALSE and not trigger an exception.
2149 * To work around this behavior, we stat the file on failure.
2151 * This was supposedly fixed on kernel 3.0 [1] but we could reproduce it with Ubuntu 16.04 which has kernel 4.4.
2152 * We can't remove this workaround until the early 2020's when most Android deviced will have a fix.
2153 * [1] https://github.com/torvalds/linux/commit/50338b889dc504c69e0cb316ac92d1b9e51f3c8a
2155 if (errno
== EROFS
) {
2157 if (mono_w32file_get_attributes_ex (name
, &stat
)) //The file exists, so must be due the RO file system
2158 mono_set_errno (EROFS
);
2160 _wapi_set_last_path_error_from_errno (NULL
, filename
);
2171 MoveFile (const gunichar2
*name
, const gunichar2
*dest_name
)
2173 gchar
*utf8_name
, *utf8_dest_name
;
2174 gint result
, errno_copy
;
2175 struct stat stat_src
, stat_dest
;
2176 gboolean ret
= FALSE
;
2177 FileShare
*shareinfo
;
2181 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
2183 mono_w32error_set_last (ERROR_INVALID_NAME
);
2187 utf8_name
= mono_unicode_to_external_checked (name
, error
);
2188 if (utf8_name
== NULL
) {
2189 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
2191 mono_error_cleanup (error
);
2192 mono_w32error_set_last (ERROR_INVALID_NAME
);
2196 if(dest_name
==NULL
) {
2197 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
2200 mono_w32error_set_last (ERROR_INVALID_NAME
);
2204 utf8_dest_name
= mono_unicode_to_external_checked (dest_name
, error
);
2205 if (utf8_dest_name
== NULL
) {
2206 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
2208 mono_error_cleanup (error
);
2210 mono_w32error_set_last (ERROR_INVALID_NAME
);
2215 * In C# land we check for the existence of src, but not for dest.
2216 * We check it here and return the failure if dest exists and is not
2217 * the same file as src.
2219 if (_wapi_stat (utf8_name
, &stat_src
) < 0) {
2220 if (errno
!= ENOENT
|| _wapi_lstat (utf8_name
, &stat_src
) < 0) {
2221 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
2223 g_free (utf8_dest_name
);
2228 if (!_wapi_stat (utf8_dest_name
, &stat_dest
)) {
2229 if (stat_dest
.st_dev
!= stat_src
.st_dev
||
2230 stat_dest
.st_ino
!= stat_src
.st_ino
) {
2232 g_free (utf8_dest_name
);
2233 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
2238 /* Check to make that we have delete sharing permission.
2239 * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
2241 * Do the checks that don't need an open file descriptor, for
2242 * simplicity's sake. If we really have to do the full checks
2243 * then we can implement that later.
2245 if (share_allows_delete (&stat_src
, &shareinfo
) == FALSE
) {
2246 mono_w32error_set_last (ERROR_SHARING_VIOLATION
);
2250 file_share_release (shareinfo
);
2254 result
= _wapi_rename (utf8_name
, utf8_dest_name
);
2258 switch(errno_copy
) {
2260 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
2264 /* Ignore here, it is dealt with below */
2268 /* We already know src exists. Must be dest that doesn't exist. */
2269 _wapi_set_last_path_error_from_errno (NULL
, utf8_dest_name
);
2273 _wapi_set_last_error_from_errno ();
2278 g_free (utf8_dest_name
);
2280 if (result
!= 0 && errno_copy
== EXDEV
) {
2283 if (S_ISDIR (stat_src
.st_mode
)) {
2284 mono_w32error_set_last (ERROR_NOT_SAME_DEVICE
);
2287 /* Try a copy to the new location, and delete the source */
2288 if (!mono_w32file_copy (name
, dest_name
, FALSE
, ©_error
)) {
2289 /* mono_w32file_copy will set the error */
2293 return(mono_w32file_delete (name
));
2304 write_file (gint src_fd
, gint dest_fd
, struct stat
*st_src
, gboolean report_errors
)
2308 gint buf_size
= st_src
->st_blksize
;
2309 MonoThreadInfo
*info
= mono_thread_info_current ();
2311 buf_size
= buf_size
< 8192 ? 8192 : (buf_size
> 65536 ? 65536 : buf_size
);
2312 buf
= (gchar
*) g_malloc (buf_size
);
2316 remain
= read (src_fd
, buf
, buf_size
);
2319 if (errno
== EINTR
&& !mono_thread_info_is_interrupt_state (info
))
2323 _wapi_set_last_error_from_errno ();
2333 while (remain
> 0) {
2335 n
= write (dest_fd
, wbuf
, remain
);
2338 if (errno
== EINTR
&& !mono_thread_info_is_interrupt_state (info
))
2342 _wapi_set_last_error_from_errno ();
2343 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: write failed.", __func__
);
2359 _wapi_clonefile(const char *from
, const char *to
, int flags
)
2361 gchar
*located_from
, *located_to
;
2364 g_assert (clonefile_ptr
!= NULL
);
2366 located_from
= mono_portability_find_file (from
, FALSE
);
2367 located_to
= mono_portability_find_file (to
, FALSE
);
2370 ret
= clonefile_ptr (
2371 located_from
== NULL
? from
: located_from
,
2372 located_to
== NULL
? to
: located_to
,
2376 g_free (located_from
);
2377 g_free (located_to
);
2384 CopyFile (const gunichar2
*name
, const gunichar2
*dest_name
, gboolean fail_if_exists
)
2386 gchar
*utf8_src
, *utf8_dest
;
2387 struct stat st
, dest_st
;
2388 gboolean ret
= TRUE
;
2392 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
2394 mono_w32error_set_last (ERROR_INVALID_NAME
);
2398 utf8_src
= mono_unicode_to_external_checked (name
, error
);
2399 if (utf8_src
== NULL
) {
2400 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion of source returned NULL; %s",
2401 __func__
, mono_error_get_message (error
));
2403 mono_error_cleanup (error
);
2404 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2408 if(dest_name
==NULL
) {
2409 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: dest is NULL", __func__
);
2412 mono_w32error_set_last (ERROR_INVALID_NAME
);
2416 utf8_dest
= mono_unicode_to_external_checked (dest_name
, error
);
2417 if (utf8_dest
== NULL
) {
2418 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion of dest returned NULL; %s",
2419 __func__
, mono_error_get_message (error
));
2421 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2423 mono_error_cleanup (error
);
2429 gint src_fd
, dest_fd
;
2433 src_fd
= _wapi_open (utf8_src
, O_RDONLY
, 0);
2435 _wapi_set_last_path_error_from_errno (NULL
, utf8_src
);
2444 syscall_res
= fstat (src_fd
, &st
);
2446 if (syscall_res
< 0) {
2447 _wapi_set_last_error_from_errno ();
2458 if (!_wapi_stat (utf8_dest
, &dest_st
)) {
2459 /* Before trying to open/create the dest, we need to report a 'file busy'
2460 * error if src and dest are actually the same file. We do the check here to take
2461 * advantage of the IOMAP capability */
2462 if (st
.st_dev
== dest_st
.st_dev
&& st
.st_ino
== dest_st
.st_ino
) {
2469 mono_w32error_set_last (ERROR_SHARING_VIOLATION
);
2473 /* Take advantage of the fact that we already know the file exists and bail out
2475 if (fail_if_exists
) {
2482 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
2487 /* If we attempt to use clonefile API we need to unlink the destination file
2489 if (clonefile_ptr
!= NULL
) {
2491 /* Bail out if the destination is read-only */
2492 if (!is_file_writable (&dest_st
, utf8_dest
)) {
2499 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2503 _wapi_unlink (utf8_dest
);
2509 if (clonefile_ptr
!= NULL
) {
2510 ret
= _wapi_clonefile (utf8_src
, utf8_dest
, 0);
2511 if (ret
== 0 || (errno
!= ENOTSUP
&& errno
!= EXDEV
)) {
2521 _wapi_set_last_error_from_errno ();
2528 if (fail_if_exists
) {
2529 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_CREAT
| O_EXCL
, st
.st_mode
);
2531 /* FIXME: it's bad that this code path potentially scans
2532 * the directory twice due to the weird mono_w32error_set_last()
2534 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_TRUNC
, st
.st_mode
);
2536 /* The file does not exist, try creating it */
2537 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_CREAT
| O_TRUNC
, st
.st_mode
);
2539 /* Apparently this error is set if we
2540 * overwrite the dest file
2542 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
2546 _wapi_set_last_error_from_errno ();
2557 if (!write_file (src_fd
, dest_fd
, &st
, TRUE
))
2563 #ifdef HAVE_STRUCT_TIMEVAL
2564 struct timeval times
[2];
2565 memset (times
, 0, sizeof (times
));
2567 convert_stattime_access_to_timeval (×
[0], &st
);
2568 convert_stattime_mod_to_timeval (×
[1], &st
);
2570 ret_utime
= _wapi_utimes (utf8_dest
, times
);
2572 struct utimbuf dest_time
;
2573 dest_time
.modtime
= st
.st_mtime
;
2574 dest_time
.actime
= st
.st_atime
;
2576 ret_utime
= _wapi_utime (utf8_dest
, &dest_time
);
2578 if (ret_utime
== -1)
2579 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: file [%s] utime failed: %s", __func__
, utf8_dest
, g_strerror(errno
));
2588 convert_arg_to_utf8 (const gunichar2
*arg
, const gchar
*arg_name
)
2594 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: %s is NULL", __func__
, arg_name
);
2595 mono_w32error_set_last (ERROR_INVALID_NAME
);
2599 utf8_ret
= mono_unicode_to_external_checked (arg
, error
);
2600 if (utf8_ret
== NULL
) {
2601 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion of %s returned NULL; %s",
2602 __func__
, arg_name
, mono_error_get_message (error
));
2604 mono_error_cleanup (error
);
2605 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2613 ReplaceFile (const gunichar2
*replacedFileName
, const gunichar2
*replacementFileName
, const gunichar2
*backupFileName
, guint32 replaceFlags
, gpointer exclude
, gpointer reserved
)
2615 gint result
, backup_fd
= -1,replaced_fd
= -1;
2616 gchar
*utf8_replacedFileName
, *utf8_replacementFileName
= NULL
, *utf8_backupFileName
= NULL
;
2617 struct stat stBackup
;
2618 gboolean ret
= FALSE
;
2620 if (!(utf8_replacedFileName
= convert_arg_to_utf8 (replacedFileName
, "replacedFileName")))
2622 if (!(utf8_replacementFileName
= convert_arg_to_utf8 (replacementFileName
, "replacementFileName")))
2623 goto replace_cleanup
;
2624 if (backupFileName
!= NULL
) {
2625 if (!(utf8_backupFileName
= convert_arg_to_utf8 (backupFileName
, "backupFileName")))
2626 goto replace_cleanup
;
2629 if (utf8_backupFileName
) {
2630 // Open the backup file for read so we can restore the file if an error occurs.
2631 backup_fd
= _wapi_open (utf8_backupFileName
, O_RDONLY
, 0);
2632 result
= _wapi_rename (utf8_replacedFileName
, utf8_backupFileName
);
2634 goto replace_cleanup
;
2637 result
= _wapi_rename (utf8_replacementFileName
, utf8_replacedFileName
);
2639 _wapi_set_last_path_error_from_errno (NULL
, utf8_replacementFileName
);
2640 _wapi_rename (utf8_backupFileName
, utf8_replacedFileName
);
2641 if (backup_fd
!= -1 && !fstat (backup_fd
, &stBackup
)) {
2642 replaced_fd
= _wapi_open (utf8_backupFileName
, O_WRONLY
| O_CREAT
| O_TRUNC
,
2645 if (replaced_fd
== -1)
2646 goto replace_cleanup
;
2648 write_file (backup_fd
, replaced_fd
, &stBackup
, FALSE
);
2651 goto replace_cleanup
;
2657 g_free (utf8_replacedFileName
);
2658 g_free (utf8_replacementFileName
);
2659 g_free (utf8_backupFileName
);
2660 if (backup_fd
!= -1) {
2665 if (replaced_fd
!= -1) {
2667 close (replaced_fd
);
2674 _wapi_stdhandle_create (gint fd
, const gchar
*name
)
2677 FileHandle
*filehandle
;
2679 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: creating standard handle type %s, fd %d", __func__
, name
, fd
);
2681 /* Check if fd is valid */
2683 flags
= fcntl(fd
, F_GETFL
);
2684 } while (flags
== -1 && errno
== EINTR
);
2687 /* Invalid fd. Not really much point checking for EBADF
2690 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fcntl error on fd %d: %s", __func__
, fd
, g_strerror(errno
));
2692 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2693 return INVALID_HANDLE_VALUE
;
2696 filehandle
= file_data_create (MONO_FDTYPE_CONSOLE
, fd
);
2697 filehandle
->filename
= g_strdup(name
);
2699 switch (flags
& (O_RDONLY
|O_WRONLY
|O_RDWR
)) {
2701 filehandle
->fileaccess
= GENERIC_READ
;
2704 filehandle
->fileaccess
= GENERIC_WRITE
;
2707 filehandle
->fileaccess
= GENERIC_READ
| GENERIC_WRITE
;
2710 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Can't figure out flags 0x%x", __func__
, flags
);
2711 filehandle
->fileaccess
= 0;
2715 /* some default security attributes might be needed */
2716 filehandle
->security_attributes
= 0;
2718 /* Apparently input handles can't be written to. (I don't
2719 * know if output or error handles can't be read from.)
2722 filehandle
->fileaccess
&= ~GENERIC_WRITE
;
2724 filehandle
->sharemode
= 0;
2725 filehandle
->attrs
= 0;
2727 if (!mono_fdhandle_try_insert ((MonoFDHandle
*) filehandle
)) {
2728 /* we raced between 2 invocations of _wapi_stdhandle_create */
2729 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2730 return GINT_TO_POINTER(fd
);
2733 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: returning handle %p", __func__
, GINT_TO_POINTER(((MonoFDHandle
*) filehandle
)->fd
));
2735 return GINT_TO_POINTER(((MonoFDHandle
*) filehandle
)->fd
);
2739 STD_INPUT_HANDLE
= -10,
2740 STD_OUTPUT_HANDLE
= -11,
2741 STD_ERROR_HANDLE
= -12,
2745 mono_w32file_get_std_handle (gint stdhandle
)
2747 FileHandle
**filehandle
;
2752 case STD_INPUT_HANDLE
:
2757 case STD_OUTPUT_HANDLE
:
2762 case STD_ERROR_HANDLE
:
2768 g_assert_not_reached ();
2771 if (!mono_fdhandle_lookup_and_ref(fd
, (MonoFDHandle
**) &filehandle
)) {
2774 handle
= _wapi_stdhandle_create (fd
, name
);
2775 if (handle
== INVALID_HANDLE_VALUE
) {
2776 mono_w32error_set_last (ERROR_NO_MORE_FILES
);
2777 return INVALID_HANDLE_VALUE
;
2781 return GINT_TO_POINTER (fd
);
2785 mono_w32file_read_or_write (gboolean read
, gpointer handle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
, gint32
*win32error
)
2787 MONO_REQ_GC_UNSAFE_MODE
;
2789 FileHandle
*filehandle
;
2790 gboolean ret
= FALSE
;
2792 gboolean
const ref
= mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
);
2794 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2798 switch (((MonoFDHandle
*) filehandle
)->type
) {
2799 case MONO_FDTYPE_FILE
:
2800 ret
= (read
? file_read
: file_write
) (filehandle
, buffer
, numbytes
, bytesread
);
2802 case MONO_FDTYPE_CONSOLE
:
2803 ret
= (read
? console_read
: console_write
) (filehandle
, buffer
, numbytes
, bytesread
);
2805 case MONO_FDTYPE_PIPE
:
2806 ret
= (read
? pipe_read
: pipe_write
) (filehandle
, buffer
, numbytes
, bytesread
);
2809 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2815 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2817 *win32error
= mono_w32error_get_last ();
2822 mono_w32file_read (gpointer handle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
, gint32
*win32error
)
2824 return mono_w32file_read_or_write (TRUE
, handle
, buffer
, numbytes
, bytesread
, win32error
);
2828 mono_w32file_write (gpointer handle
, gconstpointer buffer
, guint32 numbytes
, guint32
*byteswritten
, gint32
*win32error
)
2830 return mono_w32file_read_or_write (FALSE
, handle
, (gpointer
)buffer
, numbytes
, byteswritten
, win32error
);
2834 mono_w32file_flush (gpointer handle
)
2836 FileHandle
*filehandle
;
2839 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2840 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2844 switch (((MonoFDHandle
*) filehandle
)->type
) {
2845 case MONO_FDTYPE_FILE
:
2846 ret
= file_flush(filehandle
);
2849 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2850 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2854 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2859 mono_w32file_truncate (gpointer handle
)
2861 FileHandle
*filehandle
;
2864 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2865 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2869 switch (((MonoFDHandle
*) filehandle
)->type
) {
2870 case MONO_FDTYPE_FILE
:
2871 ret
= file_setendoffile(filehandle
);
2874 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2875 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2879 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2884 mono_w32file_seek (gpointer handle
, gint32 movedistance
, gint32
*highmovedistance
, guint32 method
)
2886 FileHandle
*filehandle
;
2889 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2890 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2891 return INVALID_SET_FILE_POINTER
;
2894 switch (((MonoFDHandle
*) filehandle
)->type
) {
2895 case MONO_FDTYPE_FILE
:
2896 ret
= file_seek(filehandle
, movedistance
, highmovedistance
, method
);
2899 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2900 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2901 return INVALID_SET_FILE_POINTER
;
2904 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2909 mono_w32file_get_type(gpointer handle
)
2911 FileHandle
*filehandle
;
2914 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2915 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2916 return FILE_TYPE_UNKNOWN
;
2919 switch (((MonoFDHandle
*) filehandle
)->type
) {
2920 case MONO_FDTYPE_FILE
:
2921 ret
= FILE_TYPE_DISK
;
2923 case MONO_FDTYPE_CONSOLE
:
2924 ret
= FILE_TYPE_CHAR
;
2926 case MONO_FDTYPE_PIPE
:
2927 ret
= FILE_TYPE_PIPE
;
2930 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2931 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2932 return FILE_TYPE_UNKNOWN
;
2935 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2940 GetFileSize(gpointer handle
, guint32
*highsize
)
2942 FileHandle
*filehandle
;
2945 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2946 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2947 return INVALID_FILE_SIZE
;
2950 switch (((MonoFDHandle
*) filehandle
)->type
) {
2951 case MONO_FDTYPE_FILE
:
2952 ret
= file_getfilesize(filehandle
, highsize
);
2955 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2956 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2957 return INVALID_FILE_SIZE
;
2960 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2965 mono_w32file_set_times(gpointer handle
, const FILETIME
*create_time
, const FILETIME
*access_time
, const FILETIME
*write_time
)
2967 FileHandle
*filehandle
;
2970 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2971 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2975 switch (((MonoFDHandle
*) filehandle
)->type
) {
2976 case MONO_FDTYPE_FILE
:
2977 ret
= file_setfiletime(filehandle
, create_time
, access_time
, write_time
);
2980 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2981 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2985 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2989 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2990 * January 1 1601 GMT
2993 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2995 static const guint16 mon_yday
[2][13]={
2996 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2997 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
3001 mono_w32file_filetime_to_systemtime(const FILETIME
*file_time
, SYSTEMTIME
*system_time
)
3003 gint64 file_ticks
, totaldays
, rem
, y
;
3006 if(system_time
==NULL
) {
3007 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: system_time NULL", __func__
);
3009 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3013 file_ticks
=((gint64
)file_time
->dwHighDateTime
<< 32) +
3014 file_time
->dwLowDateTime
;
3016 /* Really compares if file_ticks>=0x8000000000000000
3017 * (LLONG_MAX+1) but we're working with a signed value for the
3018 * year and day calculation to work later
3021 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: file_time too big", __func__
);
3023 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3027 totaldays
=(file_ticks
/ TICKS_PER_DAY
);
3028 rem
= file_ticks
% TICKS_PER_DAY
;
3029 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: totaldays: %" G_GINT64_FORMAT
" rem: %" G_GINT64_FORMAT
, __func__
,
3032 system_time
->wHour
=rem
/TICKS_PER_HOUR
;
3033 rem
%= TICKS_PER_HOUR
;
3034 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Hour: %d rem: %" G_GINT64_FORMAT
, __func__
,
3035 system_time
->wHour
, rem
);
3037 system_time
->wMinute
= rem
/ TICKS_PER_MINUTE
;
3038 rem
%= TICKS_PER_MINUTE
;
3039 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Minute: %d rem: %" G_GINT64_FORMAT
, __func__
,
3040 system_time
->wMinute
, rem
);
3042 system_time
->wSecond
= rem
/ TICKS_PER_SECOND
;
3043 rem
%= TICKS_PER_SECOND
;
3044 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Second: %d rem: %" G_GINT64_FORMAT
, __func__
,
3045 system_time
->wSecond
, rem
);
3047 system_time
->wMilliseconds
= rem
/ TICKS_PER_MILLISECOND
;
3048 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Milliseconds: %d", __func__
,
3049 system_time
->wMilliseconds
);
3051 /* January 1, 1601 was a Monday, according to Emacs calendar */
3052 system_time
->wDayOfWeek
= ((1 + totaldays
) % 7) + 1;
3053 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Day of week: %d", __func__
, system_time
->wDayOfWeek
);
3055 /* This algorithm to find year and month given days from epoch
3060 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
3061 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
3063 while(totaldays
< 0 || totaldays
>= (isleap(y
)?366:365)) {
3064 /* Guess a corrected year, assuming 365 days per year */
3065 gint64 yg
= y
+ totaldays
/ 365 - (totaldays
% 365 < 0);
3066 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: totaldays: %" G_GINT64_FORMAT
" yg: %" G_GINT64_FORMAT
" y: %" G_GINT64_FORMAT
, __func__
,
3068 g_message("%s: LEAPS(yg): %li LEAPS(y): %li", __func__
,
3069 LEAPS_THRU_END_OF(yg
-1), LEAPS_THRU_END_OF(y
-1));
3071 /* Adjust days and y to match the guessed year. */
3072 totaldays
-= ((yg
- y
) * 365
3073 + LEAPS_THRU_END_OF (yg
- 1)
3074 - LEAPS_THRU_END_OF (y
- 1));
3075 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: totaldays: %" G_GINT64_FORMAT
,
3076 __func__
, totaldays
);
3078 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: y: %" G_GINT64_FORMAT
, __func__
, y
);
3081 system_time
->wYear
= y
;
3082 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Year: %d", __func__
, system_time
->wYear
);
3084 ip
= mon_yday
[isleap(y
)];
3086 for(y
=11; totaldays
< ip
[y
]; --y
) {
3090 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: totaldays: %" G_GINT64_FORMAT
, __func__
, totaldays
);
3092 system_time
->wMonth
= y
+ 1;
3093 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Month: %d", __func__
, system_time
->wMonth
);
3095 system_time
->wDay
= totaldays
+ 1;
3096 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Day: %d", __func__
, system_time
->wDay
);
3102 findhandle_destroy (gpointer data
)
3104 FindHandle
*findhandle
;
3106 findhandle
= (FindHandle
*) data
;
3107 g_assert (findhandle
);
3109 mono_coop_mutex_destroy (&findhandle
->mutex
);
3111 if (findhandle
->namelist
)
3112 g_strfreev (findhandle
->namelist
);
3113 if (findhandle
->dir_part
)
3114 g_free (findhandle
->dir_part
);
3116 g_free (findhandle
);
3120 findhandle_create (void)
3122 FindHandle
* findhandle
;
3124 findhandle
= g_new0 (FindHandle
, 1);
3125 mono_refcount_init (findhandle
, findhandle_destroy
);
3127 mono_coop_mutex_init (&findhandle
->mutex
);
3133 findhandle_insert (FindHandle
*findhandle
)
3135 mono_coop_mutex_lock (&finds_mutex
);
3137 if (g_hash_table_lookup_extended (finds
, (gpointer
) findhandle
, NULL
, NULL
))
3138 g_error("%s: duplicate Find handle %p", __func__
, (gpointer
) findhandle
);
3140 g_hash_table_insert (finds
, (gpointer
) findhandle
, findhandle
);
3142 mono_coop_mutex_unlock (&finds_mutex
);
3146 findhandle_lookup_and_ref (gpointer handle
, FindHandle
**findhandle
)
3148 mono_coop_mutex_lock (&finds_mutex
);
3150 if (!g_hash_table_lookup_extended (finds
, handle
, NULL
, (gpointer
*) findhandle
)) {
3151 mono_coop_mutex_unlock (&finds_mutex
);
3155 mono_refcount_inc (*findhandle
);
3157 mono_coop_mutex_unlock (&finds_mutex
);
3163 findhandle_unref (FindHandle
*findhandle
)
3165 mono_refcount_dec (findhandle
);
3169 findhandle_close (gpointer handle
)
3171 FindHandle
*findhandle
;
3174 mono_coop_mutex_lock (&finds_mutex
);
3176 if (!g_hash_table_lookup_extended (finds
, handle
, NULL
, (gpointer
*) &findhandle
)) {
3177 mono_coop_mutex_unlock (&finds_mutex
);
3182 removed
= g_hash_table_remove (finds
, (gpointer
) findhandle
);
3185 mono_coop_mutex_unlock (&finds_mutex
);
3191 finds_remove (gpointer data
)
3193 FindHandle
* findhandle
;
3195 findhandle
= (FindHandle
*) data
;
3196 g_assert (findhandle
);
3198 mono_refcount_dec (findhandle
);
3202 mono_w32file_find_first (const gunichar2
*pattern
, WIN32_FIND_DATA
*find_data
)
3204 FindHandle
*findhandle
;
3205 gchar
*utf8_pattern
= NULL
, *dir_part
, *entry_part
, **namelist
;
3209 if (pattern
== NULL
) {
3210 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: pattern is NULL", __func__
);
3212 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
3213 return(INVALID_HANDLE_VALUE
);
3216 utf8_pattern
= mono_unicode_to_external_checked (pattern
, error
);
3217 if (utf8_pattern
== NULL
) {
3218 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
3220 mono_error_cleanup (error
);
3221 mono_w32error_set_last (ERROR_INVALID_NAME
);
3222 return(INVALID_HANDLE_VALUE
);
3225 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: looking for [%s]", __func__
, utf8_pattern
);
3227 /* Figure out which bit of the pattern is the directory */
3228 dir_part
= _wapi_dirname (utf8_pattern
);
3229 entry_part
= _wapi_basename (utf8_pattern
);
3232 /* Don't do this check for now, it breaks if directories
3233 * really do have metachars in their names (see bug 58116).
3234 * FIXME: Figure out a better solution to keep some checks...
3236 if (strchr (dir_part
, '*') || strchr (dir_part
, '?')) {
3237 mono_w32error_set_last (ERROR_INVALID_NAME
);
3239 g_free (entry_part
);
3240 g_free (utf8_pattern
);
3241 return(INVALID_HANDLE_VALUE
);
3245 /* The pattern can specify a directory or a set of files.
3247 * The pattern can have wildcard characters ? and *, but only
3248 * in the section after the last directory delimiter. (Return
3249 * ERROR_INVALID_NAME if there are wildcards in earlier path
3250 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3251 * means "match one character", "??" seems to mean "match one
3252 * or two characters", "???" seems to mean "match one, two or
3253 * three characters", etc. Windows will also try and match
3254 * the mangled "short name" of files, so 8 character patterns
3255 * with wildcards will show some surprising results.
3257 * All the written documentation I can find says that '?'
3258 * should only match one character, and doesn't mention '??',
3259 * '???' etc. I'm going to assume that the strict behaviour
3260 * (ie '???' means three and only three characters) is the
3261 * correct one, because that lets me use fnmatch(3) rather
3262 * than mess around with regexes.
3266 result
= _wapi_io_scandir (dir_part
, entry_part
,
3270 /* No files, which windows seems to call
3273 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
3274 g_free (utf8_pattern
);
3275 g_free (entry_part
);
3277 g_strfreev (namelist
);
3278 return (INVALID_HANDLE_VALUE
);
3282 _wapi_set_last_path_error_from_errno (dir_part
, NULL
);
3283 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: scandir error: %s", __func__
, g_strerror (errno
));
3284 g_free (utf8_pattern
);
3285 g_free (entry_part
);
3287 g_strfreev (namelist
);
3288 return (INVALID_HANDLE_VALUE
);
3291 g_free (utf8_pattern
);
3292 g_free (entry_part
);
3294 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Got %d matches", __func__
, result
);
3296 findhandle
= findhandle_create ();
3297 findhandle
->namelist
= namelist
;
3298 findhandle
->dir_part
= dir_part
;
3299 findhandle
->num
= result
;
3300 findhandle
->count
= 0;
3302 findhandle_insert (findhandle
);
3304 if (!mono_w32file_find_next ((gpointer
) findhandle
, find_data
)) {
3305 mono_w32file_find_close ((gpointer
) findhandle
);
3306 mono_w32error_set_last (ERROR_NO_MORE_FILES
);
3307 return INVALID_HANDLE_VALUE
;
3310 return (gpointer
) findhandle
;
3314 mono_w32file_find_next (gpointer handle
, WIN32_FIND_DATA
*find_data
)
3316 FindHandle
*findhandle
;
3317 struct stat buf
, linkbuf
;
3320 gchar
*utf8_filename
, *utf8_basename
;
3321 gunichar2
*utf16_basename
;
3324 gboolean ret
= FALSE
;
3326 if (!findhandle_lookup_and_ref (handle
, &findhandle
)) {
3327 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
3331 mono_coop_mutex_lock (&findhandle
->mutex
);
3334 if (findhandle
->count
>= findhandle
->num
) {
3335 mono_w32error_set_last (ERROR_NO_MORE_FILES
);
3339 /* stat next match */
3341 filename
= g_build_filename (findhandle
->dir_part
, findhandle
->namelist
[findhandle
->count
++], (const char*)NULL
);
3343 result
= _wapi_stat (filename
, &buf
);
3344 if (result
== -1 && errno
== ENOENT
) {
3345 /* Might be a dangling symlink */
3346 result
= _wapi_lstat (filename
, &buf
);
3350 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: stat failed: %s", __func__
, filename
);
3356 result
= _wapi_lstat (filename
, &linkbuf
);
3358 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: lstat failed: %s", __func__
, filename
);
3364 utf8_filename
= mono_utf8_from_external (filename
);
3365 if (utf8_filename
== NULL
) {
3366 /* We couldn't turn this filename into utf8 (eg the
3367 * encoding of the name wasn't convertible), so just
3370 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__
, filename
);
3377 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Found [%s]", __func__
, utf8_filename
);
3379 /* fill data block */
3381 if (buf
.st_mtime
< buf
.st_ctime
)
3382 create_time
= buf
.st_mtime
;
3384 create_time
= buf
.st_ctime
;
3386 find_data
->dwFileAttributes
= _wapi_stat_to_file_attributes (utf8_filename
, &buf
, &linkbuf
);
3388 time_t_to_filetime (create_time
, &find_data
->ftCreationTime
);
3389 time_t_to_filetime (buf
.st_atime
, &find_data
->ftLastAccessTime
);
3390 time_t_to_filetime (buf
.st_mtime
, &find_data
->ftLastWriteTime
);
3392 if (find_data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
3393 find_data
->nFileSizeHigh
= 0;
3394 find_data
->nFileSizeLow
= 0;
3396 find_data
->nFileSizeHigh
= buf
.st_size
>> 32;
3397 find_data
->nFileSizeLow
= buf
.st_size
& 0xFFFFFFFF;
3400 find_data
->dwReserved0
= 0;
3401 find_data
->dwReserved1
= 0;
3403 utf8_basename
= _wapi_basename (utf8_filename
);
3404 utf16_basename
= g_utf8_to_utf16 (utf8_basename
, -1, NULL
, &bytes
,
3406 if(utf16_basename
==NULL
) {
3407 g_free (utf8_basename
);
3408 g_free (utf8_filename
);
3413 /* utf16 is 2 * utf8 */
3416 memset (find_data
->cFileName
, '\0', (MAX_PATH
*2));
3418 /* Truncating a utf16 string like this might leave the last
3421 memcpy (find_data
->cFileName
, utf16_basename
,
3422 bytes
<(MAX_PATH
*2)-2?bytes
:(MAX_PATH
*2)-2);
3424 find_data
->cAlternateFileName
[0] = 0; /* not used */
3426 g_free (utf8_basename
);
3427 g_free (utf8_filename
);
3428 g_free (utf16_basename
);
3431 mono_coop_mutex_unlock (&findhandle
->mutex
);
3433 findhandle_unref (findhandle
);
3439 mono_w32file_find_close (gpointer handle
)
3441 if (!findhandle_close (handle
)) {
3442 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
3450 mono_w32file_create_directory (const gunichar2
*name
)
3457 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3459 mono_w32error_set_last (ERROR_INVALID_NAME
);
3463 utf8_name
= mono_unicode_to_external_checked (name
, error
);
3464 if (utf8_name
== NULL
) {
3465 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
3467 mono_error_cleanup (error
);
3468 mono_w32error_set_last (ERROR_INVALID_NAME
);
3472 result
= _wapi_mkdir (utf8_name
, 0777);
3479 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3485 mono_w32file_remove_directory (const gunichar2
*name
)
3492 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3494 mono_w32error_set_last (ERROR_INVALID_NAME
);
3498 utf8_name
= mono_unicode_to_external_checked (name
, error
);
3499 if (utf8_name
== NULL
) {
3500 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
3502 mono_error_cleanup (error
);
3503 mono_w32error_set_last (ERROR_INVALID_NAME
);
3507 result
= _wapi_rmdir (utf8_name
);
3509 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3520 mono_w32file_get_attributes (const gunichar2
*name
)
3523 struct stat buf
, linkbuf
;
3529 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3531 mono_w32error_set_last (ERROR_INVALID_NAME
);
3535 utf8_name
= mono_unicode_to_external_checked (name
, error
);
3536 if (utf8_name
== NULL
) {
3537 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
3539 mono_error_cleanup (error
);
3540 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3541 return (INVALID_FILE_ATTRIBUTES
);
3544 result
= _wapi_stat (utf8_name
, &buf
);
3545 if (result
== -1 && (errno
== ENOENT
|| errno
== ELOOP
)) {
3546 /* Might be a dangling symlink... */
3547 result
= _wapi_lstat (utf8_name
, &buf
);
3551 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3553 return (INVALID_FILE_ATTRIBUTES
);
3556 result
= _wapi_lstat (utf8_name
, &linkbuf
);
3558 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3560 return (INVALID_FILE_ATTRIBUTES
);
3563 ret
= _wapi_stat_to_file_attributes (utf8_name
, &buf
, &linkbuf
);
3571 mono_w32file_get_attributes_ex (const gunichar2
*name
, MonoIOStat
*stat
)
3575 struct stat buf
, linkbuf
;
3580 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3582 mono_w32error_set_last (ERROR_INVALID_NAME
);
3586 utf8_name
= mono_unicode_to_external_checked (name
, error
);
3587 if (utf8_name
== NULL
) {
3588 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
3590 mono_error_cleanup (error
);
3591 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3595 result
= _wapi_stat (utf8_name
, &buf
);
3596 if (result
== -1 && errno
== ENOENT
) {
3597 /* Might be a dangling symlink... */
3598 result
= _wapi_lstat (utf8_name
, &buf
);
3602 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3607 result
= _wapi_lstat (utf8_name
, &linkbuf
);
3609 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3614 /* fill stat block */
3616 stat
->attributes
= _wapi_stat_to_file_attributes (utf8_name
, &buf
, &linkbuf
);
3617 stat
->length
= (stat
->attributes
& FILE_ATTRIBUTE_DIRECTORY
) ? 0 : buf
.st_size
;
3619 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
3620 if (linkbuf
.st_mtimespec
.tv_sec
< linkbuf
.st_ctimespec
.tv_sec
|| (linkbuf
.st_mtimespec
.tv_sec
== linkbuf
.st_ctimespec
.tv_sec
&& linkbuf
.st_mtimespec
.tv_nsec
< linkbuf
.st_ctimespec
.tv_nsec
))
3621 stat
->creation_time
= linkbuf
.st_mtimespec
.tv_sec
* TICKS_PER_SECOND
+ (linkbuf
.st_mtimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3623 stat
->creation_time
= linkbuf
.st_ctimespec
.tv_sec
* TICKS_PER_SECOND
+ (linkbuf
.st_ctimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3625 stat
->last_access_time
= linkbuf
.st_atimespec
.tv_sec
* TICKS_PER_SECOND
+ (linkbuf
.st_atimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3626 stat
->last_write_time
= linkbuf
.st_mtimespec
.tv_sec
* TICKS_PER_SECOND
+ (linkbuf
.st_mtimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3627 #elif HAVE_STRUCT_STAT_ST_ATIM
3628 if (linkbuf
.st_mtime
< linkbuf
.st_ctime
|| (linkbuf
.st_mtime
== linkbuf
.st_ctime
&& linkbuf
.st_mtim
.tv_nsec
< linkbuf
.st_ctim
.tv_nsec
))
3629 stat
->creation_time
= linkbuf
.st_mtime
* TICKS_PER_SECOND
+ (linkbuf
.st_mtim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3631 stat
->creation_time
= linkbuf
.st_ctime
* TICKS_PER_SECOND
+ (linkbuf
.st_ctim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3633 stat
->last_access_time
= linkbuf
.st_atime
* TICKS_PER_SECOND
+ (linkbuf
.st_atim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3634 stat
->last_write_time
= linkbuf
.st_mtime
* TICKS_PER_SECOND
+ (linkbuf
.st_mtim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3636 stat
->creation_time
= (((guint64
) (linkbuf
.st_mtime
< linkbuf
.st_ctime
? linkbuf
.st_mtime
: linkbuf
.st_ctime
)) * TICKS_PER_SECOND
) + CONVERT_BASE
;
3637 stat
->last_access_time
= (((guint64
) (linkbuf
.st_atime
)) * TICKS_PER_SECOND
) + CONVERT_BASE
;
3638 stat
->last_write_time
= (((guint64
) (linkbuf
.st_mtime
)) * TICKS_PER_SECOND
) + CONVERT_BASE
;
3646 mono_w32file_set_attributes (const gunichar2
*name
, guint32 attrs
)
3648 /* FIXME: think of something clever to do on unix */
3655 * Currently we only handle one *internal* case, with a value that is
3656 * not standard: 0x80000000, which means `set executable bit'
3660 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3662 mono_w32error_set_last (ERROR_INVALID_NAME
);
3666 utf8_name
= mono_unicode_to_external_checked (name
, error
);
3667 if (utf8_name
== NULL
) {
3668 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
3670 mono_error_cleanup (error
);
3671 mono_w32error_set_last (ERROR_INVALID_NAME
);
3675 result
= _wapi_stat (utf8_name
, &buf
);
3676 if (result
== -1 && errno
== ENOENT
) {
3677 /* Might be a dangling symlink... */
3678 result
= _wapi_lstat (utf8_name
, &buf
);
3682 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3687 /* Contrary to the documentation, ms allows NORMAL to be
3688 * specified along with other attributes, so dont bother to
3689 * catch that case here.
3691 if (attrs
& FILE_ATTRIBUTE_READONLY
) {
3692 result
= _wapi_chmod (utf8_name
, buf
.st_mode
& ~(S_IWUSR
| S_IWOTH
| S_IWGRP
));
3694 result
= _wapi_chmod (utf8_name
, buf
.st_mode
| S_IWUSR
);
3697 /* Ignore the other attributes for now */
3699 if (attrs
& 0x80000000){
3700 mode_t exec_mask
= 0;
3702 if ((buf
.st_mode
& S_IRUSR
) != 0)
3703 exec_mask
|= S_IXUSR
;
3705 if ((buf
.st_mode
& S_IRGRP
) != 0)
3706 exec_mask
|= S_IXGRP
;
3708 if ((buf
.st_mode
& S_IROTH
) != 0)
3709 exec_mask
|= S_IXOTH
;
3711 #if defined(HAVE_CHMOD)
3713 result
= chmod (utf8_name
, buf
.st_mode
| exec_mask
);
3719 /* Don't bother to reset executable (might need to change this
3729 mono_w32file_get_cwd (guint32 length
, gunichar2
*buffer
)
3731 gunichar2
*utf16_path
;
3735 if (getcwd ((gchar
*)buffer
, length
) == NULL
) {
3736 if (errno
== ERANGE
) { /*buffer length is not big enough */
3737 gchar
*path
= g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/
3740 utf16_path
= mono_unicode_from_external (path
, &bytes
);
3741 g_free (utf16_path
);
3745 _wapi_set_last_error_from_errno ();
3749 utf16_path
= mono_unicode_from_external ((gchar
*)buffer
, &bytes
);
3750 count
= (bytes
/2)+1;
3751 g_assert (count
<= length
); /*getcwd must have failed before with ERANGE*/
3753 /* Add the terminator */
3754 memset (buffer
, '\0', bytes
+2);
3755 memcpy (buffer
, utf16_path
, bytes
);
3757 g_free (utf16_path
);
3763 mono_w32file_set_cwd (const gunichar2
*path
)
3770 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3774 utf8_path
= mono_unicode_to_external_checked (path
, error
);
3775 if (utf8_path
== NULL
) {
3776 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
3778 mono_error_cleanup (error
);
3779 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3783 if (_wapi_chdir (utf8_path
) != 0) {
3784 _wapi_set_last_error_from_errno ();
3795 mono_w32file_create_pipe (gpointer
*readpipe
, gpointer
*writepipe
, guint32 size
)
3797 FileHandle
*read_filehandle
, *write_filehandle
;
3801 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Creating pipe", __func__
);
3807 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Error creating pipe: (%d) %s",
3808 __func__
, errno
, g_strerror (errno
));
3810 _wapi_set_last_error_from_errno ();
3814 /* filedes[0] is open for reading, filedes[1] for writing */
3816 read_filehandle
= file_data_create (MONO_FDTYPE_PIPE
, filedes
[0]);
3817 read_filehandle
->fileaccess
= GENERIC_READ
;
3819 write_filehandle
= file_data_create (MONO_FDTYPE_PIPE
, filedes
[1]);
3820 write_filehandle
->fileaccess
= GENERIC_WRITE
;
3822 mono_fdhandle_insert ((MonoFDHandle
*) read_filehandle
);
3823 mono_fdhandle_insert ((MonoFDHandle
*) write_filehandle
);
3825 *readpipe
= GINT_TO_POINTER(((MonoFDHandle
*) read_filehandle
)->fd
);
3826 *writepipe
= GINT_TO_POINTER(((MonoFDHandle
*) write_filehandle
)->fd
);
3828 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Returning pipe: read handle %p, write handle %p",
3829 __func__
, GINT_TO_POINTER(((MonoFDHandle
*) read_filehandle
)->fd
), GINT_TO_POINTER(((MonoFDHandle
*) write_filehandle
)->fd
));
3834 #ifdef HAVE_GETFSSTAT
3835 /* Darwin has getfsstat */
3837 mono_w32file_get_logical_drive (guint32 len
, gunichar2
*buf
, MonoError
*error
)
3839 struct statfs
*stats
;
3842 glong length
, total
= 0;
3846 n
= getfsstat (NULL
, 0, MNT_NOWAIT
);
3850 size
= n
* sizeof (struct statfs
);
3851 stats
= (struct statfs
*) g_malloc (size
);
3855 syscall_res
= getfsstat (stats
, size
, MNT_NOWAIT
);
3857 if (syscall_res
== -1){
3861 for (i
= 0; i
< n
; i
++){
3862 dir
= g_utf8_to_utf16 (stats
[i
].f_mntonname
, -1, NULL
, &length
, NULL
);
3863 if (total
+ length
< len
){
3864 memcpy (buf
+ total
, dir
, sizeof (gunichar2
) * length
);
3865 buf
[total
+length
] = 0;
3868 total
+= length
+ 1;
3878 mono_w32file_get_logical_drive (guint32 len
, gunichar2
*buf
, MonoError
*error
)
3880 struct vmount
*mounts
;
3881 // ret will first be the errno cond, then no of structs
3882 int needsize
, ret
, total
;
3888 ret
= mntctl (MCTL_QUERY
, sizeof(needsize
), (char*)&needsize
);
3892 mounts
= (struct vmount
*) g_malloc (needsize
);
3896 ret
= mntctl (MCTL_QUERY
, needsize
, (char*)mounts
);
3903 for (int i
= 0; i
< ret
; i
++) {
3904 dir
= g_utf8_to_utf16 (vmt2dataptr(mounts
, VMT_STUB
), -1, NULL
, &length
, NULL
);
3905 if (total
+ length
< len
){
3906 memcpy (buf
+ total
, dir
, sizeof (gunichar2
) * length
);
3907 buf
[total
+length
] = 0;
3910 total
+= length
+ 1;
3911 mounts
= (struct vmount
*)((char*)mounts
+ mounts
->vmt_length
); // next!
3920 /* In-place octal sequence replacement */
3922 unescape_octal (gchar
*str
)
3931 while (*rptr
!= '\0') {
3932 if (*rptr
== '\\') {
3935 c
= (*(rptr
++) - '0') << 6;
3936 c
+= (*(rptr
++) - '0') << 3;
3937 c
+= *(rptr
++) - '0';
3939 } else if (wptr
!= rptr
) {
3947 static gint32
GetLogicalDriveStrings_Mtab (guint32 len
, gunichar2
*buf
);
3950 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
3951 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
3952 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
3957 guint32 buffer_index
;
3958 guint32 mountpoint_index
;
3959 guint32 field_number
;
3960 guint32 allocated_size
;
3961 guint32 fsname_index
;
3962 guint32 fstype_index
;
3963 gchar mountpoint
[GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER
+ 1];
3964 gchar
*mountpoint_allocated
;
3965 gchar buffer
[GET_LOGICAL_DRIVE_STRINGS_BUFFER
];
3966 gchar fsname
[GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
+ 1];
3967 gchar fstype
[GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
+ 1];
3970 gboolean check_mount_source
;
3971 } LinuxMountInfoParseState
;
3973 static gboolean
GetLogicalDriveStrings_Mounts (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3974 static gboolean
GetLogicalDriveStrings_MountInfo (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3975 static void append_to_mountpoint (LinuxMountInfoParseState
*state
);
3976 static gboolean
add_drive_string (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3979 mono_w32file_get_logical_drive (guint32 len
, gunichar2
*buf
, MonoError
*error
)
3983 LinuxMountInfoParseState state
;
3984 gboolean (*parser
)(guint32
, gunichar2
*, LinuxMountInfoParseState
*) = NULL
;
3986 memset (buf
, 0, len
* sizeof (gunichar2
));
3988 fd
= open ("/proc/self/mountinfo", O_RDONLY
);
3991 parser
= GetLogicalDriveStrings_MountInfo
;
3994 fd
= open ("/proc/mounts", O_RDONLY
);
3997 parser
= GetLogicalDriveStrings_Mounts
;
4001 ret
= GetLogicalDriveStrings_Mtab (len
, buf
);
4005 memset (&state
, 0, sizeof (LinuxMountInfoParseState
));
4006 state
.field_number
= 1;
4007 state
.delimiter
= ' ';
4011 state
.nbytes
= read (fd
, state
.buffer
, GET_LOGICAL_DRIVE_STRINGS_BUFFER
);
4013 if (!(state
.nbytes
> 0))
4015 state
.buffer_index
= 0;
4017 while ((*parser
)(len
, buf
, &state
)) {
4018 if (state
.buffer
[state
.buffer_index
] == '\n') {
4019 gboolean quit
= add_drive_string (len
, buf
, &state
);
4020 state
.field_number
= 1;
4021 state
.buffer_index
++;
4022 if (state
.mountpoint_allocated
) {
4023 g_free (state
.mountpoint_allocated
);
4024 state
.mountpoint_allocated
= NULL
;
4044 static gboolean
GetLogicalDriveStrings_Mounts (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
4048 if (state
->field_number
== 1)
4049 state
->check_mount_source
= TRUE
;
4051 while (state
->buffer_index
< (guint32
)state
->nbytes
) {
4052 if (state
->buffer
[state
->buffer_index
] == state
->delimiter
) {
4053 state
->field_number
++;
4054 switch (state
->field_number
) {
4056 state
->mountpoint_index
= 0;
4060 if (state
->mountpoint_allocated
)
4061 state
->mountpoint_allocated
[state
->mountpoint_index
] = 0;
4063 state
->mountpoint
[state
->mountpoint_index
] = 0;
4067 ptr
= (gchar
*)memchr (state
->buffer
+ state
->buffer_index
, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER
- state
->buffer_index
);
4069 state
->buffer_index
= (ptr
- (gchar
*)state
->buffer
) - 1;
4071 state
->buffer_index
= state
->nbytes
;
4074 state
->buffer_index
++;
4076 } else if (state
->buffer
[state
->buffer_index
] == '\n')
4079 switch (state
->field_number
) {
4081 if (state
->check_mount_source
) {
4082 if (state
->fsname_index
== 0 && state
->buffer
[state
->buffer_index
] == '/') {
4083 /* We can ignore the rest, it's a device
4085 state
->check_mount_source
= FALSE
;
4086 state
->fsname
[state
->fsname_index
++] = '/';
4089 if (state
->fsname_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
4090 state
->fsname
[state
->fsname_index
++] = state
->buffer
[state
->buffer_index
];
4095 append_to_mountpoint (state
);
4099 if (state
->fstype_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
4100 state
->fstype
[state
->fstype_index
++] = state
->buffer
[state
->buffer_index
];
4104 state
->buffer_index
++;
4110 static gboolean
GetLogicalDriveStrings_MountInfo (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
4112 while (state
->buffer_index
< (guint32
)state
->nbytes
) {
4113 if (state
->buffer
[state
->buffer_index
] == state
->delimiter
) {
4114 state
->field_number
++;
4115 switch (state
->field_number
) {
4117 state
->mountpoint_index
= 0;
4121 if (state
->mountpoint_allocated
)
4122 state
->mountpoint_allocated
[state
->mountpoint_index
] = 0;
4124 state
->mountpoint
[state
->mountpoint_index
] = 0;
4128 state
->delimiter
= '-';
4132 state
->delimiter
= ' ';
4136 state
->check_mount_source
= TRUE
;
4139 state
->buffer_index
++;
4141 } else if (state
->buffer
[state
->buffer_index
] == '\n')
4144 switch (state
->field_number
) {
4146 append_to_mountpoint (state
);
4150 if (state
->fstype_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
4151 state
->fstype
[state
->fstype_index
++] = state
->buffer
[state
->buffer_index
];
4155 if (state
->check_mount_source
) {
4156 if (state
->fsname_index
== 0 && state
->buffer
[state
->buffer_index
] == '/') {
4157 /* We can ignore the rest, it's a device
4159 state
->check_mount_source
= FALSE
;
4160 state
->fsname
[state
->fsname_index
++] = '/';
4163 if (state
->fsname_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
4164 state
->fsname
[state
->fsname_index
++] = state
->buffer
[state
->buffer_index
];
4169 state
->buffer_index
++;
4176 append_to_mountpoint (LinuxMountInfoParseState
*state
)
4178 gchar ch
= state
->buffer
[state
->buffer_index
];
4179 if (state
->mountpoint_allocated
) {
4180 if (state
->mountpoint_index
>= state
->allocated_size
) {
4181 guint32 newsize
= (state
->allocated_size
<< 1) + 1;
4182 gchar
*newbuf
= (gchar
*)g_malloc0 (newsize
* sizeof (gchar
));
4184 memcpy (newbuf
, state
->mountpoint_allocated
, state
->mountpoint_index
);
4185 g_free (state
->mountpoint_allocated
);
4186 state
->mountpoint_allocated
= newbuf
;
4187 state
->allocated_size
= newsize
;
4189 state
->mountpoint_allocated
[state
->mountpoint_index
++] = ch
;
4191 if (state
->mountpoint_index
>= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER
) {
4192 state
->allocated_size
= (state
->mountpoint_index
<< 1) + 1;
4193 state
->mountpoint_allocated
= (gchar
*)g_malloc0 (state
->allocated_size
* sizeof (gchar
));
4194 memcpy (state
->mountpoint_allocated
, state
->mountpoint
, state
->mountpoint_index
);
4195 state
->mountpoint_allocated
[state
->mountpoint_index
++] = ch
;
4197 state
->mountpoint
[state
->mountpoint_index
++] = ch
;
4202 add_drive_string (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
4204 gboolean quit
= FALSE
;
4205 gboolean ignore_entry
;
4207 if (state
->fsname_index
== 1 && state
->fsname
[0] == '/')
4208 ignore_entry
= FALSE
;
4209 else if (memcmp ("overlay", state
->fsname
, state
->fsname_index
) == 0 ||
4210 memcmp ("aufs", state
->fstype
, state
->fstype_index
) == 0) {
4211 /* Don't ignore overlayfs and aufs - these might be used on Docker
4212 * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */
4213 ignore_entry
= FALSE
;
4214 } else if (state
->fsname_index
== 0 || memcmp ("none", state
->fsname
, state
->fsname_index
) == 0) {
4215 ignore_entry
= TRUE
;
4216 } else if (state
->fstype_index
>= 5 && memcmp ("fuse.", state
->fstype
, 5) == 0) {
4217 /* Ignore GNOME's gvfs */
4218 if (state
->fstype_index
== 21 && memcmp ("fuse.gvfs-fuse-daemon", state
->fstype
, state
->fstype_index
) == 0)
4219 ignore_entry
= TRUE
;
4221 ignore_entry
= FALSE
;
4222 } else if (state
->fstype_index
== 3 && memcmp ("nfs", state
->fstype
, state
->fstype_index
) == 0)
4223 ignore_entry
= FALSE
;
4225 ignore_entry
= TRUE
;
4227 if (!ignore_entry
) {
4230 gchar
*mountpoint
= state
->mountpoint_allocated
? state
->mountpoint_allocated
: state
->mountpoint
;
4232 unescape_octal (mountpoint
);
4233 dir
= g_utf8_to_utf16 (mountpoint
, -1, NULL
, &length
, NULL
);
4234 if (state
->total
+ length
+ 1 > len
) {
4236 state
->total
= len
* 2;
4239 memcpy (buf
+ state
->total
, dir
, sizeof (gunichar2
) * length
);
4240 state
->total
+= length
;
4244 state
->fsname_index
= 0;
4245 state
->fstype_index
= 0;
4251 mono_w32file_get_logical_drive (guint32 len
, gunichar2
*buf
, MonoError
*error
)
4253 return GetLogicalDriveStrings_Mtab (len
, buf
);
4257 GetLogicalDriveStrings_Mtab (guint32 len
, gunichar2
*buf
)
4260 gunichar2
*ptr
, *dir
;
4261 glong length
, total
= 0;
4265 memset (buf
, 0, sizeof (gunichar2
) * (len
+ 1));
4270 /* Sigh, mntent and friends don't work well.
4271 * It stops on the first line that doesn't begin with a '/'.
4272 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4274 fp
= fopen ("/etc/mtab", "rt");
4278 fp
= fopen ("/etc/mnttab", "rt");
4288 fgets_res
= fgets (buffer
, 512, fp
);
4295 splitted
= g_strsplit (buffer
, " ", 0);
4296 if (!*splitted
|| !*(splitted
+ 1)) {
4297 g_strfreev (splitted
);
4301 unescape_octal (*(splitted
+ 1));
4302 dir
= g_utf8_to_utf16 (*(splitted
+ 1), -1, NULL
, &length
, NULL
);
4303 g_strfreev (splitted
);
4304 if (total
+ length
+ 1 > len
) {
4309 return len
* 2; /* guess */
4312 memcpy (ptr
+ total
, dir
, sizeof (gunichar2
) * length
);
4314 total
+= length
+ 1;
4321 /* Commented out, does not work with my mtab!!! - Gonz */
4322 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4326 gunichar2
*ptr
, *dir
;
4327 glong len
, total
= 0;
4331 fp
= setmntent ("/etc/mtab", "rt");
4335 fp
= setmntent ("/etc/mnttab", "rt");
4344 mnt
= getmntent (fp
);
4348 g_print ("GOT %s\n", mnt
->mnt_dir
);
4349 dir
= g_utf8_to_utf16 (mnt
->mnt_dir
, &len
, NULL
, NULL
, NULL
);
4350 if (total
+ len
+ 1 > len
) {
4354 return len
* 2; /* guess */
4357 memcpy (ptr
+ total
, dir
, sizeof (gunichar2
) * len
);
4371 #ifndef PLATFORM_NO_DRIVEINFO
4373 mono_w32file_get_disk_free_space (const gunichar2
*path_name
, guint64
*free_bytes_avail
, guint64
*total_number_of_bytes
, guint64
*total_number_of_free_bytes
)
4375 g_assert (free_bytes_avail
);
4376 g_assert (total_number_of_bytes
);
4377 g_assert (total_number_of_free_bytes
);
4379 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
4381 struct statvfs fsstat
;
4382 #elif defined(HAVE_STATFS)
4383 struct statfs fsstat
;
4385 gchar
*utf8_path_name
;
4387 unsigned long block_size
;
4390 if (path_name
== NULL
) {
4391 utf8_path_name
= g_strdup (g_get_current_dir());
4392 if (utf8_path_name
== NULL
) {
4393 mono_w32error_set_last (ERROR_DIRECTORY
);
4398 utf8_path_name
= mono_unicode_to_external_checked (path_name
, error
);
4399 if (utf8_path_name
== NULL
) {
4400 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
4402 mono_error_cleanup (error
);
4403 mono_w32error_set_last (ERROR_INVALID_NAME
);
4411 ret
= statvfs (utf8_path_name
, &fsstat
);
4413 block_size
= fsstat
.f_frsize
;
4414 #elif defined(HAVE_STATFS)
4416 ret
= statfs (utf8_path_name
, &fsstat
);
4418 block_size
= fsstat
.f_bsize
;
4420 } while(ret
== -1 && errno
== EINTR
);
4422 g_free(utf8_path_name
);
4425 _wapi_set_last_error_from_errno ();
4426 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: statvfs failed: %s", __func__
, g_strerror (errno
));
4430 /* total number of free bytes for non-root */
4431 *free_bytes_avail
= block_size
* (guint64
)fsstat
.f_bavail
;
4433 /* total number of bytes available for non-root */
4434 *total_number_of_bytes
= block_size
* (guint64
)fsstat
.f_blocks
;
4436 /* total number of bytes available for root */
4437 *total_number_of_free_bytes
= block_size
* (guint64
)fsstat
.f_bfree
;
4441 #endif // PLATFORM_NO_DRIVEINFO
4444 * General Unix support
4449 // http://man7.org/linux/man-pages/man2/statfs.2.html
4451 // The __fsword_t type used for various fields in the statfs structure
4452 // definition is a glibc internal type, not intended for public use.
4453 // This leaves the programmer in a bit of a conundrum when trying to
4454 // copy or compare these fields to local variables in a program. Using
4455 // unsigned int for such variables suffices on most systems.
4457 // Let's hope "most" is enough, and that it works with other libc.
4460 const gchar
* fstype
;
4463 static const _wapi_drive_type _wapi_drive_types
[] = {
4465 { DRIVE_REMOTE
, "afp" },
4466 { DRIVE_REMOTE
, "afpfs" },
4467 { DRIVE_REMOTE
, "autofs" },
4468 { DRIVE_CDROM
, "cddafs" },
4469 { DRIVE_CDROM
, "cd9660" },
4470 { DRIVE_RAMDISK
, "devfs" },
4471 { DRIVE_RAMDISK
, "nullfs" },
4472 { DRIVE_FIXED
, "exfat" },
4473 { DRIVE_RAMDISK
, "fdesc" },
4474 { DRIVE_REMOTE
, "ftp" },
4475 { DRIVE_FIXED
, "hfs" },
4476 { DRIVE_FIXED
, "apfs" },
4477 { DRIVE_REMOTE
, "kbfuse" },
4478 { DRIVE_FIXED
, "msdos" },
4479 { DRIVE_REMOTE
, "nfs" },
4480 { DRIVE_FIXED
, "ntfs" },
4481 { DRIVE_REMOTE
, "smbfs" },
4482 { DRIVE_FIXED
, "udf" },
4483 { DRIVE_REMOTE
, "webdav" },
4484 { DRIVE_FIXED
, "ufsd_NTFS"},
4485 { DRIVE_UNKNOWN
, NULL
}
4487 { DRIVE_FIXED
, ADFS_SUPER_MAGIC
, "adfs"},
4488 { DRIVE_FIXED
, AFFS_SUPER_MAGIC
, "affs"},
4489 { DRIVE_REMOTE
, AFS_SUPER_MAGIC
, "afs"},
4490 { DRIVE_RAMDISK
, AUTOFS_SUPER_MAGIC
, "autofs"},
4491 { DRIVE_RAMDISK
, AUTOFS_SBI_MAGIC
, "autofs4"},
4492 { DRIVE_REMOTE
, CODA_SUPER_MAGIC
, "coda" },
4493 { DRIVE_RAMDISK
, CRAMFS_MAGIC
, "cramfs"},
4494 { DRIVE_RAMDISK
, CRAMFS_MAGIC_WEND
, "cramfs"},
4495 { DRIVE_REMOTE
, CIFS_MAGIC_NUMBER
, "cifs"},
4496 { DRIVE_RAMDISK
, DEBUGFS_MAGIC
, "debugfs"},
4497 { DRIVE_RAMDISK
, SYSFS_MAGIC
, "sysfs"},
4498 { DRIVE_RAMDISK
, SECURITYFS_MAGIC
, "securityfs"},
4499 { DRIVE_RAMDISK
, SELINUX_MAGIC
, "selinuxfs"},
4500 { DRIVE_RAMDISK
, RAMFS_MAGIC
, "ramfs"},
4501 { DRIVE_FIXED
, SQUASHFS_MAGIC
, "squashfs"},
4502 { DRIVE_FIXED
, EFS_SUPER_MAGIC
, "efs"},
4503 { DRIVE_FIXED
, EXT2_SUPER_MAGIC
, "ext"},
4504 { DRIVE_FIXED
, EXT3_SUPER_MAGIC
, "ext"},
4505 { DRIVE_FIXED
, EXT4_SUPER_MAGIC
, "ext"},
4506 { DRIVE_REMOTE
, XENFS_SUPER_MAGIC
, "xenfs"},
4507 { DRIVE_FIXED
, BTRFS_SUPER_MAGIC
, "btrfs"},
4508 { DRIVE_FIXED
, HFS_SUPER_MAGIC
, "hfs"},
4509 { DRIVE_FIXED
, HFSPLUS_SUPER_MAGIC
, "hfsplus"},
4510 { DRIVE_FIXED
, HPFS_SUPER_MAGIC
, "hpfs"},
4511 { DRIVE_RAMDISK
, HUGETLBFS_MAGIC
, "hugetlbfs"},
4512 { DRIVE_CDROM
, ISOFS_SUPER_MAGIC
, "iso"},
4513 { DRIVE_FIXED
, JFFS2_SUPER_MAGIC
, "jffs2"},
4514 { DRIVE_RAMDISK
, ANON_INODE_FS_MAGIC
, "anon_inode"},
4515 { DRIVE_FIXED
, JFS_SUPER_MAGIC
, "jfs"},
4516 { DRIVE_FIXED
, MINIX_SUPER_MAGIC
, "minix"},
4517 { DRIVE_FIXED
, MINIX_SUPER_MAGIC2
, "minix v2"},
4518 { DRIVE_FIXED
, MINIX2_SUPER_MAGIC
, "minix2"},
4519 { DRIVE_FIXED
, MINIX2_SUPER_MAGIC2
, "minix2 v2"},
4520 { DRIVE_FIXED
, MINIX3_SUPER_MAGIC
, "minix3"},
4521 { DRIVE_FIXED
, MSDOS_SUPER_MAGIC
, "msdos"},
4522 { DRIVE_REMOTE
, NCP_SUPER_MAGIC
, "ncp"},
4523 { DRIVE_REMOTE
, NFS_SUPER_MAGIC
, "nfs"},
4524 { DRIVE_FIXED
, NTFS_SB_MAGIC
, "ntfs"},
4525 { DRIVE_RAMDISK
, OPENPROM_SUPER_MAGIC
, "openpromfs"},
4526 { DRIVE_RAMDISK
, PROC_SUPER_MAGIC
, "proc"},
4527 { DRIVE_FIXED
, QNX4_SUPER_MAGIC
, "qnx4"},
4528 { DRIVE_FIXED
, REISERFS_SUPER_MAGIC
, "reiserfs"},
4529 { DRIVE_RAMDISK
, ROMFS_MAGIC
, "romfs"},
4530 { DRIVE_REMOTE
, SMB_SUPER_MAGIC
, "samba"},
4531 { DRIVE_RAMDISK
, CGROUP_SUPER_MAGIC
, "cgroupfs"},
4532 { DRIVE_RAMDISK
, FUTEXFS_SUPER_MAGIC
, "futexfs"},
4533 { DRIVE_FIXED
, SYSV2_SUPER_MAGIC
, "sysv2"},
4534 { DRIVE_FIXED
, SYSV4_SUPER_MAGIC
, "sysv4"},
4535 { DRIVE_RAMDISK
, TMPFS_MAGIC
, "tmpfs"},
4536 { DRIVE_RAMDISK
, DEVPTS_SUPER_MAGIC
, "devpts"},
4537 { DRIVE_CDROM
, UDF_SUPER_MAGIC
, "udf"},
4538 { DRIVE_FIXED
, UFS_MAGIC
, "ufs"},
4539 { DRIVE_FIXED
, UFS_MAGIC_BW
, "ufs"},
4540 { DRIVE_FIXED
, UFS2_MAGIC
, "ufs2"},
4541 { DRIVE_FIXED
, UFS_CIGAM
, "ufs"},
4542 { DRIVE_RAMDISK
, USBDEVICE_SUPER_MAGIC
, "usbdev"},
4543 { DRIVE_FIXED
, XENIX_SUPER_MAGIC
, "xenix"},
4544 { DRIVE_FIXED
, XFS_SB_MAGIC
, "xfs"},
4545 { DRIVE_RAMDISK
, FUSE_SUPER_MAGIC
, "fuse"},
4546 { DRIVE_FIXED
, V9FS_MAGIC
, "9p"},
4547 { DRIVE_REMOTE
, CEPH_SUPER_MAGIC
, "ceph"},
4548 { DRIVE_RAMDISK
, CONFIGFS_MAGIC
, "configfs"},
4549 { DRIVE_RAMDISK
, ECRYPTFS_SUPER_MAGIC
, "eCryptfs"},
4550 { DRIVE_FIXED
, EXOFS_SUPER_MAGIC
, "exofs"},
4551 { DRIVE_FIXED
, VXFS_SUPER_MAGIC
, "vxfs"},
4552 { DRIVE_FIXED
, VXFS_OLT_MAGIC
, "vxfs_olt"},
4553 { DRIVE_REMOTE
, GFS2_MAGIC
, "gfs2"},
4554 { DRIVE_FIXED
, LOGFS_MAGIC_U32
, "logfs"},
4555 { DRIVE_FIXED
, OCFS2_SUPER_MAGIC
, "ocfs2"},
4556 { DRIVE_FIXED
, OMFS_MAGIC
, "omfs"},
4557 { DRIVE_FIXED
, UBIFS_SUPER_MAGIC
, "ubifs"},
4558 { DRIVE_UNKNOWN
, 0, NULL
}
4560 { DRIVE_RAMDISK
, "ramfs" },
4561 { DRIVE_RAMDISK
, "tmpfs" },
4562 { DRIVE_RAMDISK
, "proc" },
4563 { DRIVE_RAMDISK
, "sysfs" },
4564 { DRIVE_RAMDISK
, "debugfs" },
4565 { DRIVE_RAMDISK
, "devpts" },
4566 { DRIVE_RAMDISK
, "securityfs" },
4567 { DRIVE_RAMDISK
, "procfs" }, // AIX procfs
4568 { DRIVE_RAMDISK
, "namefs" }, // AIX soft mounts
4569 { DRIVE_RAMDISK
, "nullfs" },
4570 { DRIVE_CDROM
, "iso9660" },
4571 { DRIVE_CDROM
, "cdrfs" }, // AIX ISO9660 CDs
4572 { DRIVE_CDROM
, "udfs" }, // AIX UDF CDs
4573 { DRIVE_CDROM
, "QOPT" }, // IBM i CD mount
4574 { DRIVE_FIXED
, "ext2" },
4575 { DRIVE_FIXED
, "ext3" },
4576 { DRIVE_FIXED
, "ext4" },
4577 { DRIVE_FIXED
, "sysv" },
4578 { DRIVE_FIXED
, "reiserfs" },
4579 { DRIVE_FIXED
, "ufs" },
4580 { DRIVE_FIXED
, "vfat" },
4581 { DRIVE_FIXED
, "msdos" },
4582 { DRIVE_FIXED
, "udf" },
4583 { DRIVE_FIXED
, "hfs" },
4584 { DRIVE_FIXED
, "hpfs" },
4585 { DRIVE_FIXED
, "qnx4" },
4586 { DRIVE_FIXED
, "ntfs" },
4587 { DRIVE_FIXED
, "ntfs-3g" },
4588 { DRIVE_FIXED
, "jfs" }, // IBM JFS
4589 { DRIVE_FIXED
, "jfs2" }, // IBM JFS (AIX defalt filesystem)
4590 { DRIVE_FIXED
, "EPFS" }, // IBM i IFS (root and QOpenSys)
4591 { DRIVE_FIXED
, "EPFSP" }, // IBM i auxiliary storage pool FS
4592 { DRIVE_FIXED
, "QSYS" }, // IBM i native system libraries
4593 { DRIVE_FIXED
, "QDLS" }, // IBM i legacy S/36 directories
4594 { DRIVE_REMOTE
, "smbfs" },
4595 { DRIVE_REMOTE
, "fuse" },
4596 { DRIVE_REMOTE
, "nfs" },
4597 { DRIVE_REMOTE
, "nfs4" },
4598 { DRIVE_REMOTE
, "cifs" },
4599 { DRIVE_REMOTE
, "ncpfs" },
4600 { DRIVE_REMOTE
, "coda" },
4601 { DRIVE_REMOTE
, "afs" },
4602 { DRIVE_REMOTE
, "nfs3" },
4603 { DRIVE_REMOTE
, "stnfs" }, // AIX "short-term" NFS
4604 { DRIVE_REMOTE
, "autofs" }, // AIX automounter NFS
4605 { DRIVE_REMOTE
, "cachefs" }, // AIX cached NFS
4606 { DRIVE_REMOTE
, "NFS" }, // IBM i NFS
4607 { DRIVE_REMOTE
, "QNETC" }, // IBM i CIFS
4608 { DRIVE_REMOTE
, "QRFS" }, // IBM i native remote FS
4609 { DRIVE_UNKNOWN
, NULL
}
4614 static guint32
_wapi_get_drive_type(unsigned f_type
)
4616 const _wapi_drive_type
*current
;
4618 current
= &_wapi_drive_types
[0];
4619 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4620 if (current
->fstypeid
== f_type
)
4621 return current
->drive_type
;
4625 return DRIVE_UNKNOWN
;
4628 static guint32
_wapi_get_drive_type(const gchar
* fstype
)
4630 const _wapi_drive_type
*current
;
4632 current
= &_wapi_drive_types
[0];
4633 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4634 if (strcmp (current
->fstype
, fstype
) == 0)
4635 return current
->drive_type
;
4640 return DRIVE_UNKNOWN
;
4644 #if defined (HOST_DARWIN) || defined (__linux__) || defined (_AIX)
4646 GetDriveTypeFromPath (const gchar
*utf8_root_path_name
)
4657 res
= statvfs (utf8_root_path_name
, &buf
);
4659 res
= statfs (utf8_root_path_name
, &buf
);
4663 return DRIVE_UNKNOWN
;
4665 return _wapi_get_drive_type (buf
.f_fstypename
);
4666 #elif defined (_AIX)
4667 return _wapi_get_drive_type (buf
.f_basetype
);
4669 return _wapi_get_drive_type (buf
.f_type
);
4674 GetDriveTypeFromPath (const gchar
*utf8_root_path_name
)
4682 fp
= fopen ("/etc/mtab", "rt");
4686 fp
= fopen ("/etc/mnttab", "rt");
4689 return(DRIVE_UNKNOWN
);
4692 drive_type
= DRIVE_NO_ROOT_DIR
;
4696 fgets_res
= fgets (buffer
, 512, fp
);
4698 if (fgets_res
== NULL
)
4700 splitted
= g_strsplit (buffer
, " ", 0);
4701 if (!*splitted
|| !*(splitted
+ 1) || !*(splitted
+ 2)) {
4702 g_strfreev (splitted
);
4706 /* compare given root_path_name with the one from mtab,
4707 if length of utf8_root_path_name is zero it must be the root dir */
4708 if (strcmp (*(splitted
+ 1), utf8_root_path_name
) == 0 ||
4709 (strcmp (*(splitted
+ 1), "/") == 0 && strlen (utf8_root_path_name
) == 0)) {
4710 drive_type
= _wapi_get_drive_type (*(splitted
+ 2));
4711 /* it is possible this path might be mounted again with
4712 a known type...keep looking */
4713 if (drive_type
!= DRIVE_UNKNOWN
) {
4714 g_strfreev (splitted
);
4719 g_strfreev (splitted
);
4729 #ifndef ENABLE_NETCORE
4731 mono_w32file_get_drive_type (const gunichar2
*root_path_name
, gint32 root_path_name_length
, MonoError
*error
)
4733 // FIXME Check for embedded nuls here or in managed.
4735 gchar
*utf8_root_path_name
;
4738 if (root_path_name
== NULL
) {
4739 utf8_root_path_name
= g_strdup (g_get_current_dir());
4740 if (utf8_root_path_name
== NULL
) {
4741 return(DRIVE_NO_ROOT_DIR
);
4745 utf8_root_path_name
= mono_unicode_to_external_checked (root_path_name
, error
);
4746 if (utf8_root_path_name
== NULL
) {
4747 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
4748 return(DRIVE_NO_ROOT_DIR
);
4751 /* strip trailing slash for compare below */
4752 if (g_str_has_suffix(utf8_root_path_name
, "/") && utf8_root_path_name
[1] != 0) {
4753 utf8_root_path_name
[strlen(utf8_root_path_name
) - 1] = 0;
4756 drive_type
= GetDriveTypeFromPath (utf8_root_path_name
);
4757 g_free (utf8_root_path_name
);
4759 return (drive_type
);
4763 #if defined (HOST_DARWIN) || defined (__linux__) || defined(HOST_BSD) || defined(__FreeBSD_kernel__) || defined(__HAIKU__) || defined(_AIX)
4765 get_fstypename (gchar
*utfpath
)
4768 /* statvfs offers the FS type name, easily, no need to iterate */
4769 struct statvfs stat
;
4773 statvfs_res
= statvfs (utfpath
, &stat
);
4776 if (statvfs_res
!= -1) {
4777 return g_strdup (stat
.f_basetype
);
4781 #elif defined (HOST_DARWIN) || defined (__linux__)
4784 const _wapi_drive_type
*current
;
4788 statfs_res
= statfs (utfpath
, &stat
);
4790 if (statfs_res
== -1)
4793 return g_strdup (stat
.f_fstypename
);
4795 current
= &_wapi_drive_types
[0];
4796 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4797 if (stat
.f_type
== current
->fstypeid
)
4798 return g_strdup (current
->fstype
);
4808 /* Linux has struct statfs which has a different layout */
4810 mono_w32file_get_file_system_type (const gunichar2
*path
, gunichar2
*fsbuffer
, gint fsbuffersize
)
4814 gboolean status
= FALSE
;
4817 // We only support getting the file system type
4818 if (fsbuffer
== NULL
)
4821 utfpath
= mono_unicode_to_external (path
);
4822 if ((fstypename
= get_fstypename (utfpath
)) != NULL
){
4823 gunichar2
*ret
= g_utf8_to_utf16 (fstypename
, -1, NULL
, &len
, NULL
);
4824 if (ret
!= NULL
&& len
< fsbuffersize
){
4825 memcpy (fsbuffer
, ret
, len
* sizeof (gunichar2
));
4831 g_free (fstypename
);
4839 LockFile (gpointer handle
, guint32 offset_low
, guint32 offset_high
, guint32 length_low
, guint32 length_high
)
4841 FileHandle
*filehandle
;
4843 off_t offset
, length
;
4845 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
4846 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
4850 if (((MonoFDHandle
*) filehandle
)->type
!= MONO_FDTYPE_FILE
) {
4851 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
4852 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4856 if (!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_ALL
))) {
4857 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
);
4858 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
4859 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4863 #ifdef HAVE_LARGE_FILE_SUPPORT
4864 offset
= ((gint64
)offset_high
<< 32) | offset_low
;
4865 length
= ((gint64
)length_high
<< 32) | length_low
;
4867 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
);
4869 if (offset_high
> 0 || length_high
> 0) {
4870 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
4871 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4875 offset
= offset_low
;
4876 length
= length_low
;
4878 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Locking fd %d, offset %" G_GINT64_FORMAT
", length %" G_GINT64_FORMAT
, __func__
, ((MonoFDHandle
*) filehandle
)->fd
, (gint64
) offset
, (gint64
) length
);
4881 ret
= _wapi_lock_file_region (((MonoFDHandle
*) filehandle
)->fd
, offset
, length
);
4883 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4888 UnlockFile (gpointer handle
, guint32 offset_low
, guint32 offset_high
, guint32 length_low
, guint32 length_high
)
4890 FileHandle
*filehandle
;
4892 off_t offset
, length
;
4894 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
4895 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
4899 if (((MonoFDHandle
*) filehandle
)->type
!= MONO_FDTYPE_FILE
) {
4900 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
4901 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4905 if (!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_ALL
))) {
4906 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
);
4907 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
4908 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4912 #ifdef HAVE_LARGE_FILE_SUPPORT
4913 offset
= ((gint64
)offset_high
<< 32) | offset_low
;
4914 length
= ((gint64
)length_high
<< 32) | length_low
;
4916 offset
= offset_low
;
4917 length
= length_low
;
4919 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
);
4921 ret
= _wapi_unlock_file_region (((MonoFDHandle
*) filehandle
)->fd
, offset
, length
);
4923 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4928 mono_w32file_init (void)
4930 MonoFDHandleCallback file_data_callbacks
;
4931 memset (&file_data_callbacks
, 0, sizeof (file_data_callbacks
));
4932 file_data_callbacks
.close
= file_data_close
;
4933 file_data_callbacks
.destroy
= file_data_destroy
;
4935 mono_fdhandle_register (MONO_FDTYPE_FILE
, &file_data_callbacks
);
4936 mono_fdhandle_register (MONO_FDTYPE_CONSOLE
, &file_data_callbacks
);
4937 mono_fdhandle_register (MONO_FDTYPE_PIPE
, &file_data_callbacks
);
4939 mono_coop_mutex_init (&file_share_mutex
);
4941 finds
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, finds_remove
);
4942 mono_coop_mutex_init (&finds_mutex
);
4945 libc_handle
= mono_dl_open ("/usr/lib/libc.dylib", 0, NULL
);
4946 g_assert (libc_handle
);
4947 g_free (mono_dl_symbol (libc_handle
, "clonefile", (void**)&clonefile_ptr
));
4950 if (g_hasenv ("MONO_STRICT_IO_EMULATION"))
4951 lock_while_writing
= TRUE
;
4955 mono_w32file_cleanup (void)
4957 mono_coop_mutex_destroy (&file_share_mutex
);
4959 if (file_share_table
)
4960 g_hash_table_destroy (file_share_table
);
4962 g_hash_table_destroy (finds
);
4963 mono_coop_mutex_destroy (&finds_mutex
);
4966 mono_dl_close (libc_handle
);
4971 mono_w32file_move (const gunichar2
*path
, const gunichar2
*dest
, gint32
*error
)
4975 result
= MoveFile (path
, dest
);
4977 *error
= mono_w32error_get_last ();
4982 mono_w32file_copy (const gunichar2
*path
, const gunichar2
*dest
, gboolean overwrite
, gint32
*error
)
4986 result
= CopyFile (path
, dest
, !overwrite
);
4988 *error
= mono_w32error_get_last ();
4994 mono_w32file_replace (const gunichar2
*destination_file_name
, const gunichar2
*source_file_name
, const gunichar2
*destination_backup_file_name
, guint32 flags
, gint32
*error
)
4998 result
= ReplaceFile (destination_file_name
, source_file_name
, destination_backup_file_name
, flags
, NULL
, NULL
);
5000 *error
= mono_w32error_get_last ();
5005 mono_w32file_get_file_size (gpointer handle
, gint32
*error
)
5008 guint32 length_hi
= 0;
5010 length
= GetFileSize (handle
, &length_hi
);
5011 if(length
==INVALID_FILE_SIZE
) {
5012 *error
=mono_w32error_get_last ();
5015 return length
| ((gint64
)length_hi
<< 32);
5019 mono_w32file_lock (gpointer handle
, gint64 position
, gint64 length
, gint32
*error
)
5023 result
= LockFile (handle
, position
& 0xFFFFFFFF, position
>> 32, length
& 0xFFFFFFFF, length
>> 32);
5025 *error
= mono_w32error_get_last ();
5030 mono_w32file_unlock (gpointer handle
, gint64 position
, gint64 length
, gint32
*error
)
5034 result
= UnlockFile (handle
, position
& 0xFFFFFFFF, position
>> 32, length
& 0xFFFFFFFF, length
>> 32);
5036 *error
= mono_w32error_get_last ();
5041 mono_w32file_get_console_input (void)
5043 return mono_w32file_get_std_handle (STD_INPUT_HANDLE
);
5047 mono_w32file_get_console_output (void)
5049 return mono_w32file_get_std_handle (STD_OUTPUT_HANDLE
);
5053 mono_w32file_get_console_error (void)
5055 return mono_w32file_get_std_handle (STD_ERROR_HANDLE
);