14 #ifdef HAVE_SYS_STATVFS_H
15 #include <sys/statvfs.h>
17 #if defined(HAVE_SYS_STATFS_H)
18 #include <sys/statfs.h>
20 #if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
21 #include <sys/param.h>
22 #include <sys/mount.h>
24 #include <sys/types.h>
28 #include <sys/ioctl.h>
30 #include <mono/utils/linux_magic.h>
33 #include <sys/mntctl.h>
34 #include <sys/vmount.h>
45 #include "w32file-internals.h"
46 #include "w32file-unix-glob.h"
49 #include "utils/mono-io-portability.h"
50 #include "utils/mono-logger-internals.h"
51 #include "utils/mono-os-mutex.h"
52 #include "utils/mono-threads.h"
53 #include "utils/mono-threads-api.h"
54 #include "utils/strenc.h"
55 #include "utils/refcount.h"
56 #include "icall-decl.h"
58 #define NANOSECONDS_PER_MICROSECOND 1000LL
59 #define TICKS_PER_MICROSECOND 10L
60 #define TICKS_PER_MILLISECOND 10000L
61 #define TICKS_PER_SECOND 10000000LL
62 #define TICKS_PER_MINUTE 600000000LL
63 #define TICKS_PER_HOUR 36000000000LL
64 #define TICKS_PER_DAY 864000000000LL
66 // Constants to convert Unix times to the API expected by .NET and Windows
67 #define CONVERT_BASE 116444736000000000ULL
69 #define INVALID_HANDLE_VALUE ((gpointer)-1)
80 /* Currently used for both FILE, CONSOLE and PIPE handle types.
81 * This may have to change in future. */
83 MonoFDHandle fdhandle
;
85 FileShare
*share_info
; /* Pointer into shared mem */
86 guint32 security_attributes
;
102 * If SHM is disabled, this will point to a hash of FileShare structures, otherwise
103 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
106 static GHashTable
*file_share_table
;
107 static MonoCoopMutex file_share_mutex
;
109 static GHashTable
*finds
;
110 static MonoCoopMutex finds_mutex
;
113 typedef int (*clonefile_fn
) (const char *from
, const char *to
, int flags
);
114 static MonoDl
*libc_handle
;
115 static clonefile_fn clonefile_ptr
;
119 time_t_to_filetime (time_t timeval
, FILETIME
*filetime
)
123 ticks
= ((guint64
)timeval
* 10000000) + CONVERT_BASE
;
124 filetime
->dwLowDateTime
= ticks
& 0xFFFFFFFF;
125 filetime
->dwHighDateTime
= ticks
>> 32;
129 file_data_create (MonoFDType type
, gint fd
)
131 FileHandle
*filehandle
;
133 filehandle
= g_new0 (FileHandle
, 1);
134 mono_fdhandle_init ((MonoFDHandle
*) filehandle
, type
, fd
);
140 _wapi_unlink (const gchar
*pathname
);
143 file_share_release (FileShare
*share_info
);
146 file_data_close (MonoFDHandle
*fdhandle
)
148 FileHandle
* filehandle
;
150 filehandle
= (FileHandle
*) fdhandle
;
151 g_assert (filehandle
);
153 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: closing fd %d", __func__
, ((MonoFDHandle
*) filehandle
)->fd
);
155 if (((MonoFDHandle
*) filehandle
)->type
== MONO_FDTYPE_FILE
&& (filehandle
->attrs
& FILE_FLAG_DELETE_ON_CLOSE
)) {
156 _wapi_unlink (filehandle
->filename
);
159 if (((MonoFDHandle
*) filehandle
)->type
!= MONO_FDTYPE_CONSOLE
|| ((MonoFDHandle
*) filehandle
)->fd
> 2) {
160 if (filehandle
->share_info
) {
161 file_share_release (filehandle
->share_info
);
162 filehandle
->share_info
= NULL
;
166 close (((MonoFDHandle
*) filehandle
)->fd
);
172 file_data_destroy (MonoFDHandle
*fdhandle
)
174 FileHandle
*filehandle
;
176 filehandle
= (FileHandle
*) fdhandle
;
177 g_assert (filehandle
);
179 if (filehandle
->filename
)
180 g_free (filehandle
->filename
);
186 file_share_release (FileShare
*share_info
)
188 /* Prevent new entries racing with us */
189 mono_coop_mutex_lock (&file_share_mutex
);
191 g_assert (share_info
->handle_refs
> 0);
192 share_info
->handle_refs
-= 1;
194 if (share_info
->handle_refs
== 0) {
195 g_hash_table_remove (file_share_table
, share_info
);
196 // g_free (share_info);
199 mono_coop_mutex_unlock (&file_share_mutex
);
203 file_share_equal (gconstpointer ka
, gconstpointer kb
)
205 const FileShare
*s1
= (const FileShare
*)ka
;
206 const FileShare
*s2
= (const FileShare
*)kb
;
208 return (s1
->device
== s2
->device
&& s1
->inode
== s2
->inode
) ? 1 : 0;
212 file_share_hash (gconstpointer data
)
214 const FileShare
*s
= (const FileShare
*)data
;
220 file_share_get (guint64 device
, guint64 inode
, guint32 new_sharemode
, guint32 new_access
,
221 guint32
*old_sharemode
, guint32
*old_access
, FileShare
**share_info
)
223 FileShare
*file_share
;
224 gboolean exists
= FALSE
;
226 /* Prevent new entries racing with us */
227 mono_coop_mutex_lock (&file_share_mutex
);
232 * Instead of allocating a 4MB array, we use a hash table to keep track of this
233 * info. This is needed even if SHM is disabled, to track sharing inside
234 * the current process.
236 if (!file_share_table
)
237 file_share_table
= g_hash_table_new_full (file_share_hash
, file_share_equal
, NULL
, g_free
);
242 file_share
= (FileShare
*)g_hash_table_lookup (file_share_table
, &tmp
);
244 *old_sharemode
= file_share
->sharemode
;
245 *old_access
= file_share
->access
;
246 *share_info
= file_share
;
248 g_assert (file_share
->handle_refs
> 0);
249 file_share
->handle_refs
+= 1;
253 file_share
= g_new0 (FileShare
, 1);
255 file_share
->device
= device
;
256 file_share
->inode
= inode
;
257 file_share
->sharemode
= new_sharemode
;
258 file_share
->access
= new_access
;
259 file_share
->handle_refs
= 1;
260 *share_info
= file_share
;
262 g_hash_table_insert (file_share_table
, file_share
, file_share
);
265 mono_coop_mutex_unlock (&file_share_mutex
);
271 _wapi_open (const gchar
*pathname
, gint flags
, mode_t mode
)
274 gchar
*located_filename
;
276 if (flags
& O_CREAT
) {
277 located_filename
= mono_portability_find_file (pathname
, FALSE
);
278 if (located_filename
== NULL
) {
280 fd
= open (pathname
, flags
, mode
);
284 fd
= open (located_filename
, flags
, mode
);
286 g_free (located_filename
);
290 fd
= open (pathname
, flags
, mode
);
292 if (fd
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
293 gint saved_errno
= errno
;
294 located_filename
= mono_portability_find_file (pathname
, TRUE
);
296 if (located_filename
== NULL
) {
302 fd
= open (located_filename
, flags
, mode
);
304 g_free (located_filename
);
312 _wapi_access (const gchar
*pathname
, gint mode
)
317 ret
= access (pathname
, mode
);
319 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
320 gint saved_errno
= errno
;
321 gchar
*located_filename
= mono_portability_find_file (pathname
, TRUE
);
323 if (located_filename
== NULL
) {
329 ret
= access (located_filename
, mode
);
331 g_free (located_filename
);
338 _wapi_chmod (const gchar
*pathname
, mode_t mode
)
343 ret
= chmod (pathname
, mode
);
345 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
346 gint saved_errno
= errno
;
347 gchar
*located_filename
= mono_portability_find_file (pathname
, TRUE
);
349 if (located_filename
== NULL
) {
355 ret
= chmod (located_filename
, mode
);
357 g_free (located_filename
);
363 #ifndef HAVE_STRUCT_TIMEVAL
365 _wapi_utime (const gchar
*filename
, const struct utimbuf
*buf
)
370 ret
= utime (filename
, buf
);
372 if (ret
== -1 && errno
== ENOENT
&& IS_PORTABILITY_SET
) {
373 gint saved_errno
= errno
;
374 gchar
*located_filename
= mono_portability_find_file (filename
, TRUE
);
376 if (located_filename
== NULL
) {
382 ret
= utime (located_filename
, buf
);
384 g_free (located_filename
);
392 _wapi_utimes (const gchar
*filename
, const struct timeval times
[2])
397 ret
= utimes (filename
, times
);
399 if (ret
== -1 && errno
== ENOENT
&& IS_PORTABILITY_SET
) {
400 gint saved_errno
= errno
;
401 gchar
*located_filename
= mono_portability_find_file (filename
, TRUE
);
403 if (located_filename
== NULL
) {
409 ret
= utimes (located_filename
, times
);
411 g_free (located_filename
);
419 _wapi_unlink (const gchar
*pathname
)
424 ret
= unlink (pathname
);
426 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
|| errno
== EISDIR
) && IS_PORTABILITY_SET
) {
427 gint saved_errno
= errno
;
428 gchar
*located_filename
= mono_portability_find_file (pathname
, TRUE
);
430 if (located_filename
== NULL
) {
436 ret
= unlink (located_filename
);
438 g_free (located_filename
);
445 _wapi_rename (const gchar
*oldpath
, const gchar
*newpath
)
448 gchar
*located_newpath
= mono_portability_find_file (newpath
, FALSE
);
450 if (located_newpath
== NULL
) {
452 ret
= rename (oldpath
, newpath
);
456 ret
= rename (oldpath
, located_newpath
);
459 if (ret
== -1 && (errno
== EISDIR
|| errno
== ENAMETOOLONG
|| errno
== ENOENT
|| errno
== ENOTDIR
|| errno
== EXDEV
) && IS_PORTABILITY_SET
) {
460 gint saved_errno
= errno
;
461 gchar
*located_oldpath
= mono_portability_find_file (oldpath
, TRUE
);
463 if (located_oldpath
== NULL
) {
464 g_free (located_oldpath
);
465 g_free (located_newpath
);
472 ret
= rename (located_oldpath
, located_newpath
);
474 g_free (located_oldpath
);
476 g_free (located_newpath
);
483 _wapi_stat (const gchar
*path
, struct stat
*buf
)
488 ret
= stat (path
, buf
);
490 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
491 gint saved_errno
= errno
;
492 gchar
*located_filename
= mono_portability_find_file (path
, TRUE
);
494 if (located_filename
== NULL
) {
500 ret
= stat (located_filename
, buf
);
502 g_free (located_filename
);
509 _wapi_lstat (const gchar
*path
, struct stat
*buf
)
514 ret
= lstat (path
, buf
);
516 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
517 gint saved_errno
= errno
;
518 gchar
*located_filename
= mono_portability_find_file (path
, TRUE
);
520 if (located_filename
== NULL
) {
525 ret
= lstat (located_filename
, buf
);
526 g_free (located_filename
);
533 _wapi_mkdir (const gchar
*pathname
, mode_t mode
)
536 gchar
*located_filename
= mono_portability_find_file (pathname
, FALSE
);
538 if (located_filename
== NULL
) {
540 ret
= mkdir (pathname
, mode
);
544 ret
= mkdir (located_filename
, mode
);
546 g_free (located_filename
);
553 _wapi_rmdir (const gchar
*pathname
)
558 ret
= rmdir (pathname
);
560 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
|| errno
== ENAMETOOLONG
) && IS_PORTABILITY_SET
) {
561 gint saved_errno
= errno
;
562 gchar
*located_filename
= mono_portability_find_file (pathname
, TRUE
);
564 if (located_filename
== NULL
) {
570 ret
= rmdir (located_filename
);
572 g_free (located_filename
);
579 _wapi_chdir (const gchar
*path
)
586 if (ret
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
|| errno
== ENAMETOOLONG
) && IS_PORTABILITY_SET
) {
587 gint saved_errno
= errno
;
588 gchar
*located_filename
= mono_portability_find_file (path
, TRUE
);
590 if (located_filename
== NULL
) {
596 ret
= chdir (located_filename
);
598 g_free (located_filename
);
605 _wapi_basename (const gchar
*filename
)
607 gchar
*new_filename
= g_strdup (filename
), *ret
;
609 if (IS_PORTABILITY_SET
)
610 g_strdelimit (new_filename
, '\\', '/');
612 if (IS_PORTABILITY_DRIVE
&& g_ascii_isalpha (new_filename
[0]) && (new_filename
[1] == ':')) {
613 gint len
= strlen (new_filename
);
615 g_memmove (new_filename
, new_filename
+ 2, len
- 2);
616 new_filename
[len
- 2] = '\0';
619 ret
= g_path_get_basename (new_filename
);
620 g_free (new_filename
);
626 _wapi_dirname (const gchar
*filename
)
628 gchar
*new_filename
= g_strdup (filename
), *ret
;
630 if (IS_PORTABILITY_SET
)
631 g_strdelimit (new_filename
, '\\', '/');
633 if (IS_PORTABILITY_DRIVE
&& g_ascii_isalpha (new_filename
[0]) && (new_filename
[1] == ':')) {
634 gint len
= strlen (new_filename
);
636 g_memmove (new_filename
, new_filename
+ 2, len
- 2);
637 new_filename
[len
- 2] = '\0';
640 ret
= g_path_get_dirname (new_filename
);
641 g_free (new_filename
);
647 _wapi_g_dir_open (const gchar
*path
, guint flags
, GError
**gerror
)
652 ret
= g_dir_open (path
, flags
, gerror
);
654 if (ret
== NULL
&& ((*gerror
)->code
== G_FILE_ERROR_NOENT
|| (*gerror
)->code
== G_FILE_ERROR_NOTDIR
|| (*gerror
)->code
== G_FILE_ERROR_NAMETOOLONG
) && IS_PORTABILITY_SET
) {
655 gchar
*located_filename
= mono_portability_find_file (path
, TRUE
);
656 GError
*tmp_error
= NULL
;
658 if (located_filename
== NULL
) {
663 ret
= g_dir_open (located_filename
, flags
, &tmp_error
);
665 g_free (located_filename
);
666 if (tmp_error
== NULL
) {
667 g_clear_error (gerror
);
675 get_errno_from_g_file_error (gint error
)
679 case G_FILE_ERROR_ACCES
: return EACCES
;
682 case G_FILE_ERROR_NAMETOOLONG
: return ENAMETOOLONG
;
685 case G_FILE_ERROR_NOENT
: return ENOENT
;
688 case G_FILE_ERROR_NOTDIR
: return ENOTDIR
;
691 case G_FILE_ERROR_NXIO
: return ENXIO
;
694 case G_FILE_ERROR_NODEV
: return ENODEV
;
697 case G_FILE_ERROR_ROFS
: return EROFS
;
700 case G_FILE_ERROR_TXTBSY
: return ETXTBSY
;
703 case G_FILE_ERROR_FAULT
: return EFAULT
;
706 case G_FILE_ERROR_LOOP
: return ELOOP
;
709 case G_FILE_ERROR_NOSPC
: return ENOSPC
;
712 case G_FILE_ERROR_NOMEM
: return ENOMEM
;
715 case G_FILE_ERROR_MFILE
: return EMFILE
;
718 case G_FILE_ERROR_NFILE
: return ENFILE
;
721 case G_FILE_ERROR_BADF
: return EBADF
;
724 case G_FILE_ERROR_INVAL
: return EINVAL
;
727 case G_FILE_ERROR_PIPE
: return EPIPE
;
730 case G_FILE_ERROR_AGAIN
: return EAGAIN
;
733 case G_FILE_ERROR_INTR
: return EINTR
;
736 case G_FILE_ERROR_IO
: return EIO
;
739 case G_FILE_ERROR_PERM
: return EPERM
;
741 case G_FILE_ERROR_FAILED
: return ERROR_INVALID_PARAMETER
;
743 g_assert_not_reached ();
748 file_compare (gconstpointer a
, gconstpointer b
)
750 gchar
*astr
= *(gchar
**) a
;
751 gchar
*bstr
= *(gchar
**) b
;
753 return strcmp (astr
, bstr
);
756 /* scandir using glib */
758 _wapi_io_scandir (const gchar
*dirname
, const gchar
*pattern
, gchar
***namelist
)
760 GError
*gerror
= NULL
;
764 mono_w32file_unix_glob_t glob_buf
;
767 dir
= _wapi_g_dir_open (dirname
, 0, &gerror
);
769 /* g_dir_open returns ENOENT on directories on which we don't
770 * have read/x permission */
771 gint errnum
= get_errno_from_g_file_error (gerror
->code
);
772 g_error_free (gerror
);
773 if (errnum
== ENOENT
&&
774 !_wapi_access (dirname
, F_OK
) &&
775 _wapi_access (dirname
, R_OK
|X_OK
)) {
783 if (IS_PORTABILITY_CASE
) {
784 flags
= W32FILE_UNIX_GLOB_IGNORECASE
;
787 result
= mono_w32file_unix_glob (dir
, pattern
, flags
, &glob_buf
);
788 if (g_str_has_suffix (pattern
, ".*")) {
789 /* Special-case the patterns ending in '.*', as
790 * windows also matches entries with no extension with
793 * TODO: should this be a MONO_IOMAP option?
795 gchar
*pattern2
= g_strndup (pattern
, strlen (pattern
) - 2);
801 result2
= mono_w32file_unix_glob (dir
, pattern2
, flags
| W32FILE_UNIX_GLOB_APPEND
| W32FILE_UNIX_GLOB_UNIQUE
, &glob_buf
);
813 if (glob_buf
.gl_pathc
== 0) {
815 } else if (result
!= 0) {
819 names
= g_ptr_array_new ();
820 for (i
= 0; i
< glob_buf
.gl_pathc
; i
++) {
821 g_ptr_array_add (names
, g_strdup (glob_buf
.gl_pathv
[i
]));
824 mono_w32file_unix_globfree (&glob_buf
);
828 g_ptr_array_sort (names
, file_compare
);
829 g_ptr_array_set_size (names
, result
+ 1);
831 *namelist
= (gchar
**) g_ptr_array_free (names
, FALSE
);
833 g_ptr_array_free (names
, TRUE
);
840 _wapi_lock_file_region (gint fd
, off_t offset
, off_t length
)
842 struct flock lock_data
;
845 if (offset
< 0 || length
< 0) {
846 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
850 lock_data
.l_type
= F_WRLCK
;
851 lock_data
.l_whence
= SEEK_SET
;
852 lock_data
.l_start
= offset
;
853 lock_data
.l_len
= length
;
856 ret
= fcntl (fd
, F_SETLK
, &lock_data
);
857 } while(ret
== -1 && errno
== EINTR
);
859 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fcntl returns %d", __func__
, ret
);
863 * if locks are not available (NFS for example),
868 || errno
== EOPNOTSUPP
877 mono_w32error_set_last (ERROR_LOCK_VIOLATION
);
885 _wapi_unlock_file_region (gint fd
, off_t offset
, off_t length
)
887 struct flock lock_data
;
890 lock_data
.l_type
= F_UNLCK
;
891 lock_data
.l_whence
= SEEK_SET
;
892 lock_data
.l_start
= offset
;
893 lock_data
.l_len
= length
;
896 ret
= fcntl (fd
, F_SETLK
, &lock_data
);
897 } while(ret
== -1 && errno
== EINTR
);
899 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fcntl returns %d", __func__
, ret
);
903 * if locks are not available (NFS for example),
908 || errno
== EOPNOTSUPP
917 mono_w32error_set_last (ERROR_LOCK_VIOLATION
);
924 static gboolean lock_while_writing
= FALSE
;
926 /* Some utility functions.
930 * Check if a file is writable by the current user.
932 * This is is a best effort kind of thing. It assumes a reasonable sane set
933 * of permissions by the underlying OS.
935 * We generally assume that basic unix permission bits are authoritative. Which might not
936 * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc)
938 * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file.
940 * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent
941 * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous
942 * and should not be used with a dynamic runtime.
945 is_file_writable (struct stat
*st
, const gchar
*path
)
951 // OS X Finder "locked" or `ls -lO` "uchg".
952 // This only covers one of several cases where an OS X file could be unwritable through special flags.
953 if (st
->st_flags
& (UF_IMMUTABLE
|SF_IMMUTABLE
))
957 /* Is it globally writable? */
958 if (st
->st_mode
& S_IWOTH
)
961 /* Am I the owner? */
962 if ((st
->st_uid
== geteuid ()) && (st
->st_mode
& S_IWUSR
))
965 /* Am I in the same group? */
966 if ((st
->st_gid
== getegid ()) && (st
->st_mode
& S_IWGRP
))
969 located_path
= mono_portability_find_file (path
, FALSE
);
971 /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid
972 * but it's the only sane option we have on unix.
975 ret
= access (located_path
!= NULL
? located_path
: path
, W_OK
) == 0;
978 g_free (located_path
);
984 static guint32
_wapi_stat_to_file_attributes (const gchar
*pathname
,
991 /* FIXME: this could definitely be better, but there seems to
992 * be no pattern to the attributes that are set
995 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
996 if (S_ISSOCK (buf
->st_mode
))
997 buf
->st_mode
&= ~S_IFSOCK
; /* don't consider socket protection */
999 filename
= _wapi_basename (pathname
);
1001 if (S_ISDIR (buf
->st_mode
)) {
1002 attrs
= FILE_ATTRIBUTE_DIRECTORY
;
1003 if (!is_file_writable (buf
, pathname
)) {
1004 attrs
|= FILE_ATTRIBUTE_READONLY
;
1006 if (filename
[0] == '.') {
1007 attrs
|= FILE_ATTRIBUTE_HIDDEN
;
1010 if (!is_file_writable (buf
, pathname
)) {
1011 attrs
= FILE_ATTRIBUTE_READONLY
;
1013 if (filename
[0] == '.') {
1014 attrs
|= FILE_ATTRIBUTE_HIDDEN
;
1016 } else if (filename
[0] == '.') {
1017 attrs
= FILE_ATTRIBUTE_HIDDEN
;
1019 attrs
= FILE_ATTRIBUTE_NORMAL
;
1024 if (S_ISLNK (lbuf
->st_mode
)) {
1025 attrs
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1035 _wapi_set_last_error_from_errno (void)
1037 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
1040 static void _wapi_set_last_path_error_from_errno (const gchar
*dir
,
1043 if (errno
== ENOENT
) {
1044 /* Check the path - if it's a missing directory then
1045 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
1051 dirname
= _wapi_dirname (path
);
1053 dirname
= g_strdup (dir
);
1056 if (_wapi_access (dirname
, F_OK
) == 0) {
1057 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1059 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1064 _wapi_set_last_error_from_errno ();
1069 file_read(FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
)
1072 MonoThreadInfo
*info
= mono_thread_info_current ();
1074 if(bytesread
!=NULL
) {
1078 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_ALL
))) {
1079 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1081 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1087 ret
= read (((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1089 } while (ret
== -1 && errno
== EINTR
&&
1090 !mono_thread_info_is_interrupt_state (info
));
1095 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: read of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(err
));
1096 mono_w32error_set_last (mono_w32error_unix_to_win32 (err
));
1100 if (bytesread
!= NULL
) {
1108 file_write (FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*byteswritten
)
1111 off_t current_pos
= 0;
1112 MonoThreadInfo
*info
= mono_thread_info_current ();
1114 if(byteswritten
!=NULL
) {
1118 if(!(filehandle
->fileaccess
& GENERIC_WRITE
) && !(filehandle
->fileaccess
& GENERIC_ALL
)) {
1119 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1121 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1125 if (lock_while_writing
) {
1126 /* Need to lock the region we're about to write to,
1127 * because we only do advisory locking on POSIX
1131 current_pos
= lseek (((MonoFDHandle
*) filehandle
)->fd
, (off_t
)0, SEEK_CUR
);
1133 if (current_pos
== -1) {
1134 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d lseek failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror (errno
));
1135 _wapi_set_last_error_from_errno ();
1139 if (_wapi_lock_file_region (((MonoFDHandle
*) filehandle
)->fd
, current_pos
, numbytes
) == FALSE
) {
1140 /* The error has already been set */
1147 ret
= write (((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1149 } while (ret
== -1 && errno
== EINTR
&&
1150 !mono_thread_info_is_interrupt_state (info
));
1152 if (lock_while_writing
) {
1153 _wapi_unlock_file_region (((MonoFDHandle
*) filehandle
)->fd
, current_pos
, numbytes
);
1157 if (errno
== EINTR
) {
1160 _wapi_set_last_error_from_errno ();
1162 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: write of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1167 if (byteswritten
!= NULL
) {
1168 *byteswritten
= ret
;
1173 static gboolean
file_flush(FileHandle
*filehandle
)
1177 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1178 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1180 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1185 ret
=fsync(((MonoFDHandle
*) filehandle
)->fd
);
1188 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fsync of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1190 _wapi_set_last_error_from_errno ();
1197 static guint32
file_seek(FileHandle
*filehandle
, gint32 movedistance
,
1198 gint32
*highmovedistance
, gint method
)
1200 gint64 offset
, newpos
;
1204 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_ALL
))) {
1205 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1207 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1208 return(INVALID_SET_FILE_POINTER
);
1222 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: invalid seek type %d", __func__
, method
);
1224 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1225 return(INVALID_SET_FILE_POINTER
);
1228 #ifdef HAVE_LARGE_FILE_SUPPORT
1229 if(highmovedistance
==NULL
) {
1230 offset
=movedistance
;
1231 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: setting offset to %" G_GINT64_FORMAT
" (low %" G_GINT32_FORMAT
")", __func__
,
1232 offset
, movedistance
);
1234 offset
=((gint64
) *highmovedistance
<< 32) | (guint32
)movedistance
;
1236 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: setting offset to %" G_GINT64_FORMAT
" 0x%" PRIx64
" (high %" G_GINT32_FORMAT
" 0x%" PRIx32
", low %" G_GINT32_FORMAT
" 0x%" PRIx32
")",
1237 __func__
, offset
, offset
, *highmovedistance
, *highmovedistance
, movedistance
, movedistance
);
1240 offset
=movedistance
;
1243 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: moving fd %d by %" G_GINT64_FORMAT
" bytes from %d", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, offset
, whence
);
1246 /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */
1248 newpos
=lseek64(((MonoFDHandle
*) filehandle
)->fd
, offset
, whence
);
1252 newpos
=lseek(((MonoFDHandle
*) filehandle
)->fd
, offset
, whence
);
1256 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: lseek on fd %d returned error %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1258 _wapi_set_last_error_from_errno ();
1259 return(INVALID_SET_FILE_POINTER
);
1262 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: lseek returns %" G_GINT64_FORMAT
, __func__
, newpos
);
1264 #ifdef HAVE_LARGE_FILE_SUPPORT
1265 ret
=newpos
& 0xFFFFFFFF;
1266 if(highmovedistance
!=NULL
) {
1267 *highmovedistance
=newpos
>>32;
1271 if(highmovedistance
!=NULL
) {
1272 /* Accurate, but potentially dodgy :-) */
1273 *highmovedistance
=0;
1277 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: move of fd %d returning %" G_GUINT32_FORMAT
"/%" G_GINT32_FORMAT
, __func__
, ((MonoFDHandle
*) filehandle
)->fd
, ret
, highmovedistance
==NULL
?0:*highmovedistance
);
1282 static gboolean
file_setendoffile(FileHandle
*filehandle
)
1284 struct stat statbuf
;
1287 MonoThreadInfo
*info
= mono_thread_info_current ();
1289 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1290 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1292 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1296 /* Find the current file position, and the file length. If
1297 * the file position is greater than the length, write to
1298 * extend the file with a hole. If the file position is less
1299 * than the length, truncate the file.
1303 ret
=fstat(((MonoFDHandle
*) filehandle
)->fd
, &statbuf
);
1306 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d fstat failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1308 _wapi_set_last_error_from_errno ();
1313 pos
=lseek(((MonoFDHandle
*) filehandle
)->fd
, (off_t
)0, SEEK_CUR
);
1316 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d lseek failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1318 _wapi_set_last_error_from_errno ();
1322 #ifdef FTRUNCATE_DOESNT_EXTEND
1323 off_t size
= statbuf
.st_size
;
1324 /* I haven't bothered to write the configure.ac stuff for this
1325 * because I don't know if any platform needs it. I'm leaving
1326 * this code just in case though
1329 /* Extend the file. Use write() here, because some
1330 * manuals say that ftruncate() behaviour is undefined
1331 * when the file needs extending. The POSIX spec says
1332 * that on XSI-conformant systems it extends, so if
1333 * every system we care about conforms, then we can
1338 ret
= write (((MonoFDHandle
*) filehandle
)->fd
, "", 1);
1340 } while (ret
== -1 && errno
== EINTR
&&
1341 !mono_thread_info_is_interrupt_state (info
));
1344 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d extend write failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1346 _wapi_set_last_error_from_errno ();
1350 /* And put the file position back after the write */
1352 ret
= lseek (((MonoFDHandle
*) filehandle
)->fd
, pos
, SEEK_SET
);
1355 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d second lseek failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1357 _wapi_set_last_error_from_errno ();
1363 /* always truncate, because the extend write() adds an extra
1364 * byte to the end of the file
1368 ret
=ftruncate(((MonoFDHandle
*) filehandle
)->fd
, pos
);
1371 while (ret
==-1 && errno
==EINTR
&& !mono_thread_info_is_interrupt_state (info
));
1373 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d ftruncate failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1375 _wapi_set_last_error_from_errno ();
1382 static guint32
file_getfilesize(FileHandle
*filehandle
, guint32
*highsize
)
1384 struct stat statbuf
;
1388 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_ALL
))) {
1389 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1391 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1392 return(INVALID_FILE_SIZE
);
1395 /* If the file has a size with the low bits 0xFFFFFFFF the
1396 * caller can't tell if this is an error, so clear the error
1399 mono_w32error_set_last (ERROR_SUCCESS
);
1402 ret
= fstat(((MonoFDHandle
*) filehandle
)->fd
, &statbuf
);
1405 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d fstat failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1407 _wapi_set_last_error_from_errno ();
1408 return(INVALID_FILE_SIZE
);
1411 /* fstat indicates block devices as zero-length, so go a different path */
1413 if (S_ISBLK(statbuf
.st_mode
)) {
1417 res
= ioctl (((MonoFDHandle
*) filehandle
)->fd
, BLKGETSIZE64
, &bigsize
);
1420 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d ioctl BLKGETSIZE64 failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1422 _wapi_set_last_error_from_errno ();
1423 return(INVALID_FILE_SIZE
);
1426 size
= bigsize
& 0xFFFFFFFF;
1427 if (highsize
!= NULL
) {
1428 *highsize
= bigsize
>>32;
1431 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Returning block device size %" G_GUINT32_FORMAT
"/%" G_GUINT32_FORMAT
,
1432 __func__
, size
, *highsize
);
1438 #ifdef HAVE_LARGE_FILE_SUPPORT
1439 size
= statbuf
.st_size
& 0xFFFFFFFF;
1440 if (highsize
!= NULL
) {
1441 *highsize
= statbuf
.st_size
>>32;
1444 if (highsize
!= NULL
) {
1445 /* Accurate, but potentially dodgy :-) */
1448 size
= statbuf
.st_size
;
1451 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Returning size %" G_GUINT32_FORMAT
"/%" G_GUINT32_FORMAT
, __func__
, size
, *highsize
);
1457 convert_unix_filetime_ms (const FILETIME
*file_time
, const char *ttype
)
1459 guint64 t
= ((guint64
) file_time
->dwHighDateTime
<< 32) + file_time
->dwLowDateTime
;
1461 /* This is (time_t)0. We can actually go to INT_MIN,
1462 * but this will do for now.
1464 if (t
< CONVERT_BASE
) {
1465 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: attempt to set %s time too early", __func__
, ttype
);
1466 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1470 return t
- CONVERT_BASE
;
1473 #ifndef HAVE_STRUCT_TIMEVAL
1475 convert_unix_filetime (const FILETIME
*file_time
, size_t field_size
, const char *ttype
)
1477 guint64 t
= convert_unix_filetime_ms (file_time
, ttype
);
1479 if (field_size
== 4 && (t
/ 10000000) > INT_MAX
) {
1480 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: attempt to set %s time that is too big for a 32bits time_t", __func__
, ttype
);
1481 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1485 return t
/ 10000000;
1489 static void convert_stattime_access_to_timeval (struct timeval
*dest
, struct stat
*statbuf
)
1491 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
1492 dest
->tv_sec
= statbuf
->st_atimespec
.tv_sec
;
1493 dest
->tv_usec
= statbuf
->st_atimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
;
1495 dest
->tv_sec
= statbuf
->st_atime
;
1496 #if HAVE_STRUCT_STAT_ST_ATIM
1497 dest
->tv_usec
= statbuf
->st_atim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
;
1502 static void convert_stattime_mod_to_timeval (struct timeval
*dest
, struct stat
*statbuf
)
1504 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
1505 dest
->tv_sec
= statbuf
->st_mtimespec
.tv_sec
;
1506 dest
->tv_usec
= statbuf
->st_mtimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
;
1508 dest
->tv_sec
= statbuf
->st_mtime
;
1509 #if HAVE_STRUCT_STAT_ST_ATIM
1510 dest
->tv_usec
= statbuf
->st_mtim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
;
1515 static gboolean
file_setfiletime(FileHandle
*filehandle
,
1516 const FILETIME
*create_time G_GNUC_UNUSED
,
1517 const FILETIME
*access_time
,
1518 const FILETIME
*write_time
)
1520 struct stat statbuf
;
1523 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1524 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1526 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1530 if(filehandle
->filename
== NULL
) {
1531 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d unknown filename", __func__
, ((MonoFDHandle
*) filehandle
)->fd
);
1533 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
1537 /* Get the current times, so we can put the same times back in
1538 * the event that one of the FileTime structs is NULL
1541 ret
=fstat (((MonoFDHandle
*) filehandle
)->fd
, &statbuf
);
1544 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d fstat failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1546 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1550 #ifdef HAVE_STRUCT_TIMEVAL
1551 struct timeval times
[2];
1552 memset (times
, 0, sizeof (times
));
1555 guint64 actime
= convert_unix_filetime_ms (access_time
, "access");
1556 times
[0].tv_sec
= actime
/ TICKS_PER_SECOND
;
1557 times
[0].tv_usec
= (actime
% TICKS_PER_SECOND
) / TICKS_PER_MICROSECOND
;
1559 convert_stattime_access_to_timeval (×
[0], &statbuf
);
1563 guint64 wtime
= convert_unix_filetime_ms (write_time
, "write");
1564 times
[1].tv_sec
= wtime
/ TICKS_PER_SECOND
;
1565 times
[1].tv_usec
= (wtime
% TICKS_PER_SECOND
) / TICKS_PER_MICROSECOND
;
1567 convert_stattime_mod_to_timeval (×
[1], &statbuf
);
1570 ret
= _wapi_utimes (filehandle
->filename
, times
);
1572 struct utimbuf utbuf
;
1573 utbuf
.actime
= access_time
? convert_unix_filetime (access_time
, sizeof (utbuf
.actime
), "access") : statbuf
.st_atime
;
1574 utbuf
.modtime
= write_time
? convert_unix_filetime (write_time
, sizeof (utbuf
.modtime
), "write") : statbuf
.st_mtime
;
1576 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: setting fd %d access %ld write %ld", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, utbuf
.actime
, utbuf
.modtime
);
1578 ret
= _wapi_utime (filehandle
->filename
, &utbuf
);
1581 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d [%s] utime failed: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->filename
, g_strerror(errno
));
1583 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
1591 console_read(FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
)
1594 MonoThreadInfo
*info
= mono_thread_info_current ();
1596 if(bytesread
!=NULL
) {
1600 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_ALL
))) {
1601 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1603 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1609 ret
=read(((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1611 } while (ret
==-1 && errno
==EINTR
&& !mono_thread_info_is_interrupt_state (info
));
1614 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: read of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1616 _wapi_set_last_error_from_errno ();
1620 if(bytesread
!=NULL
) {
1628 console_write (FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*byteswritten
)
1631 MonoThreadInfo
*info
= mono_thread_info_current ();
1633 if(byteswritten
!=NULL
) {
1637 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1638 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1640 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1646 ret
= write(((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1648 } while (ret
== -1 && errno
== EINTR
&&
1649 !mono_thread_info_is_interrupt_state (info
));
1652 if (errno
== EINTR
) {
1655 _wapi_set_last_error_from_errno ();
1657 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: write of fd %d error: %s", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1662 if(byteswritten
!=NULL
) {
1670 pipe_read (FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
)
1673 MonoThreadInfo
*info
= mono_thread_info_current ();
1675 if(bytesread
!=NULL
) {
1679 if(!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_ALL
))) {
1680 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1682 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1686 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: reading up to %" G_GUINT32_FORMAT
" bytes from pipe %d", __func__
, numbytes
, ((MonoFDHandle
*) filehandle
)->fd
);
1690 ret
=read(((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1692 } while (ret
==-1 && errno
==EINTR
&& !mono_thread_info_is_interrupt_state (info
));
1695 if (errno
== EINTR
) {
1698 _wapi_set_last_error_from_errno ();
1700 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: read of fd %d error: %s", __func__
,((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1706 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: read %d bytes from pipe %d", __func__
, ret
, ((MonoFDHandle
*) filehandle
)->fd
);
1708 if(bytesread
!=NULL
) {
1716 pipe_write (FileHandle
*filehandle
, gpointer buffer
, guint32 numbytes
, guint32
*byteswritten
)
1719 MonoThreadInfo
*info
= mono_thread_info_current ();
1721 if(byteswritten
!=NULL
) {
1725 if(!(filehandle
->fileaccess
& (GENERIC_WRITE
| GENERIC_ALL
))) {
1726 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
1728 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1732 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: writing up to %" G_GUINT32_FORMAT
" bytes to pipe %d", __func__
, numbytes
, ((MonoFDHandle
*) filehandle
)->fd
);
1736 ret
= write (((MonoFDHandle
*) filehandle
)->fd
, buffer
, numbytes
);
1738 } while (ret
== -1 && errno
== EINTR
&&
1739 !mono_thread_info_is_interrupt_state (info
));
1742 if (errno
== EINTR
) {
1745 _wapi_set_last_error_from_errno ();
1747 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: write of fd %d error: %s", __func__
,((MonoFDHandle
*) filehandle
)->fd
, g_strerror(errno
));
1752 if(byteswritten
!=NULL
) {
1759 static gint
convert_flags(guint32 fileaccess
, guint32 createmode
)
1763 switch(fileaccess
) {
1770 case GENERIC_READ
|GENERIC_WRITE
:
1774 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Unknown access type 0x%" PRIx32
, __func__
,
1779 switch(createmode
) {
1781 flags
|=O_CREAT
|O_EXCL
;
1784 flags
|=O_CREAT
|O_TRUNC
;
1791 case TRUNCATE_EXISTING
:
1795 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Unknown create mode 0x%" PRIx32
, __func__
,
1804 static mode_t
convert_perms(guint32 sharemode
)
1808 if(sharemode
&FILE_SHARE_READ
) {
1811 if(sharemode
&FILE_SHARE_WRITE
) {
1819 static gboolean
share_allows_open (struct stat
*statbuf
, guint32 sharemode
,
1821 FileShare
**share_info
)
1823 gboolean file_already_shared
;
1824 guint32 file_existing_share
, file_existing_access
;
1826 file_already_shared
= file_share_get (statbuf
->st_dev
, statbuf
->st_ino
, sharemode
, fileaccess
, &file_existing_share
, &file_existing_access
, share_info
);
1828 if (file_already_shared
) {
1829 /* The reference to this share info was incremented
1830 * when we looked it up, so be careful to put it back
1831 * if we conclude we can't use this file.
1833 if (file_existing_share
== 0) {
1834 /* Quick and easy, no possibility to share */
1835 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Share mode prevents open: requested access: 0x%" PRIx32
", file has sharing = NONE", __func__
, fileaccess
);
1837 file_share_release (*share_info
);
1843 if (((file_existing_share
== FILE_SHARE_READ
) &&
1844 (fileaccess
!= GENERIC_READ
)) ||
1845 ((file_existing_share
== FILE_SHARE_WRITE
) &&
1846 (fileaccess
!= GENERIC_WRITE
))) {
1847 /* New access mode doesn't match up */
1848 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Share mode prevents open: requested access: 0x%" PRIx32
", file has sharing: 0x%" PRIx32
, __func__
, fileaccess
, file_existing_share
);
1850 file_share_release (*share_info
);
1856 if (((file_existing_access
& GENERIC_READ
) &&
1857 !(sharemode
& FILE_SHARE_READ
)) ||
1858 ((file_existing_access
& GENERIC_WRITE
) &&
1859 !(sharemode
& FILE_SHARE_WRITE
))) {
1860 /* New share mode doesn't match up */
1861 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Access mode prevents open: requested share: 0x%" PRIx32
", file has access: 0x%" PRIx32
, __func__
, sharemode
, file_existing_access
);
1863 file_share_release (*share_info
);
1869 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: New file!", __func__
);
1877 share_allows_delete (struct stat
*statbuf
, FileShare
**share_info
)
1879 gboolean file_already_shared
;
1880 guint32 file_existing_share
, file_existing_access
;
1882 file_already_shared
= file_share_get (statbuf
->st_dev
, statbuf
->st_ino
, FILE_SHARE_DELETE
, GENERIC_READ
, &file_existing_share
, &file_existing_access
, share_info
);
1884 if (file_already_shared
) {
1885 /* The reference to this share info was incremented
1886 * when we looked it up, so be careful to put it back
1887 * if we conclude we can't use this file.
1889 if (file_existing_share
== 0) {
1890 /* Quick and easy, no possibility to share */
1891 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Share mode prevents open: requested access: 0x%" PRIx32
", file has sharing = NONE", __func__
, (*share_info
)->access
);
1893 file_share_release (*share_info
);
1899 if (!(file_existing_share
& FILE_SHARE_DELETE
)) {
1900 /* New access mode doesn't match up */
1901 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Share mode prevents open: requested access: 0x%" PRIx32
", file has sharing: 0x%" PRIx32
, __func__
, (*share_info
)->access
, file_existing_share
);
1903 file_share_release (*share_info
);
1909 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: New file!", __func__
);
1916 mono_w32file_create(const gunichar2
*name
, guint32 fileaccess
, guint32 sharemode
, guint32 createmode
, guint32 attrs
)
1918 FileHandle
*filehandle
;
1920 gint flags
=convert_flags(fileaccess
, createmode
);
1921 /*mode_t perms=convert_perms(sharemode);*/
1922 /* we don't use sharemode, because that relates to sharing of
1923 * the file when the file is open and is already handled by
1924 * other code, perms instead are the on-disk permissions and
1925 * this is a sane default.
1930 struct stat statbuf
;
1932 if (attrs
& FILE_ATTRIBUTE_TEMPORARY
)
1935 if (attrs
& FILE_ATTRIBUTE_ENCRYPTED
){
1936 mono_w32error_set_last (ERROR_ENCRYPTION_FAILED
);
1937 return INVALID_HANDLE_VALUE
;
1941 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
1943 mono_w32error_set_last (ERROR_INVALID_NAME
);
1944 return(INVALID_HANDLE_VALUE
);
1947 filename
= mono_unicode_to_external (name
);
1948 if (filename
== NULL
) {
1949 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
1951 mono_w32error_set_last (ERROR_INVALID_NAME
);
1952 return(INVALID_HANDLE_VALUE
);
1955 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Opening %s with share 0x%" PRIx32
" and access 0x%" PRIx32
, __func__
,
1956 filename
, sharemode
, fileaccess
);
1958 fd
= _wapi_open (filename
, flags
, perms
);
1960 /* If we were trying to open a directory with write permissions
1961 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1962 * EISDIR. However, this is a bit bogus because calls to
1963 * manipulate the directory (e.g. mono_w32file_set_times) will still work on
1964 * the directory because they use other API calls
1965 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1966 * to open the directory again without write permission.
1968 if (fd
== -1 && errno
== EISDIR
)
1970 /* Try again but don't try to make it writable */
1971 fd
= _wapi_open (filename
, flags
& ~(O_RDWR
|O_WRONLY
), perms
);
1975 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Error opening file %s: %s", __func__
, filename
, g_strerror(errno
));
1976 _wapi_set_last_path_error_from_errno (NULL
, filename
);
1979 return(INVALID_HANDLE_VALUE
);
1983 ret
= fstat (fd
, &statbuf
);
1986 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fstat error of file %s: %s", __func__
, filename
, g_strerror (errno
));
1987 _wapi_set_last_error_from_errno ();
1992 return(INVALID_HANDLE_VALUE
);
1996 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
1998 if (S_ISFIFO (statbuf
.st_mode
)) {
1999 type
= MONO_FDTYPE_PIPE
;
2000 /* maintain invariant that pipes have no filename */
2003 } else if (S_ISCHR (statbuf
.st_mode
)) {
2004 type
= MONO_FDTYPE_CONSOLE
;
2006 type
= MONO_FDTYPE_FILE
;
2009 filehandle
= file_data_create (type
, fd
);
2010 filehandle
->filename
= filename
;
2011 filehandle
->fileaccess
= fileaccess
;
2012 filehandle
->sharemode
= sharemode
;
2013 filehandle
->attrs
= attrs
;
2015 if (!share_allows_open (&statbuf
, filehandle
->sharemode
, filehandle
->fileaccess
, &filehandle
->share_info
)) {
2016 mono_w32error_set_last (ERROR_SHARING_VIOLATION
);
2018 close (((MonoFDHandle
*) filehandle
)->fd
);
2021 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2022 return (INVALID_HANDLE_VALUE
);
2024 if (!filehandle
->share_info
) {
2025 /* No space, so no more files can be opened */
2026 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: No space in the share table", __func__
);
2028 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES
);
2030 close (((MonoFDHandle
*) filehandle
)->fd
);
2033 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2034 return(INVALID_HANDLE_VALUE
);
2037 #ifdef HAVE_POSIX_FADVISE
2038 if (attrs
& FILE_FLAG_SEQUENTIAL_SCAN
) {
2040 posix_fadvise (((MonoFDHandle
*) filehandle
)->fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
2043 if (attrs
& FILE_FLAG_RANDOM_ACCESS
) {
2045 posix_fadvise (((MonoFDHandle
*) filehandle
)->fd
, 0, 0, POSIX_FADV_RANDOM
);
2051 if (attrs
& FILE_FLAG_SEQUENTIAL_SCAN
) {
2053 fcntl(((MonoFDHandle
*) filehandle
)->fd
, F_RDAHEAD
, 1);
2058 mono_fdhandle_insert ((MonoFDHandle
*) filehandle
);
2060 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: returning handle %p", __func__
, GINT_TO_POINTER(((MonoFDHandle
*) filehandle
)->fd
));
2062 return GINT_TO_POINTER(((MonoFDHandle
*) filehandle
)->fd
);
2066 mono_w32file_close (gpointer handle
)
2068 if (!mono_fdhandle_close (GPOINTER_TO_INT (handle
))) {
2069 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2076 gboolean
mono_w32file_delete(const gunichar2
*name
)
2080 gboolean ret
= FALSE
;
2082 struct stat statbuf
;
2083 FileShare
*shareinfo
;
2087 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
2089 mono_w32error_set_last (ERROR_INVALID_NAME
);
2093 filename
=mono_unicode_to_external(name
);
2094 if(filename
==NULL
) {
2095 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
2097 mono_w32error_set_last (ERROR_INVALID_NAME
);
2102 /* Check to make sure sharing allows us to open the file for
2103 * writing. See bug 323389.
2105 * Do the checks that don't need an open file descriptor, for
2106 * simplicity's sake. If we really have to do the full checks
2107 * then we can implement that later.
2109 if (_wapi_stat (filename
, &statbuf
) < 0) {
2110 _wapi_set_last_path_error_from_errno (NULL
, filename
);
2115 if (share_allows_open (&statbuf
, 0, GENERIC_WRITE
,
2116 &shareinfo
) == FALSE
) {
2117 mono_w32error_set_last (ERROR_SHARING_VIOLATION
);
2122 file_share_release (shareinfo
);
2125 retval
= _wapi_unlink (filename
);
2128 /* On linux, calling unlink on an non-existing file in a read-only mount will fail with EROFS.
2129 * The expected behavior is for this function to return FALSE and not trigger an exception.
2130 * To work around this behavior, we stat the file on failure.
2132 * This was supposedly fixed on kernel 3.0 [1] but we could reproduce it with Ubuntu 16.04 which has kernel 4.4.
2133 * We can't remove this workaround until the early 2020's when most Android deviced will have a fix.
2134 * [1] https://github.com/torvalds/linux/commit/50338b889dc504c69e0cb316ac92d1b9e51f3c8a
2136 if (errno
== EROFS
) {
2138 if (mono_w32file_get_attributes_ex (name
, &stat
)) //The file exists, so must be due the RO file system
2141 _wapi_set_last_path_error_from_errno (NULL
, filename
);
2152 MoveFile (const gunichar2
*name
, const gunichar2
*dest_name
)
2154 gchar
*utf8_name
, *utf8_dest_name
;
2155 gint result
, errno_copy
;
2156 struct stat stat_src
, stat_dest
;
2157 gboolean ret
= FALSE
;
2158 FileShare
*shareinfo
;
2161 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
2163 mono_w32error_set_last (ERROR_INVALID_NAME
);
2167 utf8_name
= mono_unicode_to_external (name
);
2168 if (utf8_name
== NULL
) {
2169 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
2171 mono_w32error_set_last (ERROR_INVALID_NAME
);
2175 if(dest_name
==NULL
) {
2176 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
2179 mono_w32error_set_last (ERROR_INVALID_NAME
);
2183 utf8_dest_name
= mono_unicode_to_external (dest_name
);
2184 if (utf8_dest_name
== NULL
) {
2185 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
2188 mono_w32error_set_last (ERROR_INVALID_NAME
);
2193 * In C# land we check for the existence of src, but not for dest.
2194 * We check it here and return the failure if dest exists and is not
2195 * the same file as src.
2197 if (_wapi_stat (utf8_name
, &stat_src
) < 0) {
2198 if (errno
!= ENOENT
|| _wapi_lstat (utf8_name
, &stat_src
) < 0) {
2199 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
2201 g_free (utf8_dest_name
);
2206 if (!_wapi_stat (utf8_dest_name
, &stat_dest
)) {
2207 if (stat_dest
.st_dev
!= stat_src
.st_dev
||
2208 stat_dest
.st_ino
!= stat_src
.st_ino
) {
2210 g_free (utf8_dest_name
);
2211 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
2216 /* Check to make that we have delete sharing permission.
2217 * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
2219 * Do the checks that don't need an open file descriptor, for
2220 * simplicity's sake. If we really have to do the full checks
2221 * then we can implement that later.
2223 if (share_allows_delete (&stat_src
, &shareinfo
) == FALSE
) {
2224 mono_w32error_set_last (ERROR_SHARING_VIOLATION
);
2228 file_share_release (shareinfo
);
2232 result
= _wapi_rename (utf8_name
, utf8_dest_name
);
2236 switch(errno_copy
) {
2238 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
2242 /* Ignore here, it is dealt with below */
2246 /* We already know src exists. Must be dest that doesn't exist. */
2247 _wapi_set_last_path_error_from_errno (NULL
, utf8_dest_name
);
2251 _wapi_set_last_error_from_errno ();
2256 g_free (utf8_dest_name
);
2258 if (result
!= 0 && errno_copy
== EXDEV
) {
2261 if (S_ISDIR (stat_src
.st_mode
)) {
2262 mono_w32error_set_last (ERROR_NOT_SAME_DEVICE
);
2265 /* Try a copy to the new location, and delete the source */
2266 if (!mono_w32file_copy (name
, dest_name
, FALSE
, ©_error
)) {
2267 /* mono_w32file_copy will set the error */
2271 return(mono_w32file_delete (name
));
2282 write_file (gint src_fd
, gint dest_fd
, struct stat
*st_src
, gboolean report_errors
)
2286 gint buf_size
= st_src
->st_blksize
;
2287 MonoThreadInfo
*info
= mono_thread_info_current ();
2289 buf_size
= buf_size
< 8192 ? 8192 : (buf_size
> 65536 ? 65536 : buf_size
);
2290 buf
= (gchar
*) g_malloc (buf_size
);
2294 remain
= read (src_fd
, buf
, buf_size
);
2297 if (errno
== EINTR
&& !mono_thread_info_is_interrupt_state (info
))
2301 _wapi_set_last_error_from_errno ();
2311 while (remain
> 0) {
2313 n
= write (dest_fd
, wbuf
, remain
);
2316 if (errno
== EINTR
&& !mono_thread_info_is_interrupt_state (info
))
2320 _wapi_set_last_error_from_errno ();
2321 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: write failed.", __func__
);
2337 _wapi_clonefile(const char *from
, const char *to
, int flags
)
2339 gchar
*located_from
, *located_to
;
2342 g_assert (clonefile_ptr
!= NULL
);
2344 located_from
= mono_portability_find_file (from
, FALSE
);
2345 located_to
= mono_portability_find_file (to
, FALSE
);
2348 ret
= clonefile_ptr (
2349 located_from
== NULL
? from
: located_from
,
2350 located_to
== NULL
? to
: located_to
,
2354 g_free (located_from
);
2355 g_free (located_to
);
2362 CopyFile (const gunichar2
*name
, const gunichar2
*dest_name
, gboolean fail_if_exists
)
2364 gchar
*utf8_src
, *utf8_dest
;
2365 struct stat st
, dest_st
;
2366 gboolean ret
= TRUE
;
2369 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
2371 mono_w32error_set_last (ERROR_INVALID_NAME
);
2375 utf8_src
= mono_unicode_to_external (name
);
2376 if (utf8_src
== NULL
) {
2377 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion of source returned NULL",
2380 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2384 if(dest_name
==NULL
) {
2385 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: dest is NULL", __func__
);
2388 mono_w32error_set_last (ERROR_INVALID_NAME
);
2392 utf8_dest
= mono_unicode_to_external (dest_name
);
2393 if (utf8_dest
== NULL
) {
2394 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion of dest returned NULL",
2397 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2404 gint src_fd
, dest_fd
;
2408 src_fd
= _wapi_open (utf8_src
, O_RDONLY
, 0);
2410 _wapi_set_last_path_error_from_errno (NULL
, utf8_src
);
2419 syscall_res
= fstat (src_fd
, &st
);
2421 if (syscall_res
< 0) {
2422 _wapi_set_last_error_from_errno ();
2433 if (!_wapi_stat (utf8_dest
, &dest_st
)) {
2434 /* Before trying to open/create the dest, we need to report a 'file busy'
2435 * error if src and dest are actually the same file. We do the check here to take
2436 * advantage of the IOMAP capability */
2437 if (st
.st_dev
== dest_st
.st_dev
&& st
.st_ino
== dest_st
.st_ino
) {
2444 mono_w32error_set_last (ERROR_SHARING_VIOLATION
);
2448 /* Take advantage of the fact that we already know the file exists and bail out
2450 if (fail_if_exists
) {
2457 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
2462 /* If we attempt to use clonefile API we need to unlink the destination file
2464 if (clonefile_ptr
!= NULL
) {
2466 /* Bail out if the destination is read-only */
2467 if (!is_file_writable (&dest_st
, utf8_dest
)) {
2474 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2478 _wapi_unlink (utf8_dest
);
2484 if (clonefile_ptr
!= NULL
) {
2485 ret
= _wapi_clonefile (utf8_src
, utf8_dest
, 0);
2486 if (ret
== 0 || (errno
!= ENOTSUP
&& errno
!= EXDEV
)) {
2496 _wapi_set_last_error_from_errno ();
2503 if (fail_if_exists
) {
2504 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_CREAT
| O_EXCL
, st
.st_mode
);
2506 /* FIXME: it kinda sucks that this code path potentially scans
2507 * the directory twice due to the weird mono_w32error_set_last()
2509 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_TRUNC
, st
.st_mode
);
2511 /* The file does not exist, try creating it */
2512 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_CREAT
| O_TRUNC
, st
.st_mode
);
2514 /* Apparently this error is set if we
2515 * overwrite the dest file
2517 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
2521 _wapi_set_last_error_from_errno ();
2532 if (!write_file (src_fd
, dest_fd
, &st
, TRUE
))
2538 #ifdef HAVE_STRUCT_TIMEVAL
2539 struct timeval times
[2];
2540 memset (times
, 0, sizeof (times
));
2542 convert_stattime_access_to_timeval (×
[0], &st
);
2543 convert_stattime_mod_to_timeval (×
[1], &st
);
2545 ret_utime
= _wapi_utimes (utf8_dest
, times
);
2547 struct utimbuf dest_time
;
2548 dest_time
.modtime
= st
.st_mtime
;
2549 dest_time
.actime
= st
.st_atime
;
2551 ret_utime
= _wapi_utime (utf8_dest
, &dest_time
);
2553 if (ret_utime
== -1)
2554 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: file [%s] utime failed: %s", __func__
, utf8_dest
, g_strerror(errno
));
2563 convert_arg_to_utf8 (const gunichar2
*arg
, const gchar
*arg_name
)
2568 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: %s is NULL", __func__
, arg_name
);
2569 mono_w32error_set_last (ERROR_INVALID_NAME
);
2573 utf8_ret
= mono_unicode_to_external (arg
);
2574 if (utf8_ret
== NULL
) {
2575 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion of %s returned NULL",
2576 __func__
, arg_name
);
2577 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2585 ReplaceFile (const gunichar2
*replacedFileName
, const gunichar2
*replacementFileName
, const gunichar2
*backupFileName
, guint32 replaceFlags
, gpointer exclude
, gpointer reserved
)
2587 gint result
, backup_fd
= -1,replaced_fd
= -1;
2588 gchar
*utf8_replacedFileName
, *utf8_replacementFileName
= NULL
, *utf8_backupFileName
= NULL
;
2589 struct stat stBackup
;
2590 gboolean ret
= FALSE
;
2592 if (!(utf8_replacedFileName
= convert_arg_to_utf8 (replacedFileName
, "replacedFileName")))
2594 if (!(utf8_replacementFileName
= convert_arg_to_utf8 (replacementFileName
, "replacementFileName")))
2595 goto replace_cleanup
;
2596 if (backupFileName
!= NULL
) {
2597 if (!(utf8_backupFileName
= convert_arg_to_utf8 (backupFileName
, "backupFileName")))
2598 goto replace_cleanup
;
2601 if (utf8_backupFileName
) {
2602 // Open the backup file for read so we can restore the file if an error occurs.
2603 backup_fd
= _wapi_open (utf8_backupFileName
, O_RDONLY
, 0);
2604 result
= _wapi_rename (utf8_replacedFileName
, utf8_backupFileName
);
2606 goto replace_cleanup
;
2609 result
= _wapi_rename (utf8_replacementFileName
, utf8_replacedFileName
);
2611 _wapi_set_last_path_error_from_errno (NULL
, utf8_replacementFileName
);
2612 _wapi_rename (utf8_backupFileName
, utf8_replacedFileName
);
2613 if (backup_fd
!= -1 && !fstat (backup_fd
, &stBackup
)) {
2614 replaced_fd
= _wapi_open (utf8_backupFileName
, O_WRONLY
| O_CREAT
| O_TRUNC
,
2617 if (replaced_fd
== -1)
2618 goto replace_cleanup
;
2620 write_file (backup_fd
, replaced_fd
, &stBackup
, FALSE
);
2623 goto replace_cleanup
;
2629 g_free (utf8_replacedFileName
);
2630 g_free (utf8_replacementFileName
);
2631 g_free (utf8_backupFileName
);
2632 if (backup_fd
!= -1) {
2637 if (replaced_fd
!= -1) {
2639 close (replaced_fd
);
2646 _wapi_stdhandle_create (gint fd
, const gchar
*name
)
2649 FileHandle
*filehandle
;
2651 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: creating standard handle type %s, fd %d", __func__
, name
, fd
);
2653 /* Check if fd is valid */
2655 flags
= fcntl(fd
, F_GETFL
);
2656 } while (flags
== -1 && errno
== EINTR
);
2659 /* Invalid fd. Not really much point checking for EBADF
2662 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fcntl error on fd %d: %s", __func__
, fd
, g_strerror(errno
));
2664 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2665 return INVALID_HANDLE_VALUE
;
2668 filehandle
= file_data_create (MONO_FDTYPE_CONSOLE
, fd
);
2669 filehandle
->filename
= g_strdup(name
);
2671 switch (flags
& (O_RDONLY
|O_WRONLY
|O_RDWR
)) {
2673 filehandle
->fileaccess
= GENERIC_READ
;
2676 filehandle
->fileaccess
= GENERIC_WRITE
;
2679 filehandle
->fileaccess
= GENERIC_READ
| GENERIC_WRITE
;
2682 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Can't figure out flags 0x%x", __func__
, flags
);
2683 filehandle
->fileaccess
= 0;
2687 /* some default security attributes might be needed */
2688 filehandle
->security_attributes
= 0;
2690 /* Apparently input handles can't be written to. (I don't
2691 * know if output or error handles can't be read from.)
2694 filehandle
->fileaccess
&= ~GENERIC_WRITE
;
2696 filehandle
->sharemode
= 0;
2697 filehandle
->attrs
= 0;
2699 if (!mono_fdhandle_try_insert ((MonoFDHandle
*) filehandle
)) {
2700 /* we raced between 2 invocations of _wapi_stdhandle_create */
2701 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2702 return GINT_TO_POINTER(fd
);
2705 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: returning handle %p", __func__
, GINT_TO_POINTER(((MonoFDHandle
*) filehandle
)->fd
));
2707 return GINT_TO_POINTER(((MonoFDHandle
*) filehandle
)->fd
);
2711 STD_INPUT_HANDLE
= -10,
2712 STD_OUTPUT_HANDLE
= -11,
2713 STD_ERROR_HANDLE
= -12,
2717 mono_w32file_get_std_handle (gint stdhandle
)
2719 FileHandle
**filehandle
;
2724 case STD_INPUT_HANDLE
:
2729 case STD_OUTPUT_HANDLE
:
2734 case STD_ERROR_HANDLE
:
2740 g_assert_not_reached ();
2743 if (!mono_fdhandle_lookup_and_ref(fd
, (MonoFDHandle
**) &filehandle
)) {
2746 handle
= _wapi_stdhandle_create (fd
, name
);
2747 if (handle
== INVALID_HANDLE_VALUE
) {
2748 mono_w32error_set_last (ERROR_NO_MORE_FILES
);
2749 return INVALID_HANDLE_VALUE
;
2753 return GINT_TO_POINTER (fd
);
2757 mono_w32file_read_or_write (gboolean read
, gpointer handle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
, gint32
*win32error
)
2759 MONO_REQ_GC_UNSAFE_MODE
;
2761 FileHandle
*filehandle
;
2762 gboolean ret
= FALSE
;
2764 gboolean
const ref
= mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
);
2766 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2770 switch (((MonoFDHandle
*) filehandle
)->type
) {
2771 case MONO_FDTYPE_FILE
:
2772 ret
= (read
? file_read
: file_write
) (filehandle
, buffer
, numbytes
, bytesread
);
2774 case MONO_FDTYPE_CONSOLE
:
2775 ret
= (read
? console_read
: console_write
) (filehandle
, buffer
, numbytes
, bytesread
);
2777 case MONO_FDTYPE_PIPE
:
2778 ret
= (read
? pipe_read
: pipe_write
) (filehandle
, buffer
, numbytes
, bytesread
);
2781 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2787 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2789 *win32error
= mono_w32error_get_last ();
2794 mono_w32file_read (gpointer handle
, gpointer buffer
, guint32 numbytes
, guint32
*bytesread
, gint32
*win32error
)
2796 return mono_w32file_read_or_write (TRUE
, handle
, buffer
, numbytes
, bytesread
, win32error
);
2800 mono_w32file_write (gpointer handle
, gconstpointer buffer
, guint32 numbytes
, guint32
*byteswritten
, gint32
*win32error
)
2802 return mono_w32file_read_or_write (FALSE
, handle
, (gpointer
)buffer
, numbytes
, byteswritten
, win32error
);
2806 mono_w32file_flush (gpointer handle
)
2808 FileHandle
*filehandle
;
2811 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2812 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2816 switch (((MonoFDHandle
*) filehandle
)->type
) {
2817 case MONO_FDTYPE_FILE
:
2818 ret
= file_flush(filehandle
);
2821 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2822 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2826 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2831 mono_w32file_truncate (gpointer handle
)
2833 FileHandle
*filehandle
;
2836 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2837 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2841 switch (((MonoFDHandle
*) filehandle
)->type
) {
2842 case MONO_FDTYPE_FILE
:
2843 ret
= file_setendoffile(filehandle
);
2846 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2847 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2851 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2856 mono_w32file_seek (gpointer handle
, gint32 movedistance
, gint32
*highmovedistance
, guint32 method
)
2858 FileHandle
*filehandle
;
2861 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2862 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2863 return INVALID_SET_FILE_POINTER
;
2866 switch (((MonoFDHandle
*) filehandle
)->type
) {
2867 case MONO_FDTYPE_FILE
:
2868 ret
= file_seek(filehandle
, movedistance
, highmovedistance
, method
);
2871 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2872 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2873 return INVALID_SET_FILE_POINTER
;
2876 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2881 mono_w32file_get_type(gpointer handle
)
2883 FileHandle
*filehandle
;
2886 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2887 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2888 return FILE_TYPE_UNKNOWN
;
2891 switch (((MonoFDHandle
*) filehandle
)->type
) {
2892 case MONO_FDTYPE_FILE
:
2893 ret
= FILE_TYPE_DISK
;
2895 case MONO_FDTYPE_CONSOLE
:
2896 ret
= FILE_TYPE_CHAR
;
2898 case MONO_FDTYPE_PIPE
:
2899 ret
= FILE_TYPE_PIPE
;
2902 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2903 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2904 return FILE_TYPE_UNKNOWN
;
2907 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2912 GetFileSize(gpointer handle
, guint32
*highsize
)
2914 FileHandle
*filehandle
;
2917 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2918 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2919 return INVALID_FILE_SIZE
;
2922 switch (((MonoFDHandle
*) filehandle
)->type
) {
2923 case MONO_FDTYPE_FILE
:
2924 ret
= file_getfilesize(filehandle
, highsize
);
2927 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2928 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2929 return INVALID_FILE_SIZE
;
2932 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2937 mono_w32file_set_times(gpointer handle
, const FILETIME
*create_time
, const FILETIME
*access_time
, const FILETIME
*write_time
)
2939 FileHandle
*filehandle
;
2942 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
2943 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2947 switch (((MonoFDHandle
*) filehandle
)->type
) {
2948 case MONO_FDTYPE_FILE
:
2949 ret
= file_setfiletime(filehandle
, create_time
, access_time
, write_time
);
2952 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2953 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2957 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
2961 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2962 * January 1 1601 GMT
2965 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2967 static const guint16 mon_yday
[2][13]={
2968 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2969 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2973 mono_w32file_filetime_to_systemtime(const FILETIME
*file_time
, SYSTEMTIME
*system_time
)
2975 gint64 file_ticks
, totaldays
, rem
, y
;
2978 if(system_time
==NULL
) {
2979 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: system_time NULL", __func__
);
2981 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2985 file_ticks
=((gint64
)file_time
->dwHighDateTime
<< 32) +
2986 file_time
->dwLowDateTime
;
2988 /* Really compares if file_ticks>=0x8000000000000000
2989 * (LLONG_MAX+1) but we're working with a signed value for the
2990 * year and day calculation to work later
2993 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: file_time too big", __func__
);
2995 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2999 totaldays
=(file_ticks
/ TICKS_PER_DAY
);
3000 rem
= file_ticks
% TICKS_PER_DAY
;
3001 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: totaldays: %" G_GINT64_FORMAT
" rem: %" G_GINT64_FORMAT
, __func__
,
3004 system_time
->wHour
=rem
/TICKS_PER_HOUR
;
3005 rem
%= TICKS_PER_HOUR
;
3006 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Hour: %d rem: %" G_GINT64_FORMAT
, __func__
,
3007 system_time
->wHour
, rem
);
3009 system_time
->wMinute
= rem
/ TICKS_PER_MINUTE
;
3010 rem
%= TICKS_PER_MINUTE
;
3011 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Minute: %d rem: %" G_GINT64_FORMAT
, __func__
,
3012 system_time
->wMinute
, rem
);
3014 system_time
->wSecond
= rem
/ TICKS_PER_SECOND
;
3015 rem
%= TICKS_PER_SECOND
;
3016 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Second: %d rem: %" G_GINT64_FORMAT
, __func__
,
3017 system_time
->wSecond
, rem
);
3019 system_time
->wMilliseconds
= rem
/ TICKS_PER_MILLISECOND
;
3020 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Milliseconds: %d", __func__
,
3021 system_time
->wMilliseconds
);
3023 /* January 1, 1601 was a Monday, according to Emacs calendar */
3024 system_time
->wDayOfWeek
= ((1 + totaldays
) % 7) + 1;
3025 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Day of week: %d", __func__
, system_time
->wDayOfWeek
);
3027 /* This algorithm to find year and month given days from epoch
3032 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
3033 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
3035 while(totaldays
< 0 || totaldays
>= (isleap(y
)?366:365)) {
3036 /* Guess a corrected year, assuming 365 days per year */
3037 gint64 yg
= y
+ totaldays
/ 365 - (totaldays
% 365 < 0);
3038 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: totaldays: %" G_GINT64_FORMAT
" yg: %" G_GINT64_FORMAT
" y: %" G_GINT64_FORMAT
, __func__
,
3040 g_message("%s: LEAPS(yg): %li LEAPS(y): %li", __func__
,
3041 LEAPS_THRU_END_OF(yg
-1), LEAPS_THRU_END_OF(y
-1));
3043 /* Adjust days and y to match the guessed year. */
3044 totaldays
-= ((yg
- y
) * 365
3045 + LEAPS_THRU_END_OF (yg
- 1)
3046 - LEAPS_THRU_END_OF (y
- 1));
3047 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: totaldays: %" G_GINT64_FORMAT
,
3048 __func__
, totaldays
);
3050 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: y: %" G_GINT64_FORMAT
, __func__
, y
);
3053 system_time
->wYear
= y
;
3054 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Year: %d", __func__
, system_time
->wYear
);
3056 ip
= mon_yday
[isleap(y
)];
3058 for(y
=11; totaldays
< ip
[y
]; --y
) {
3062 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: totaldays: %" G_GINT64_FORMAT
, __func__
, totaldays
);
3064 system_time
->wMonth
= y
+ 1;
3065 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Month: %d", __func__
, system_time
->wMonth
);
3067 system_time
->wDay
= totaldays
+ 1;
3068 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Day: %d", __func__
, system_time
->wDay
);
3074 findhandle_destroy (gpointer data
)
3076 FindHandle
*findhandle
;
3078 findhandle
= (FindHandle
*) data
;
3079 g_assert (findhandle
);
3081 mono_coop_mutex_destroy (&findhandle
->mutex
);
3083 if (findhandle
->namelist
)
3084 g_strfreev (findhandle
->namelist
);
3085 if (findhandle
->dir_part
)
3086 g_free (findhandle
->dir_part
);
3088 g_free (findhandle
);
3092 findhandle_create (void)
3094 FindHandle
* findhandle
;
3096 findhandle
= g_new0 (FindHandle
, 1);
3097 mono_refcount_init (findhandle
, findhandle_destroy
);
3099 mono_coop_mutex_init (&findhandle
->mutex
);
3105 findhandle_insert (FindHandle
*findhandle
)
3107 mono_coop_mutex_lock (&finds_mutex
);
3109 if (g_hash_table_lookup_extended (finds
, (gpointer
) findhandle
, NULL
, NULL
))
3110 g_error("%s: duplicate Find handle %p", __func__
, (gpointer
) findhandle
);
3112 g_hash_table_insert (finds
, (gpointer
) findhandle
, findhandle
);
3114 mono_coop_mutex_unlock (&finds_mutex
);
3118 findhandle_lookup_and_ref (gpointer handle
, FindHandle
**findhandle
)
3120 mono_coop_mutex_lock (&finds_mutex
);
3122 if (!g_hash_table_lookup_extended (finds
, handle
, NULL
, (gpointer
*) findhandle
)) {
3123 mono_coop_mutex_unlock (&finds_mutex
);
3127 mono_refcount_inc (*findhandle
);
3129 mono_coop_mutex_unlock (&finds_mutex
);
3135 findhandle_unref (FindHandle
*findhandle
)
3137 mono_refcount_dec (findhandle
);
3141 findhandle_close (gpointer handle
)
3143 FindHandle
*findhandle
;
3146 mono_coop_mutex_lock (&finds_mutex
);
3148 if (!g_hash_table_lookup_extended (finds
, handle
, NULL
, (gpointer
*) &findhandle
)) {
3149 mono_coop_mutex_unlock (&finds_mutex
);
3154 removed
= g_hash_table_remove (finds
, (gpointer
) findhandle
);
3157 mono_coop_mutex_unlock (&finds_mutex
);
3163 finds_remove (gpointer data
)
3165 FindHandle
* findhandle
;
3167 findhandle
= (FindHandle
*) data
;
3168 g_assert (findhandle
);
3170 mono_refcount_dec (findhandle
);
3174 mono_w32file_find_first (const gunichar2
*pattern
, WIN32_FIND_DATA
*find_data
)
3176 FindHandle
*findhandle
;
3177 gchar
*utf8_pattern
= NULL
, *dir_part
, *entry_part
, **namelist
;
3180 if (pattern
== NULL
) {
3181 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: pattern is NULL", __func__
);
3183 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
3184 return(INVALID_HANDLE_VALUE
);
3187 utf8_pattern
= mono_unicode_to_external (pattern
);
3188 if (utf8_pattern
== NULL
) {
3189 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
3191 mono_w32error_set_last (ERROR_INVALID_NAME
);
3192 return(INVALID_HANDLE_VALUE
);
3195 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: looking for [%s]", __func__
, utf8_pattern
);
3197 /* Figure out which bit of the pattern is the directory */
3198 dir_part
= _wapi_dirname (utf8_pattern
);
3199 entry_part
= _wapi_basename (utf8_pattern
);
3202 /* Don't do this check for now, it breaks if directories
3203 * really do have metachars in their names (see bug 58116).
3204 * FIXME: Figure out a better solution to keep some checks...
3206 if (strchr (dir_part
, '*') || strchr (dir_part
, '?')) {
3207 mono_w32error_set_last (ERROR_INVALID_NAME
);
3209 g_free (entry_part
);
3210 g_free (utf8_pattern
);
3211 return(INVALID_HANDLE_VALUE
);
3215 /* The pattern can specify a directory or a set of files.
3217 * The pattern can have wildcard characters ? and *, but only
3218 * in the section after the last directory delimiter. (Return
3219 * ERROR_INVALID_NAME if there are wildcards in earlier path
3220 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3221 * means "match one character", "??" seems to mean "match one
3222 * or two characters", "???" seems to mean "match one, two or
3223 * three characters", etc. Windows will also try and match
3224 * the mangled "short name" of files, so 8 character patterns
3225 * with wildcards will show some surprising results.
3227 * All the written documentation I can find says that '?'
3228 * should only match one character, and doesn't mention '??',
3229 * '???' etc. I'm going to assume that the strict behaviour
3230 * (ie '???' means three and only three characters) is the
3231 * correct one, because that lets me use fnmatch(3) rather
3232 * than mess around with regexes.
3236 result
= _wapi_io_scandir (dir_part
, entry_part
,
3240 /* No files, which windows seems to call
3243 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
3244 g_free (utf8_pattern
);
3245 g_free (entry_part
);
3247 g_strfreev (namelist
);
3248 return (INVALID_HANDLE_VALUE
);
3252 _wapi_set_last_path_error_from_errno (dir_part
, NULL
);
3253 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: scandir error: %s", __func__
, g_strerror (errno
));
3254 g_free (utf8_pattern
);
3255 g_free (entry_part
);
3257 g_strfreev (namelist
);
3258 return (INVALID_HANDLE_VALUE
);
3261 g_free (utf8_pattern
);
3262 g_free (entry_part
);
3264 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Got %d matches", __func__
, result
);
3266 findhandle
= findhandle_create ();
3267 findhandle
->namelist
= namelist
;
3268 findhandle
->dir_part
= dir_part
;
3269 findhandle
->num
= result
;
3270 findhandle
->count
= 0;
3272 findhandle_insert (findhandle
);
3274 if (!mono_w32file_find_next ((gpointer
) findhandle
, find_data
)) {
3275 mono_w32file_find_close ((gpointer
) findhandle
);
3276 mono_w32error_set_last (ERROR_NO_MORE_FILES
);
3277 return INVALID_HANDLE_VALUE
;
3280 return (gpointer
) findhandle
;
3284 mono_w32file_find_next (gpointer handle
, WIN32_FIND_DATA
*find_data
)
3286 FindHandle
*findhandle
;
3287 struct stat buf
, linkbuf
;
3290 gchar
*utf8_filename
, *utf8_basename
;
3291 gunichar2
*utf16_basename
;
3294 gboolean ret
= FALSE
;
3296 if (!findhandle_lookup_and_ref (handle
, &findhandle
)) {
3297 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
3301 mono_coop_mutex_lock (&findhandle
->mutex
);
3304 if (findhandle
->count
>= findhandle
->num
) {
3305 mono_w32error_set_last (ERROR_NO_MORE_FILES
);
3309 /* stat next match */
3311 filename
= g_build_filename (findhandle
->dir_part
, findhandle
->namelist
[findhandle
->count
++], NULL
);
3313 result
= _wapi_stat (filename
, &buf
);
3314 if (result
== -1 && errno
== ENOENT
) {
3315 /* Might be a dangling symlink */
3316 result
= _wapi_lstat (filename
, &buf
);
3320 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: stat failed: %s", __func__
, filename
);
3326 result
= _wapi_lstat (filename
, &linkbuf
);
3328 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: lstat failed: %s", __func__
, filename
);
3334 utf8_filename
= mono_utf8_from_external (filename
);
3335 if (utf8_filename
== NULL
) {
3336 /* We couldn't turn this filename into utf8 (eg the
3337 * encoding of the name wasn't convertible), so just
3340 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__
, filename
);
3347 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Found [%s]", __func__
, utf8_filename
);
3349 /* fill data block */
3351 if (buf
.st_mtime
< buf
.st_ctime
)
3352 create_time
= buf
.st_mtime
;
3354 create_time
= buf
.st_ctime
;
3356 find_data
->dwFileAttributes
= _wapi_stat_to_file_attributes (utf8_filename
, &buf
, &linkbuf
);
3358 time_t_to_filetime (create_time
, &find_data
->ftCreationTime
);
3359 time_t_to_filetime (buf
.st_atime
, &find_data
->ftLastAccessTime
);
3360 time_t_to_filetime (buf
.st_mtime
, &find_data
->ftLastWriteTime
);
3362 if (find_data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
3363 find_data
->nFileSizeHigh
= 0;
3364 find_data
->nFileSizeLow
= 0;
3366 find_data
->nFileSizeHigh
= buf
.st_size
>> 32;
3367 find_data
->nFileSizeLow
= buf
.st_size
& 0xFFFFFFFF;
3370 find_data
->dwReserved0
= 0;
3371 find_data
->dwReserved1
= 0;
3373 utf8_basename
= _wapi_basename (utf8_filename
);
3374 utf16_basename
= g_utf8_to_utf16 (utf8_basename
, -1, NULL
, &bytes
,
3376 if(utf16_basename
==NULL
) {
3377 g_free (utf8_basename
);
3378 g_free (utf8_filename
);
3383 /* utf16 is 2 * utf8 */
3386 memset (find_data
->cFileName
, '\0', (MAX_PATH
*2));
3388 /* Truncating a utf16 string like this might leave the last
3391 memcpy (find_data
->cFileName
, utf16_basename
,
3392 bytes
<(MAX_PATH
*2)-2?bytes
:(MAX_PATH
*2)-2);
3394 find_data
->cAlternateFileName
[0] = 0; /* not used */
3396 g_free (utf8_basename
);
3397 g_free (utf8_filename
);
3398 g_free (utf16_basename
);
3401 mono_coop_mutex_unlock (&findhandle
->mutex
);
3403 findhandle_unref (findhandle
);
3409 mono_w32file_find_close (gpointer handle
)
3411 if (!findhandle_close (handle
)) {
3412 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
3420 mono_w32file_create_directory (const gunichar2
*name
)
3426 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3428 mono_w32error_set_last (ERROR_INVALID_NAME
);
3432 utf8_name
= mono_unicode_to_external (name
);
3433 if (utf8_name
== NULL
) {
3434 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
3436 mono_w32error_set_last (ERROR_INVALID_NAME
);
3440 result
= _wapi_mkdir (utf8_name
, 0777);
3447 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3453 mono_w32file_remove_directory (const gunichar2
*name
)
3459 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3461 mono_w32error_set_last (ERROR_INVALID_NAME
);
3465 utf8_name
= mono_unicode_to_external (name
);
3466 if (utf8_name
== NULL
) {
3467 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
3469 mono_w32error_set_last (ERROR_INVALID_NAME
);
3473 result
= _wapi_rmdir (utf8_name
);
3475 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3486 mono_w32file_get_attributes (const gunichar2
*name
)
3489 struct stat buf
, linkbuf
;
3494 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3496 mono_w32error_set_last (ERROR_INVALID_NAME
);
3500 utf8_name
= mono_unicode_to_external (name
);
3501 if (utf8_name
== NULL
) {
3502 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
3504 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3505 return (INVALID_FILE_ATTRIBUTES
);
3508 result
= _wapi_stat (utf8_name
, &buf
);
3509 if (result
== -1 && (errno
== ENOENT
|| errno
== ELOOP
)) {
3510 /* Might be a dangling symlink... */
3511 result
= _wapi_lstat (utf8_name
, &buf
);
3515 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3517 return (INVALID_FILE_ATTRIBUTES
);
3520 result
= _wapi_lstat (utf8_name
, &linkbuf
);
3522 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3524 return (INVALID_FILE_ATTRIBUTES
);
3527 ret
= _wapi_stat_to_file_attributes (utf8_name
, &buf
, &linkbuf
);
3535 mono_w32file_get_attributes_ex (const gunichar2
*name
, MonoIOStat
*stat
)
3539 struct stat buf
, linkbuf
;
3543 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3545 mono_w32error_set_last (ERROR_INVALID_NAME
);
3549 utf8_name
= mono_unicode_to_external (name
);
3550 if (utf8_name
== NULL
) {
3551 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
3553 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3557 result
= _wapi_stat (utf8_name
, &buf
);
3558 if (result
== -1 && errno
== ENOENT
) {
3559 /* Might be a dangling symlink... */
3560 result
= _wapi_lstat (utf8_name
, &buf
);
3564 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3569 result
= _wapi_lstat (utf8_name
, &linkbuf
);
3571 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3576 /* fill stat block */
3578 stat
->attributes
= _wapi_stat_to_file_attributes (utf8_name
, &buf
, &linkbuf
);
3579 stat
->length
= (stat
->attributes
& FILE_ATTRIBUTE_DIRECTORY
) ? 0 : buf
.st_size
;
3581 #if HAVE_STRUCT_STAT_ST_ATIMESPEC
3582 if (linkbuf
.st_mtimespec
.tv_sec
< linkbuf
.st_ctimespec
.tv_sec
|| (linkbuf
.st_mtimespec
.tv_sec
== linkbuf
.st_ctimespec
.tv_sec
&& linkbuf
.st_mtimespec
.tv_nsec
< linkbuf
.st_ctimespec
.tv_nsec
))
3583 stat
->creation_time
= linkbuf
.st_mtimespec
.tv_sec
* TICKS_PER_SECOND
+ (linkbuf
.st_mtimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3585 stat
->creation_time
= linkbuf
.st_ctimespec
.tv_sec
* TICKS_PER_SECOND
+ (linkbuf
.st_ctimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3587 stat
->last_access_time
= linkbuf
.st_atimespec
.tv_sec
* TICKS_PER_SECOND
+ (linkbuf
.st_atimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3588 stat
->last_write_time
= linkbuf
.st_mtimespec
.tv_sec
* TICKS_PER_SECOND
+ (linkbuf
.st_mtimespec
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3589 #elif HAVE_STRUCT_STAT_ST_ATIM
3590 if (linkbuf
.st_mtime
< linkbuf
.st_ctime
|| (linkbuf
.st_mtime
== linkbuf
.st_ctime
&& linkbuf
.st_mtim
.tv_nsec
< linkbuf
.st_ctim
.tv_nsec
))
3591 stat
->creation_time
= linkbuf
.st_mtime
* TICKS_PER_SECOND
+ (linkbuf
.st_mtim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3593 stat
->creation_time
= linkbuf
.st_ctime
* TICKS_PER_SECOND
+ (linkbuf
.st_ctim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3595 stat
->last_access_time
= linkbuf
.st_atime
* TICKS_PER_SECOND
+ (linkbuf
.st_atim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3596 stat
->last_write_time
= linkbuf
.st_mtime
* TICKS_PER_SECOND
+ (linkbuf
.st_mtim
.tv_nsec
/ NANOSECONDS_PER_MICROSECOND
) * TICKS_PER_MICROSECOND
+ CONVERT_BASE
;
3598 stat
->creation_time
= (((guint64
) (linkbuf
.st_mtime
< linkbuf
.st_ctime
? linkbuf
.st_mtime
: linkbuf
.st_ctime
)) * TICKS_PER_SECOND
) + CONVERT_BASE
;
3599 stat
->last_access_time
= (((guint64
) (linkbuf
.st_atime
)) * TICKS_PER_SECOND
) + CONVERT_BASE
;
3600 stat
->last_write_time
= (((guint64
) (linkbuf
.st_mtime
)) * TICKS_PER_SECOND
) + CONVERT_BASE
;
3608 mono_w32file_set_attributes (const gunichar2
*name
, guint32 attrs
)
3610 /* FIXME: think of something clever to do on unix */
3616 * Currently we only handle one *internal* case, with a value that is
3617 * not standard: 0x80000000, which means `set executable bit'
3621 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: name is NULL", __func__
);
3623 mono_w32error_set_last (ERROR_INVALID_NAME
);
3627 utf8_name
= mono_unicode_to_external (name
);
3628 if (utf8_name
== NULL
) {
3629 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
3631 mono_w32error_set_last (ERROR_INVALID_NAME
);
3635 result
= _wapi_stat (utf8_name
, &buf
);
3636 if (result
== -1 && errno
== ENOENT
) {
3637 /* Might be a dangling symlink... */
3638 result
= _wapi_lstat (utf8_name
, &buf
);
3642 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3647 /* Contrary to the documentation, ms allows NORMAL to be
3648 * specified along with other attributes, so dont bother to
3649 * catch that case here.
3651 if (attrs
& FILE_ATTRIBUTE_READONLY
) {
3652 result
= _wapi_chmod (utf8_name
, buf
.st_mode
& ~(S_IWUSR
| S_IWOTH
| S_IWGRP
));
3654 result
= _wapi_chmod (utf8_name
, buf
.st_mode
| S_IWUSR
);
3657 /* Ignore the other attributes for now */
3659 if (attrs
& 0x80000000){
3660 mode_t exec_mask
= 0;
3662 if ((buf
.st_mode
& S_IRUSR
) != 0)
3663 exec_mask
|= S_IXUSR
;
3665 if ((buf
.st_mode
& S_IRGRP
) != 0)
3666 exec_mask
|= S_IXGRP
;
3668 if ((buf
.st_mode
& S_IROTH
) != 0)
3669 exec_mask
|= S_IXOTH
;
3672 result
= chmod (utf8_name
, buf
.st_mode
| exec_mask
);
3675 /* Don't bother to reset executable (might need to change this
3685 mono_w32file_get_cwd (guint32 length
, gunichar2
*buffer
)
3687 gunichar2
*utf16_path
;
3691 if (getcwd ((gchar
*)buffer
, length
) == NULL
) {
3692 if (errno
== ERANGE
) { /*buffer length is not big enough */
3693 gchar
*path
= g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/
3696 utf16_path
= mono_unicode_from_external (path
, &bytes
);
3697 g_free (utf16_path
);
3701 _wapi_set_last_error_from_errno ();
3705 utf16_path
= mono_unicode_from_external ((gchar
*)buffer
, &bytes
);
3706 count
= (bytes
/2)+1;
3707 g_assert (count
<= length
); /*getcwd must have failed before with ERANGE*/
3709 /* Add the terminator */
3710 memset (buffer
, '\0', bytes
+2);
3711 memcpy (buffer
, utf16_path
, bytes
);
3713 g_free (utf16_path
);
3719 mono_w32file_set_cwd (const gunichar2
*path
)
3725 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
3729 utf8_path
= mono_unicode_to_external (path
);
3730 if (_wapi_chdir (utf8_path
) != 0) {
3731 _wapi_set_last_error_from_errno ();
3742 mono_w32file_create_pipe (gpointer
*readpipe
, gpointer
*writepipe
, guint32 size
)
3744 FileHandle
*read_filehandle
, *write_filehandle
;
3748 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Creating pipe", __func__
);
3754 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Error creating pipe: (%d) %s",
3755 __func__
, errno
, g_strerror (errno
));
3757 _wapi_set_last_error_from_errno ();
3761 /* filedes[0] is open for reading, filedes[1] for writing */
3763 read_filehandle
= file_data_create (MONO_FDTYPE_PIPE
, filedes
[0]);
3764 read_filehandle
->fileaccess
= GENERIC_READ
;
3766 write_filehandle
= file_data_create (MONO_FDTYPE_PIPE
, filedes
[1]);
3767 write_filehandle
->fileaccess
= GENERIC_WRITE
;
3769 mono_fdhandle_insert ((MonoFDHandle
*) read_filehandle
);
3770 mono_fdhandle_insert ((MonoFDHandle
*) write_filehandle
);
3772 *readpipe
= GINT_TO_POINTER(((MonoFDHandle
*) read_filehandle
)->fd
);
3773 *writepipe
= GINT_TO_POINTER(((MonoFDHandle
*) write_filehandle
)->fd
);
3775 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Returning pipe: read handle %p, write handle %p",
3776 __func__
, GINT_TO_POINTER(((MonoFDHandle
*) read_filehandle
)->fd
), GINT_TO_POINTER(((MonoFDHandle
*) write_filehandle
)->fd
));
3781 #ifdef HAVE_GETFSSTAT
3782 /* Darwin has getfsstat */
3784 mono_w32file_get_logical_drive (guint32 len
, gunichar2
*buf
)
3786 struct statfs
*stats
;
3789 glong length
, total
= 0;
3793 n
= getfsstat (NULL
, 0, MNT_NOWAIT
);
3797 size
= n
* sizeof (struct statfs
);
3798 stats
= (struct statfs
*) g_malloc (size
);
3802 syscall_res
= getfsstat (stats
, size
, MNT_NOWAIT
);
3804 if (syscall_res
== -1){
3808 for (i
= 0; i
< n
; i
++){
3809 dir
= g_utf8_to_utf16 (stats
[i
].f_mntonname
, -1, NULL
, &length
, NULL
);
3810 if (total
+ length
< len
){
3811 memcpy (buf
+ total
, dir
, sizeof (gunichar2
) * length
);
3812 buf
[total
+length
] = 0;
3815 total
+= length
+ 1;
3825 mono_w32file_get_logical_drive (guint32 len
, gunichar2
*buf
)
3827 struct vmount
*mounts
;
3828 // ret will first be the errno cond, then no of structs
3829 int needsize
, ret
, total
;
3835 ret
= mntctl (MCTL_QUERY
, sizeof(needsize
), &needsize
);
3839 mounts
= (struct vmount
*) g_malloc (needsize
);
3843 ret
= mntctl (MCTL_QUERY
, needsize
, mounts
);
3850 for (int i
= 0; i
< ret
; i
++) {
3851 dir
= g_utf8_to_utf16 (vmt2dataptr(mounts
, VMT_STUB
), -1, NULL
, &length
, NULL
);
3852 if (total
+ length
< len
){
3853 memcpy (buf
+ total
, dir
, sizeof (gunichar2
) * length
);
3854 buf
[total
+length
] = 0;
3857 total
+= length
+ 1;
3858 mounts
= (void*)mounts
+ mounts
->vmt_length
; // next!
3867 /* In-place octal sequence replacement */
3869 unescape_octal (gchar
*str
)
3878 while (*rptr
!= '\0') {
3879 if (*rptr
== '\\') {
3882 c
= (*(rptr
++) - '0') << 6;
3883 c
+= (*(rptr
++) - '0') << 3;
3884 c
+= *(rptr
++) - '0';
3886 } else if (wptr
!= rptr
) {
3894 static gint32
GetLogicalDriveStrings_Mtab (guint32 len
, gunichar2
*buf
);
3897 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
3898 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
3899 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
3904 guint32 buffer_index
;
3905 guint32 mountpoint_index
;
3906 guint32 field_number
;
3907 guint32 allocated_size
;
3908 guint32 fsname_index
;
3909 guint32 fstype_index
;
3910 gchar mountpoint
[GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER
+ 1];
3911 gchar
*mountpoint_allocated
;
3912 gchar buffer
[GET_LOGICAL_DRIVE_STRINGS_BUFFER
];
3913 gchar fsname
[GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
+ 1];
3914 gchar fstype
[GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
+ 1];
3917 gboolean check_mount_source
;
3918 } LinuxMountInfoParseState
;
3920 static gboolean
GetLogicalDriveStrings_Mounts (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3921 static gboolean
GetLogicalDriveStrings_MountInfo (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3922 static void append_to_mountpoint (LinuxMountInfoParseState
*state
);
3923 static gboolean
add_drive_string (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3926 mono_w32file_get_logical_drive (guint32 len
, gunichar2
*buf
)
3930 LinuxMountInfoParseState state
;
3931 gboolean (*parser
)(guint32
, gunichar2
*, LinuxMountInfoParseState
*) = NULL
;
3933 memset (buf
, 0, len
* sizeof (gunichar2
));
3935 fd
= open ("/proc/self/mountinfo", O_RDONLY
);
3938 parser
= GetLogicalDriveStrings_MountInfo
;
3941 fd
= open ("/proc/mounts", O_RDONLY
);
3944 parser
= GetLogicalDriveStrings_Mounts
;
3948 ret
= GetLogicalDriveStrings_Mtab (len
, buf
);
3952 memset (&state
, 0, sizeof (LinuxMountInfoParseState
));
3953 state
.field_number
= 1;
3954 state
.delimiter
= ' ';
3958 state
.nbytes
= read (fd
, state
.buffer
, GET_LOGICAL_DRIVE_STRINGS_BUFFER
);
3960 if (!(state
.nbytes
> 0))
3962 state
.buffer_index
= 0;
3964 while ((*parser
)(len
, buf
, &state
)) {
3965 if (state
.buffer
[state
.buffer_index
] == '\n') {
3966 gboolean quit
= add_drive_string (len
, buf
, &state
);
3967 state
.field_number
= 1;
3968 state
.buffer_index
++;
3969 if (state
.mountpoint_allocated
) {
3970 g_free (state
.mountpoint_allocated
);
3971 state
.mountpoint_allocated
= NULL
;
3991 static gboolean
GetLogicalDriveStrings_Mounts (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
3995 if (state
->field_number
== 1)
3996 state
->check_mount_source
= TRUE
;
3998 while (state
->buffer_index
< (guint32
)state
->nbytes
) {
3999 if (state
->buffer
[state
->buffer_index
] == state
->delimiter
) {
4000 state
->field_number
++;
4001 switch (state
->field_number
) {
4003 state
->mountpoint_index
= 0;
4007 if (state
->mountpoint_allocated
)
4008 state
->mountpoint_allocated
[state
->mountpoint_index
] = 0;
4010 state
->mountpoint
[state
->mountpoint_index
] = 0;
4014 ptr
= (gchar
*)memchr (state
->buffer
+ state
->buffer_index
, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER
- state
->buffer_index
);
4016 state
->buffer_index
= (ptr
- (gchar
*)state
->buffer
) - 1;
4018 state
->buffer_index
= state
->nbytes
;
4021 state
->buffer_index
++;
4023 } else if (state
->buffer
[state
->buffer_index
] == '\n')
4026 switch (state
->field_number
) {
4028 if (state
->check_mount_source
) {
4029 if (state
->fsname_index
== 0 && state
->buffer
[state
->buffer_index
] == '/') {
4030 /* We can ignore the rest, it's a device
4032 state
->check_mount_source
= FALSE
;
4033 state
->fsname
[state
->fsname_index
++] = '/';
4036 if (state
->fsname_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
4037 state
->fsname
[state
->fsname_index
++] = state
->buffer
[state
->buffer_index
];
4042 append_to_mountpoint (state
);
4046 if (state
->fstype_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
4047 state
->fstype
[state
->fstype_index
++] = state
->buffer
[state
->buffer_index
];
4051 state
->buffer_index
++;
4057 static gboolean
GetLogicalDriveStrings_MountInfo (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
4059 while (state
->buffer_index
< (guint32
)state
->nbytes
) {
4060 if (state
->buffer
[state
->buffer_index
] == state
->delimiter
) {
4061 state
->field_number
++;
4062 switch (state
->field_number
) {
4064 state
->mountpoint_index
= 0;
4068 if (state
->mountpoint_allocated
)
4069 state
->mountpoint_allocated
[state
->mountpoint_index
] = 0;
4071 state
->mountpoint
[state
->mountpoint_index
] = 0;
4075 state
->delimiter
= '-';
4079 state
->delimiter
= ' ';
4083 state
->check_mount_source
= TRUE
;
4086 state
->buffer_index
++;
4088 } else if (state
->buffer
[state
->buffer_index
] == '\n')
4091 switch (state
->field_number
) {
4093 append_to_mountpoint (state
);
4097 if (state
->fstype_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
4098 state
->fstype
[state
->fstype_index
++] = state
->buffer
[state
->buffer_index
];
4102 if (state
->check_mount_source
) {
4103 if (state
->fsname_index
== 0 && state
->buffer
[state
->buffer_index
] == '/') {
4104 /* We can ignore the rest, it's a device
4106 state
->check_mount_source
= FALSE
;
4107 state
->fsname
[state
->fsname_index
++] = '/';
4110 if (state
->fsname_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
4111 state
->fsname
[state
->fsname_index
++] = state
->buffer
[state
->buffer_index
];
4116 state
->buffer_index
++;
4123 append_to_mountpoint (LinuxMountInfoParseState
*state
)
4125 gchar ch
= state
->buffer
[state
->buffer_index
];
4126 if (state
->mountpoint_allocated
) {
4127 if (state
->mountpoint_index
>= state
->allocated_size
) {
4128 guint32 newsize
= (state
->allocated_size
<< 1) + 1;
4129 gchar
*newbuf
= (gchar
*)g_malloc0 (newsize
* sizeof (gchar
));
4131 memcpy (newbuf
, state
->mountpoint_allocated
, state
->mountpoint_index
);
4132 g_free (state
->mountpoint_allocated
);
4133 state
->mountpoint_allocated
= newbuf
;
4134 state
->allocated_size
= newsize
;
4136 state
->mountpoint_allocated
[state
->mountpoint_index
++] = ch
;
4138 if (state
->mountpoint_index
>= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER
) {
4139 state
->allocated_size
= (state
->mountpoint_index
<< 1) + 1;
4140 state
->mountpoint_allocated
= (gchar
*)g_malloc0 (state
->allocated_size
* sizeof (gchar
));
4141 memcpy (state
->mountpoint_allocated
, state
->mountpoint
, state
->mountpoint_index
);
4142 state
->mountpoint_allocated
[state
->mountpoint_index
++] = ch
;
4144 state
->mountpoint
[state
->mountpoint_index
++] = ch
;
4149 add_drive_string (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
4151 gboolean quit
= FALSE
;
4152 gboolean ignore_entry
;
4154 if (state
->fsname_index
== 1 && state
->fsname
[0] == '/')
4155 ignore_entry
= FALSE
;
4156 else if (memcmp ("overlay", state
->fsname
, state
->fsname_index
) == 0 ||
4157 memcmp ("aufs", state
->fstype
, state
->fstype_index
) == 0) {
4158 /* Don't ignore overlayfs and aufs - these might be used on Docker
4159 * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */
4160 ignore_entry
= FALSE
;
4161 } else if (state
->fsname_index
== 0 || memcmp ("none", state
->fsname
, state
->fsname_index
) == 0) {
4162 ignore_entry
= TRUE
;
4163 } else if (state
->fstype_index
>= 5 && memcmp ("fuse.", state
->fstype
, 5) == 0) {
4164 /* Ignore GNOME's gvfs */
4165 if (state
->fstype_index
== 21 && memcmp ("fuse.gvfs-fuse-daemon", state
->fstype
, state
->fstype_index
) == 0)
4166 ignore_entry
= TRUE
;
4168 ignore_entry
= FALSE
;
4169 } else if (state
->fstype_index
== 3 && memcmp ("nfs", state
->fstype
, state
->fstype_index
) == 0)
4170 ignore_entry
= FALSE
;
4172 ignore_entry
= TRUE
;
4174 if (!ignore_entry
) {
4177 gchar
*mountpoint
= state
->mountpoint_allocated
? state
->mountpoint_allocated
: state
->mountpoint
;
4179 unescape_octal (mountpoint
);
4180 dir
= g_utf8_to_utf16 (mountpoint
, -1, NULL
, &length
, NULL
);
4181 if (state
->total
+ length
+ 1 > len
) {
4183 state
->total
= len
* 2;
4186 memcpy (buf
+ state
->total
, dir
, sizeof (gunichar2
) * length
);
4187 state
->total
+= length
;
4191 state
->fsname_index
= 0;
4192 state
->fstype_index
= 0;
4198 mono_w32file_get_logical_drive (guint32 len
, gunichar2
*buf
)
4200 return GetLogicalDriveStrings_Mtab (len
, buf
);
4204 GetLogicalDriveStrings_Mtab (guint32 len
, gunichar2
*buf
)
4207 gunichar2
*ptr
, *dir
;
4208 glong length
, total
= 0;
4212 memset (buf
, 0, sizeof (gunichar2
) * (len
+ 1));
4217 /* Sigh, mntent and friends don't work well.
4218 * It stops on the first line that doesn't begin with a '/'.
4219 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4221 fp
= fopen ("/etc/mtab", "rt");
4225 fp
= fopen ("/etc/mnttab", "rt");
4235 fgets_res
= fgets (buffer
, 512, fp
);
4242 splitted
= g_strsplit (buffer
, " ", 0);
4243 if (!*splitted
|| !*(splitted
+ 1)) {
4244 g_strfreev (splitted
);
4248 unescape_octal (*(splitted
+ 1));
4249 dir
= g_utf8_to_utf16 (*(splitted
+ 1), -1, NULL
, &length
, NULL
);
4250 g_strfreev (splitted
);
4251 if (total
+ length
+ 1 > len
) {
4256 return len
* 2; /* guess */
4259 memcpy (ptr
+ total
, dir
, sizeof (gunichar2
) * length
);
4261 total
+= length
+ 1;
4268 /* Commented out, does not work with my mtab!!! - Gonz */
4269 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4273 gunichar2
*ptr
, *dir
;
4274 glong len
, total
= 0;
4278 fp
= setmntent ("/etc/mtab", "rt");
4282 fp
= setmntent ("/etc/mnttab", "rt");
4291 mnt
= getmntent (fp
);
4295 g_print ("GOT %s\n", mnt
->mnt_dir
);
4296 dir
= g_utf8_to_utf16 (mnt
->mnt_dir
, &len
, NULL
, NULL
, NULL
);
4297 if (total
+ len
+ 1 > len
) {
4301 return len
* 2; /* guess */
4304 memcpy (ptr
+ total
, dir
, sizeof (gunichar2
) * len
);
4318 #ifndef PLATFORM_NO_DRIVEINFO
4320 mono_w32file_get_disk_free_space (const gunichar2
*path_name
, guint64
*free_bytes_avail
, guint64
*total_number_of_bytes
, guint64
*total_number_of_free_bytes
)
4322 g_assert (free_bytes_avail
);
4323 g_assert (total_number_of_bytes
);
4324 g_assert (total_number_of_free_bytes
);
4326 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
4328 struct statvfs fsstat
;
4329 #elif defined(HAVE_STATFS)
4330 struct statfs fsstat
;
4332 gboolean isreadonly
;
4333 gchar
*utf8_path_name
;
4335 unsigned long block_size
;
4337 if (path_name
== NULL
) {
4338 utf8_path_name
= g_strdup (g_get_current_dir());
4339 if (utf8_path_name
== NULL
) {
4340 mono_w32error_set_last (ERROR_DIRECTORY
);
4345 utf8_path_name
= mono_unicode_to_external (path_name
);
4346 if (utf8_path_name
== NULL
) {
4347 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
4349 mono_w32error_set_last (ERROR_INVALID_NAME
);
4357 ret
= statvfs (utf8_path_name
, &fsstat
);
4359 isreadonly
= ((fsstat
.f_flag
& ST_RDONLY
) == ST_RDONLY
);
4360 block_size
= fsstat
.f_frsize
;
4361 #elif defined(HAVE_STATFS)
4363 ret
= statfs (utf8_path_name
, &fsstat
);
4365 #if defined (MNT_RDONLY)
4366 isreadonly
= ((fsstat
.f_flags
& MNT_RDONLY
) == MNT_RDONLY
);
4367 #elif defined (MS_RDONLY)
4368 isreadonly
= ((fsstat
.f_flags
& MS_RDONLY
) == MS_RDONLY
);
4370 block_size
= fsstat
.f_bsize
;
4372 } while(ret
== -1 && errno
== EINTR
);
4374 g_free(utf8_path_name
);
4377 _wapi_set_last_error_from_errno ();
4378 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: statvfs failed: %s", __func__
, g_strerror (errno
));
4382 /* total number of free bytes for non-root */
4384 *free_bytes_avail
= 0;
4386 *free_bytes_avail
= block_size
* (guint64
)fsstat
.f_bavail
;
4388 /* total number of bytes available for non-root */
4389 *total_number_of_bytes
= block_size
* (guint64
)fsstat
.f_blocks
;
4391 /* total number of bytes available for root */
4393 *total_number_of_free_bytes
= 0;
4395 *total_number_of_free_bytes
= block_size
* (guint64
)fsstat
.f_bfree
;
4399 #endif // PLATFORM_NO_DRIVEINFO
4402 * General Unix support
4407 // http://man7.org/linux/man-pages/man2/statfs.2.html
4409 // The __fsword_t type used for various fields in the statfs structure
4410 // definition is a glibc internal type, not intended for public use.
4411 // This leaves the programmer in a bit of a conundrum when trying to
4412 // copy or compare these fields to local variables in a program. Using
4413 // unsigned int for such variables suffices on most systems.
4415 // Let's hope "most" is enough, and that it works with other libc.
4418 const gchar
* fstype
;
4421 static const _wapi_drive_type _wapi_drive_types
[] = {
4423 { DRIVE_REMOTE
, "afp" },
4424 { DRIVE_REMOTE
, "afpfs" },
4425 { DRIVE_REMOTE
, "autofs" },
4426 { DRIVE_CDROM
, "cddafs" },
4427 { DRIVE_CDROM
, "cd9660" },
4428 { DRIVE_RAMDISK
, "devfs" },
4429 { DRIVE_RAMDISK
, "nullfs" },
4430 { DRIVE_FIXED
, "exfat" },
4431 { DRIVE_RAMDISK
, "fdesc" },
4432 { DRIVE_REMOTE
, "ftp" },
4433 { DRIVE_FIXED
, "hfs" },
4434 { DRIVE_FIXED
, "apfs" },
4435 { DRIVE_REMOTE
, "kbfuse" },
4436 { DRIVE_FIXED
, "msdos" },
4437 { DRIVE_REMOTE
, "nfs" },
4438 { DRIVE_FIXED
, "ntfs" },
4439 { DRIVE_REMOTE
, "smbfs" },
4440 { DRIVE_FIXED
, "udf" },
4441 { DRIVE_REMOTE
, "webdav" },
4442 { DRIVE_FIXED
, "ufsd_NTFS"},
4443 { DRIVE_UNKNOWN
, NULL
}
4445 { DRIVE_FIXED
, ADFS_SUPER_MAGIC
, "adfs"},
4446 { DRIVE_FIXED
, AFFS_SUPER_MAGIC
, "affs"},
4447 { DRIVE_REMOTE
, AFS_SUPER_MAGIC
, "afs"},
4448 { DRIVE_RAMDISK
, AUTOFS_SUPER_MAGIC
, "autofs"},
4449 { DRIVE_RAMDISK
, AUTOFS_SBI_MAGIC
, "autofs4"},
4450 { DRIVE_REMOTE
, CODA_SUPER_MAGIC
, "coda" },
4451 { DRIVE_RAMDISK
, CRAMFS_MAGIC
, "cramfs"},
4452 { DRIVE_RAMDISK
, CRAMFS_MAGIC_WEND
, "cramfs"},
4453 { DRIVE_REMOTE
, CIFS_MAGIC_NUMBER
, "cifs"},
4454 { DRIVE_RAMDISK
, DEBUGFS_MAGIC
, "debugfs"},
4455 { DRIVE_RAMDISK
, SYSFS_MAGIC
, "sysfs"},
4456 { DRIVE_RAMDISK
, SECURITYFS_MAGIC
, "securityfs"},
4457 { DRIVE_RAMDISK
, SELINUX_MAGIC
, "selinuxfs"},
4458 { DRIVE_RAMDISK
, RAMFS_MAGIC
, "ramfs"},
4459 { DRIVE_FIXED
, SQUASHFS_MAGIC
, "squashfs"},
4460 { DRIVE_FIXED
, EFS_SUPER_MAGIC
, "efs"},
4461 { DRIVE_FIXED
, EXT2_SUPER_MAGIC
, "ext"},
4462 { DRIVE_FIXED
, EXT3_SUPER_MAGIC
, "ext"},
4463 { DRIVE_FIXED
, EXT4_SUPER_MAGIC
, "ext"},
4464 { DRIVE_REMOTE
, XENFS_SUPER_MAGIC
, "xenfs"},
4465 { DRIVE_FIXED
, BTRFS_SUPER_MAGIC
, "btrfs"},
4466 { DRIVE_FIXED
, HFS_SUPER_MAGIC
, "hfs"},
4467 { DRIVE_FIXED
, HFSPLUS_SUPER_MAGIC
, "hfsplus"},
4468 { DRIVE_FIXED
, HPFS_SUPER_MAGIC
, "hpfs"},
4469 { DRIVE_RAMDISK
, HUGETLBFS_MAGIC
, "hugetlbfs"},
4470 { DRIVE_CDROM
, ISOFS_SUPER_MAGIC
, "iso"},
4471 { DRIVE_FIXED
, JFFS2_SUPER_MAGIC
, "jffs2"},
4472 { DRIVE_RAMDISK
, ANON_INODE_FS_MAGIC
, "anon_inode"},
4473 { DRIVE_FIXED
, JFS_SUPER_MAGIC
, "jfs"},
4474 { DRIVE_FIXED
, MINIX_SUPER_MAGIC
, "minix"},
4475 { DRIVE_FIXED
, MINIX_SUPER_MAGIC2
, "minix v2"},
4476 { DRIVE_FIXED
, MINIX2_SUPER_MAGIC
, "minix2"},
4477 { DRIVE_FIXED
, MINIX2_SUPER_MAGIC2
, "minix2 v2"},
4478 { DRIVE_FIXED
, MINIX3_SUPER_MAGIC
, "minix3"},
4479 { DRIVE_FIXED
, MSDOS_SUPER_MAGIC
, "msdos"},
4480 { DRIVE_REMOTE
, NCP_SUPER_MAGIC
, "ncp"},
4481 { DRIVE_REMOTE
, NFS_SUPER_MAGIC
, "nfs"},
4482 { DRIVE_FIXED
, NTFS_SB_MAGIC
, "ntfs"},
4483 { DRIVE_RAMDISK
, OPENPROM_SUPER_MAGIC
, "openpromfs"},
4484 { DRIVE_RAMDISK
, PROC_SUPER_MAGIC
, "proc"},
4485 { DRIVE_FIXED
, QNX4_SUPER_MAGIC
, "qnx4"},
4486 { DRIVE_FIXED
, REISERFS_SUPER_MAGIC
, "reiserfs"},
4487 { DRIVE_RAMDISK
, ROMFS_MAGIC
, "romfs"},
4488 { DRIVE_REMOTE
, SMB_SUPER_MAGIC
, "samba"},
4489 { DRIVE_RAMDISK
, CGROUP_SUPER_MAGIC
, "cgroupfs"},
4490 { DRIVE_RAMDISK
, FUTEXFS_SUPER_MAGIC
, "futexfs"},
4491 { DRIVE_FIXED
, SYSV2_SUPER_MAGIC
, "sysv2"},
4492 { DRIVE_FIXED
, SYSV4_SUPER_MAGIC
, "sysv4"},
4493 { DRIVE_RAMDISK
, TMPFS_MAGIC
, "tmpfs"},
4494 { DRIVE_RAMDISK
, DEVPTS_SUPER_MAGIC
, "devpts"},
4495 { DRIVE_CDROM
, UDF_SUPER_MAGIC
, "udf"},
4496 { DRIVE_FIXED
, UFS_MAGIC
, "ufs"},
4497 { DRIVE_FIXED
, UFS_MAGIC_BW
, "ufs"},
4498 { DRIVE_FIXED
, UFS2_MAGIC
, "ufs2"},
4499 { DRIVE_FIXED
, UFS_CIGAM
, "ufs"},
4500 { DRIVE_RAMDISK
, USBDEVICE_SUPER_MAGIC
, "usbdev"},
4501 { DRIVE_FIXED
, XENIX_SUPER_MAGIC
, "xenix"},
4502 { DRIVE_FIXED
, XFS_SB_MAGIC
, "xfs"},
4503 { DRIVE_RAMDISK
, FUSE_SUPER_MAGIC
, "fuse"},
4504 { DRIVE_FIXED
, V9FS_MAGIC
, "9p"},
4505 { DRIVE_REMOTE
, CEPH_SUPER_MAGIC
, "ceph"},
4506 { DRIVE_RAMDISK
, CONFIGFS_MAGIC
, "configfs"},
4507 { DRIVE_RAMDISK
, ECRYPTFS_SUPER_MAGIC
, "eCryptfs"},
4508 { DRIVE_FIXED
, EXOFS_SUPER_MAGIC
, "exofs"},
4509 { DRIVE_FIXED
, VXFS_SUPER_MAGIC
, "vxfs"},
4510 { DRIVE_FIXED
, VXFS_OLT_MAGIC
, "vxfs_olt"},
4511 { DRIVE_REMOTE
, GFS2_MAGIC
, "gfs2"},
4512 { DRIVE_FIXED
, LOGFS_MAGIC_U32
, "logfs"},
4513 { DRIVE_FIXED
, OCFS2_SUPER_MAGIC
, "ocfs2"},
4514 { DRIVE_FIXED
, OMFS_MAGIC
, "omfs"},
4515 { DRIVE_FIXED
, UBIFS_SUPER_MAGIC
, "ubifs"},
4516 { DRIVE_UNKNOWN
, 0, NULL
}
4518 { DRIVE_RAMDISK
, "ramfs" },
4519 { DRIVE_RAMDISK
, "tmpfs" },
4520 { DRIVE_RAMDISK
, "proc" },
4521 { DRIVE_RAMDISK
, "sysfs" },
4522 { DRIVE_RAMDISK
, "debugfs" },
4523 { DRIVE_RAMDISK
, "devpts" },
4524 { DRIVE_RAMDISK
, "securityfs" },
4525 { DRIVE_RAMDISK
, "procfs" }, // AIX procfs
4526 { DRIVE_RAMDISK
, "namefs" }, // AIX soft mounts
4527 { DRIVE_RAMDISK
, "nullfs" },
4528 { DRIVE_CDROM
, "iso9660" },
4529 { DRIVE_CDROM
, "cdrfs" }, // AIX ISO9660 CDs
4530 { DRIVE_CDROM
, "udfs" }, // AIX UDF CDs
4531 { DRIVE_CDROM
, "QOPT" }, // IBM i CD mount
4532 { DRIVE_FIXED
, "ext2" },
4533 { DRIVE_FIXED
, "ext3" },
4534 { DRIVE_FIXED
, "ext4" },
4535 { DRIVE_FIXED
, "sysv" },
4536 { DRIVE_FIXED
, "reiserfs" },
4537 { DRIVE_FIXED
, "ufs" },
4538 { DRIVE_FIXED
, "vfat" },
4539 { DRIVE_FIXED
, "msdos" },
4540 { DRIVE_FIXED
, "udf" },
4541 { DRIVE_FIXED
, "hfs" },
4542 { DRIVE_FIXED
, "hpfs" },
4543 { DRIVE_FIXED
, "qnx4" },
4544 { DRIVE_FIXED
, "ntfs" },
4545 { DRIVE_FIXED
, "ntfs-3g" },
4546 { DRIVE_FIXED
, "jfs" }, // IBM JFS
4547 { DRIVE_FIXED
, "jfs2" }, // IBM JFS (AIX defalt filesystem)
4548 { DRIVE_FIXED
, "EPFS" }, // IBM i IFS (root and QOpenSys)
4549 { DRIVE_FIXED
, "EPFSP" }, // IBM i auxiliary storage pool FS
4550 { DRIVE_FIXED
, "QSYS" }, // IBM i native system libraries
4551 { DRIVE_FIXED
, "QDLS" }, // IBM i legacy S/36 directories
4552 { DRIVE_REMOTE
, "smbfs" },
4553 { DRIVE_REMOTE
, "fuse" },
4554 { DRIVE_REMOTE
, "nfs" },
4555 { DRIVE_REMOTE
, "nfs4" },
4556 { DRIVE_REMOTE
, "cifs" },
4557 { DRIVE_REMOTE
, "ncpfs" },
4558 { DRIVE_REMOTE
, "coda" },
4559 { DRIVE_REMOTE
, "afs" },
4560 { DRIVE_REMOTE
, "nfs3" },
4561 { DRIVE_REMOTE
, "stnfs" }, // AIX "short-term" NFS
4562 { DRIVE_REMOTE
, "autofs" }, // AIX automounter NFS
4563 { DRIVE_REMOTE
, "cachefs" }, // AIX cached NFS
4564 { DRIVE_REMOTE
, "NFS" }, // IBM i NFS
4565 { DRIVE_REMOTE
, "QNETC" }, // IBM i CIFS
4566 { DRIVE_REMOTE
, "QRFS" }, // IBM i native remote FS
4567 { DRIVE_UNKNOWN
, NULL
}
4572 static guint32
_wapi_get_drive_type(unsigned f_type
)
4574 const _wapi_drive_type
*current
;
4576 current
= &_wapi_drive_types
[0];
4577 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4578 if (current
->fstypeid
== f_type
)
4579 return current
->drive_type
;
4583 return DRIVE_UNKNOWN
;
4586 static guint32
_wapi_get_drive_type(const gchar
* fstype
)
4588 const _wapi_drive_type
*current
;
4590 current
= &_wapi_drive_types
[0];
4591 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4592 if (strcmp (current
->fstype
, fstype
) == 0)
4593 return current
->drive_type
;
4598 return DRIVE_UNKNOWN
;
4602 #if defined (HOST_DARWIN) || defined (__linux__) || defined (_AIX)
4604 GetDriveTypeFromPath (const gchar
*utf8_root_path_name
)
4615 res
= statvfs (utf8_root_path_name
, &buf
);
4617 res
= statfs (utf8_root_path_name
, &buf
);
4621 return DRIVE_UNKNOWN
;
4623 return _wapi_get_drive_type (buf
.f_fstypename
);
4624 #elif defined (_AIX)
4625 return _wapi_get_drive_type (buf
.f_basetype
);
4627 return _wapi_get_drive_type (buf
.f_type
);
4632 GetDriveTypeFromPath (const gchar
*utf8_root_path_name
)
4640 fp
= fopen ("/etc/mtab", "rt");
4644 fp
= fopen ("/etc/mnttab", "rt");
4647 return(DRIVE_UNKNOWN
);
4650 drive_type
= DRIVE_NO_ROOT_DIR
;
4654 fgets_res
= fgets (buffer
, 512, fp
);
4656 if (fgets_res
== NULL
)
4658 splitted
= g_strsplit (buffer
, " ", 0);
4659 if (!*splitted
|| !*(splitted
+ 1) || !*(splitted
+ 2)) {
4660 g_strfreev (splitted
);
4664 /* compare given root_path_name with the one from mtab,
4665 if length of utf8_root_path_name is zero it must be the root dir */
4666 if (strcmp (*(splitted
+ 1), utf8_root_path_name
) == 0 ||
4667 (strcmp (*(splitted
+ 1), "/") == 0 && strlen (utf8_root_path_name
) == 0)) {
4668 drive_type
= _wapi_get_drive_type (*(splitted
+ 2));
4669 /* it is possible this path might be mounted again with
4670 a known type...keep looking */
4671 if (drive_type
!= DRIVE_UNKNOWN
) {
4672 g_strfreev (splitted
);
4677 g_strfreev (splitted
);
4688 ves_icall_System_IO_DriveInfo_GetDriveType (const gunichar2
*root_path_name
, gint32 root_path_name_length
, MonoError
*error
)
4690 // FIXME Check for embedded nuls here or in managed.
4692 gchar
*utf8_root_path_name
;
4695 if (root_path_name
== NULL
) {
4696 utf8_root_path_name
= g_strdup (g_get_current_dir());
4697 if (utf8_root_path_name
== NULL
) {
4698 return(DRIVE_NO_ROOT_DIR
);
4702 utf8_root_path_name
= mono_unicode_to_external (root_path_name
);
4703 if (utf8_root_path_name
== NULL
) {
4704 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: unicode conversion returned NULL", __func__
);
4705 return(DRIVE_NO_ROOT_DIR
);
4708 /* strip trailing slash for compare below */
4709 if (g_str_has_suffix(utf8_root_path_name
, "/") && utf8_root_path_name
[1] != 0) {
4710 utf8_root_path_name
[strlen(utf8_root_path_name
) - 1] = 0;
4713 drive_type
= GetDriveTypeFromPath (utf8_root_path_name
);
4714 g_free (utf8_root_path_name
);
4716 return (drive_type
);
4719 #if defined (HOST_DARWIN) || defined (__linux__) || defined(HOST_BSD) || defined(__FreeBSD_kernel__) || defined(__HAIKU__) || defined(_AIX)
4721 get_fstypename (gchar
*utfpath
)
4724 /* statvfs offers the FS type name, easily, no need to iterate */
4725 struct statvfs stat
;
4729 statvfs_res
= statvfs (utfpath
, &stat
);
4732 if (statvfs_res
!= -1) {
4733 return g_strdup (stat
.f_basetype
);
4737 #elif defined (HOST_DARWIN) || defined (__linux__)
4740 const _wapi_drive_type
*current
;
4744 statfs_res
= statfs (utfpath
, &stat
);
4746 if (statfs_res
== -1)
4749 return g_strdup (stat
.f_fstypename
);
4751 current
= &_wapi_drive_types
[0];
4752 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4753 if (stat
.f_type
== current
->fstypeid
)
4754 return g_strdup (current
->fstype
);
4764 /* Linux has struct statfs which has a different layout */
4766 mono_w32file_get_file_system_type (const gunichar2
*path
, gunichar2
*fsbuffer
, gint fsbuffersize
)
4770 gboolean status
= FALSE
;
4773 // We only support getting the file system type
4774 if (fsbuffer
== NULL
)
4777 utfpath
= mono_unicode_to_external (path
);
4778 if ((fstypename
= get_fstypename (utfpath
)) != NULL
){
4779 gunichar2
*ret
= g_utf8_to_utf16 (fstypename
, -1, NULL
, &len
, NULL
);
4780 if (ret
!= NULL
&& len
< fsbuffersize
){
4781 memcpy (fsbuffer
, ret
, len
* sizeof (gunichar2
));
4787 g_free (fstypename
);
4795 LockFile (gpointer handle
, guint32 offset_low
, guint32 offset_high
, guint32 length_low
, guint32 length_high
)
4797 FileHandle
*filehandle
;
4799 off_t offset
, length
;
4801 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
4802 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
4806 if (((MonoFDHandle
*) filehandle
)->type
!= MONO_FDTYPE_FILE
) {
4807 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
4808 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4812 if (!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_ALL
))) {
4813 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
4814 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
4815 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4819 #ifdef HAVE_LARGE_FILE_SUPPORT
4820 offset
= ((gint64
)offset_high
<< 32) | offset_low
;
4821 length
= ((gint64
)length_high
<< 32) | length_low
;
4823 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Locking fd %d, offset %" G_GINT64_FORMAT
", length %" G_GINT64_FORMAT
, __func__
, ((MonoFDHandle
*) filehandle
)->fd
, (gint64
) offset
, (gint64
) length
);
4825 if (offset_high
> 0 || length_high
> 0) {
4826 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
4827 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4831 offset
= offset_low
;
4832 length
= length_low
;
4834 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Locking fd %d, offset %" G_GINT64_FORMAT
", length %" G_GINT64_FORMAT
, __func__
, ((MonoFDHandle
*) filehandle
)->fd
, (gint64
) offset
, (gint64
) length
);
4837 ret
= _wapi_lock_file_region (((MonoFDHandle
*) filehandle
)->fd
, offset
, length
);
4839 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4844 UnlockFile (gpointer handle
, guint32 offset_low
, guint32 offset_high
, guint32 length_low
, guint32 length_high
)
4846 FileHandle
*filehandle
;
4848 off_t offset
, length
;
4850 if (!mono_fdhandle_lookup_and_ref(GPOINTER_TO_INT(handle
), (MonoFDHandle
**) &filehandle
)) {
4851 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
4855 if (((MonoFDHandle
*) filehandle
)->type
!= MONO_FDTYPE_FILE
) {
4856 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
4857 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4861 if (!(filehandle
->fileaccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_ALL
))) {
4862 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, ((MonoFDHandle
*) filehandle
)->fd
, filehandle
->fileaccess
);
4863 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
4864 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4868 #ifdef HAVE_LARGE_FILE_SUPPORT
4869 offset
= ((gint64
)offset_high
<< 32) | offset_low
;
4870 length
= ((gint64
)length_high
<< 32) | length_low
;
4872 offset
= offset_low
;
4873 length
= length_low
;
4875 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_FILE
, "%s: Unlocking fd %d, offset %" G_GINT64_FORMAT
", length %" G_GINT64_FORMAT
, __func__
, ((MonoFDHandle
*) filehandle
)->fd
, (gint64
) offset
, (gint64
) length
);
4877 ret
= _wapi_unlock_file_region (((MonoFDHandle
*) filehandle
)->fd
, offset
, length
);
4879 mono_fdhandle_unref ((MonoFDHandle
*) filehandle
);
4884 mono_w32file_init (void)
4886 MonoFDHandleCallback file_data_callbacks
;
4887 memset (&file_data_callbacks
, 0, sizeof (file_data_callbacks
));
4888 file_data_callbacks
.close
= file_data_close
;
4889 file_data_callbacks
.destroy
= file_data_destroy
;
4891 mono_fdhandle_register (MONO_FDTYPE_FILE
, &file_data_callbacks
);
4892 mono_fdhandle_register (MONO_FDTYPE_CONSOLE
, &file_data_callbacks
);
4893 mono_fdhandle_register (MONO_FDTYPE_PIPE
, &file_data_callbacks
);
4895 mono_coop_mutex_init (&file_share_mutex
);
4897 finds
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, finds_remove
);
4898 mono_coop_mutex_init (&finds_mutex
);
4901 libc_handle
= mono_dl_open ("/usr/lib/libc.dylib", 0, NULL
);
4902 g_assert (libc_handle
);
4903 g_free (mono_dl_symbol (libc_handle
, "clonefile", (void**)&clonefile_ptr
));
4906 if (g_hasenv ("MONO_STRICT_IO_EMULATION"))
4907 lock_while_writing
= TRUE
;
4911 mono_w32file_cleanup (void)
4913 mono_coop_mutex_destroy (&file_share_mutex
);
4915 if (file_share_table
)
4916 g_hash_table_destroy (file_share_table
);
4918 g_hash_table_destroy (finds
);
4919 mono_coop_mutex_destroy (&finds_mutex
);
4922 mono_dl_close (libc_handle
);
4927 mono_w32file_move (const gunichar2
*path
, const gunichar2
*dest
, gint32
*error
)
4931 result
= MoveFile (path
, dest
);
4933 *error
= mono_w32error_get_last ();
4938 mono_w32file_copy (const gunichar2
*path
, const gunichar2
*dest
, gboolean overwrite
, gint32
*error
)
4942 result
= CopyFile (path
, dest
, !overwrite
);
4944 *error
= mono_w32error_get_last ();
4950 mono_w32file_replace (const gunichar2
*destination_file_name
, const gunichar2
*source_file_name
, const gunichar2
*destination_backup_file_name
, guint32 flags
, gint32
*error
)
4954 result
= ReplaceFile (destination_file_name
, source_file_name
, destination_backup_file_name
, flags
, NULL
, NULL
);
4956 *error
= mono_w32error_get_last ();
4961 mono_w32file_get_file_size (gpointer handle
, gint32
*error
)
4966 length
= GetFileSize (handle
, &length_hi
);
4967 if(length
==INVALID_FILE_SIZE
) {
4968 *error
=mono_w32error_get_last ();
4971 return length
| ((gint64
)length_hi
<< 32);
4975 mono_w32file_lock (gpointer handle
, gint64 position
, gint64 length
, gint32
*error
)
4979 result
= LockFile (handle
, position
& 0xFFFFFFFF, position
>> 32, length
& 0xFFFFFFFF, length
>> 32);
4981 *error
= mono_w32error_get_last ();
4986 mono_w32file_unlock (gpointer handle
, gint64 position
, gint64 length
, gint32
*error
)
4990 result
= UnlockFile (handle
, position
& 0xFFFFFFFF, position
>> 32, length
& 0xFFFFFFFF, length
>> 32);
4992 *error
= mono_w32error_get_last ();
4997 mono_w32file_get_console_input (void)
4999 return mono_w32file_get_std_handle (STD_INPUT_HANDLE
);
5003 mono_w32file_get_console_output (void)
5005 return mono_w32file_get_std_handle (STD_OUTPUT_HANDLE
);
5009 mono_w32file_get_console_error (void)
5011 return mono_w32file_get_std_handle (STD_ERROR_HANDLE
);