2 * io.c: File, console and find handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 * Copyright (c) 2002-2006 Novell, Inc.
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com).
19 #ifdef HAVE_SYS_STATVFS_H
20 #include <sys/statvfs.h>
22 #if defined(HAVE_SYS_STATFS_H)
23 #include <sys/statfs.h>
25 #if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
26 #include <sys/param.h>
27 #include <sys/mount.h>
29 #include <sys/types.h>
33 #include <sys/ioctl.h>
35 #include <mono/utils/linux_magic.h>
38 #include <mono/io-layer/wapi.h>
39 #include <mono/io-layer/wapi-private.h>
40 #include <mono/io-layer/handles-private.h>
41 #include <mono/io-layer/io-private.h>
42 #include <mono/io-layer/timefuncs-private.h>
43 #include <mono/io-layer/thread-private.h>
44 #include <mono/io-layer/io-portability.h>
45 #include <mono/utils/strenc.h>
48 #define DEBUG(...) g_message(__VA_ARGS__)
49 #define DEBUG_ENABLED 1
54 static void file_close (gpointer handle
, gpointer data
);
55 static WapiFileType
file_getfiletype(void);
56 static gboolean
file_read(gpointer handle
, gpointer buffer
,
57 guint32 numbytes
, guint32
*bytesread
,
58 WapiOverlapped
*overlapped
);
59 static gboolean
file_write(gpointer handle
, gconstpointer buffer
,
60 guint32 numbytes
, guint32
*byteswritten
,
61 WapiOverlapped
*overlapped
);
62 static gboolean
file_flush(gpointer handle
);
63 static guint32
file_seek(gpointer handle
, gint32 movedistance
,
64 gint32
*highmovedistance
, WapiSeekMethod method
);
65 static gboolean
file_setendoffile(gpointer handle
);
66 static guint32
file_getfilesize(gpointer handle
, guint32
*highsize
);
67 static gboolean
file_getfiletime(gpointer handle
, WapiFileTime
*create_time
,
68 WapiFileTime
*last_access
,
69 WapiFileTime
*last_write
);
70 static gboolean
file_setfiletime(gpointer handle
,
71 const WapiFileTime
*create_time
,
72 const WapiFileTime
*last_access
,
73 const WapiFileTime
*last_write
);
74 static guint32
GetDriveTypeFromPath (const gchar
*utf8_root_path_name
);
76 /* File handle is only signalled for overlapped IO */
77 struct _WapiHandleOps _wapi_file_ops
= {
78 file_close
, /* close */
82 NULL
, /* special_wait */
86 void _wapi_file_details (gpointer handle_info
)
88 struct _WapiHandle_file
*file
= (struct _WapiHandle_file
*)handle_info
;
90 g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u",
92 file
->fileaccess
&GENERIC_READ
?'R':'.',
93 file
->fileaccess
&GENERIC_WRITE
?'W':'.',
94 file
->fileaccess
&GENERIC_EXECUTE
?'X':'.',
95 file
->sharemode
&FILE_SHARE_READ
?'R':'.',
96 file
->sharemode
&FILE_SHARE_WRITE
?'W':'.',
97 file
->sharemode
&FILE_SHARE_DELETE
?'D':'.',
101 static void console_close (gpointer handle
, gpointer data
);
102 static WapiFileType
console_getfiletype(void);
103 static gboolean
console_read(gpointer handle
, gpointer buffer
,
104 guint32 numbytes
, guint32
*bytesread
,
105 WapiOverlapped
*overlapped
);
106 static gboolean
console_write(gpointer handle
, gconstpointer buffer
,
107 guint32 numbytes
, guint32
*byteswritten
,
108 WapiOverlapped
*overlapped
);
110 /* Console is mostly the same as file, except it can block waiting for
113 struct _WapiHandleOps _wapi_console_ops
= {
114 console_close
, /* close */
118 NULL
, /* special_wait */
122 void _wapi_console_details (gpointer handle_info
)
124 _wapi_file_details (handle_info
);
127 /* Find handle has no ops.
129 struct _WapiHandleOps _wapi_find_ops
= {
134 NULL
, /* special_wait */
138 static void pipe_close (gpointer handle
, gpointer data
);
139 static WapiFileType
pipe_getfiletype (void);
140 static gboolean
pipe_read (gpointer handle
, gpointer buffer
, guint32 numbytes
,
141 guint32
*bytesread
, WapiOverlapped
*overlapped
);
142 static gboolean
pipe_write (gpointer handle
, gconstpointer buffer
,
143 guint32 numbytes
, guint32
*byteswritten
,
144 WapiOverlapped
*overlapped
);
148 struct _WapiHandleOps _wapi_pipe_ops
= {
149 pipe_close
, /* close */
153 NULL
, /* special_wait */
157 void _wapi_pipe_details (gpointer handle_info
)
159 _wapi_file_details (handle_info
);
162 static const struct {
163 /* File, console and pipe handles */
164 WapiFileType (*getfiletype
)(void);
166 /* File, console and pipe handles */
167 gboolean (*readfile
)(gpointer handle
, gpointer buffer
,
168 guint32 numbytes
, guint32
*bytesread
,
169 WapiOverlapped
*overlapped
);
170 gboolean (*writefile
)(gpointer handle
, gconstpointer buffer
,
171 guint32 numbytes
, guint32
*byteswritten
,
172 WapiOverlapped
*overlapped
);
173 gboolean (*flushfile
)(gpointer handle
);
176 guint32 (*seek
)(gpointer handle
, gint32 movedistance
,
177 gint32
*highmovedistance
, WapiSeekMethod method
);
178 gboolean (*setendoffile
)(gpointer handle
);
179 guint32 (*getfilesize
)(gpointer handle
, guint32
*highsize
);
180 gboolean (*getfiletime
)(gpointer handle
, WapiFileTime
*create_time
,
181 WapiFileTime
*last_access
,
182 WapiFileTime
*last_write
);
183 gboolean (*setfiletime
)(gpointer handle
,
184 const WapiFileTime
*create_time
,
185 const WapiFileTime
*last_access
,
186 const WapiFileTime
*last_write
);
187 } io_ops
[WAPI_HANDLE_COUNT
]={
188 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
191 file_read
, file_write
,
192 file_flush
, file_seek
,
198 {console_getfiletype
,
201 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
203 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
205 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
207 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
209 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
210 /* socket (will need at least read and write) */
211 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
213 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
215 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
220 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
223 static mono_once_t io_ops_once
=MONO_ONCE_INIT
;
224 static gboolean lock_while_writing
= FALSE
;
226 static void io_ops_init (void)
228 /* _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
229 /* WAPI_HANDLE_CAP_WAIT); */
230 /* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
231 /* WAPI_HANDLE_CAP_WAIT); */
233 if (g_getenv ("MONO_STRICT_IO_EMULATION") != NULL
) {
234 lock_while_writing
= TRUE
;
238 /* Some utility functions.
242 * Check if a file is writable by the current user.
244 * This is is a best effort kind of thing. It assumes a reasonable sane set
245 * of permissions by the underlying OS.
247 * We assume that basic unix permission bits are authoritative. Which might not
248 * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc)
250 * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file.
252 * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent
253 * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous
254 * and should not be used with a dynamic runtime.
257 is_file_writable (struct stat
*st
, const char *path
)
259 /* Is it globally writable? */
260 if (st
->st_mode
& S_IWOTH
)
263 /* Am I the owner? */
264 if ((st
->st_uid
== geteuid ()) && (st
->st_mode
& S_IWUSR
))
267 /* Am I in the same group? */
268 if ((st
->st_gid
== getegid ()) && (st
->st_mode
& S_IWGRP
))
271 /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid
272 * but it's the only sane option we have on unix.
274 return access (path
, W_OK
) == 0;
278 static guint32
_wapi_stat_to_file_attributes (const gchar
*pathname
,
285 /* FIXME: this could definitely be better, but there seems to
286 * be no pattern to the attributes that are set
289 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
290 if (S_ISSOCK (buf
->st_mode
))
291 buf
->st_mode
&= ~S_IFSOCK
; /* don't consider socket protection */
293 filename
= _wapi_basename (pathname
);
295 if (S_ISDIR (buf
->st_mode
)) {
296 attrs
= FILE_ATTRIBUTE_DIRECTORY
;
297 if (!is_file_writable (buf
, pathname
)) {
298 attrs
|= FILE_ATTRIBUTE_READONLY
;
300 if (filename
[0] == '.') {
301 attrs
|= FILE_ATTRIBUTE_HIDDEN
;
304 if (!is_file_writable (buf
, pathname
)) {
305 attrs
= FILE_ATTRIBUTE_READONLY
;
307 if (filename
[0] == '.') {
308 attrs
|= FILE_ATTRIBUTE_HIDDEN
;
310 } else if (filename
[0] == '.') {
311 attrs
= FILE_ATTRIBUTE_HIDDEN
;
313 attrs
= FILE_ATTRIBUTE_NORMAL
;
318 if (S_ISLNK (lbuf
->st_mode
)) {
319 attrs
|= FILE_ATTRIBUTE_REPARSE_POINT
;
329 _wapi_set_last_error_from_errno (void)
331 SetLastError (_wapi_get_win32_file_error (errno
));
334 static void _wapi_set_last_path_error_from_errno (const gchar
*dir
,
337 if (errno
== ENOENT
) {
338 /* Check the path - if it's a missing directory then
339 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
345 dirname
= _wapi_dirname (path
);
347 dirname
= g_strdup (dir
);
350 if (_wapi_access (dirname
, F_OK
) == 0) {
351 SetLastError (ERROR_FILE_NOT_FOUND
);
353 SetLastError (ERROR_PATH_NOT_FOUND
);
358 _wapi_set_last_error_from_errno ();
364 static void file_close (gpointer handle
, gpointer data
)
366 struct _WapiHandle_file
*file_handle
= (struct _WapiHandle_file
*)data
;
367 int fd
= file_handle
->fd
;
369 DEBUG("%s: closing file handle %p [%s]", __func__
, handle
,
370 file_handle
->filename
);
372 if (file_handle
->attrs
& FILE_FLAG_DELETE_ON_CLOSE
)
373 _wapi_unlink (file_handle
->filename
);
375 g_free (file_handle
->filename
);
377 if (file_handle
->share_info
)
378 _wapi_handle_share_release (file_handle
->share_info
);
383 static WapiFileType
file_getfiletype(void)
385 return(FILE_TYPE_DISK
);
388 static gboolean
file_read(gpointer handle
, gpointer buffer
,
389 guint32 numbytes
, guint32
*bytesread
,
390 WapiOverlapped
*overlapped
)
392 struct _WapiHandle_file
*file_handle
;
396 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
397 (gpointer
*)&file_handle
);
399 g_warning ("%s: error looking up file handle %p", __func__
,
401 SetLastError (ERROR_INVALID_HANDLE
);
405 fd
= file_handle
->fd
;
406 if(bytesread
!=NULL
) {
410 if(!(file_handle
->fileaccess
& GENERIC_READ
) &&
411 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
412 DEBUG("%s: handle %p doesn't have GENERIC_READ access: %u",
413 __func__
, handle
, file_handle
->fileaccess
);
415 SetLastError (ERROR_ACCESS_DENIED
);
420 ret
= read (fd
, buffer
, numbytes
);
421 } while (ret
== -1 && errno
== EINTR
&&
422 !_wapi_thread_cur_apc_pending());
427 DEBUG("%s: read of handle %p error: %s", __func__
,
428 handle
, strerror(err
));
429 SetLastError (_wapi_get_win32_file_error (err
));
433 if (bytesread
!= NULL
) {
440 static gboolean
file_write(gpointer handle
, gconstpointer buffer
,
441 guint32 numbytes
, guint32
*byteswritten
,
442 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
444 struct _WapiHandle_file
*file_handle
;
447 off_t current_pos
= 0;
449 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
450 (gpointer
*)&file_handle
);
452 g_warning ("%s: error looking up file handle %p", __func__
,
454 SetLastError (ERROR_INVALID_HANDLE
);
458 fd
= file_handle
->fd
;
460 if(byteswritten
!=NULL
) {
464 if(!(file_handle
->fileaccess
& GENERIC_WRITE
) &&
465 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
466 DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
468 SetLastError (ERROR_ACCESS_DENIED
);
472 if (lock_while_writing
) {
473 /* Need to lock the region we're about to write to,
474 * because we only do advisory locking on POSIX
477 current_pos
= lseek (fd
, (off_t
)0, SEEK_CUR
);
478 if (current_pos
== -1) {
479 DEBUG ("%s: handle %p lseek failed: %s", __func__
,
480 handle
, strerror (errno
));
481 _wapi_set_last_error_from_errno ();
485 if (_wapi_lock_file_region (fd
, current_pos
,
486 numbytes
) == FALSE
) {
487 /* The error has already been set */
493 ret
= write (fd
, buffer
, numbytes
);
494 } while (ret
== -1 && errno
== EINTR
&&
495 !_wapi_thread_cur_apc_pending());
497 if (lock_while_writing
) {
498 _wapi_unlock_file_region (fd
, current_pos
, numbytes
);
502 if (errno
== EINTR
) {
505 _wapi_set_last_error_from_errno ();
507 DEBUG("%s: write of handle %p error: %s",
508 __func__
, handle
, strerror(errno
));
513 if (byteswritten
!= NULL
) {
519 static gboolean
file_flush(gpointer handle
)
521 struct _WapiHandle_file
*file_handle
;
525 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
526 (gpointer
*)&file_handle
);
528 g_warning ("%s: error looking up file handle %p", __func__
,
530 SetLastError (ERROR_INVALID_HANDLE
);
534 fd
= file_handle
->fd
;
536 if(!(file_handle
->fileaccess
& GENERIC_WRITE
) &&
537 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
538 DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
540 SetLastError (ERROR_ACCESS_DENIED
);
546 DEBUG("%s: fsync of handle %p error: %s", __func__
, handle
,
549 _wapi_set_last_error_from_errno ();
556 static guint32
file_seek(gpointer handle
, gint32 movedistance
,
557 gint32
*highmovedistance
, WapiSeekMethod method
)
559 struct _WapiHandle_file
*file_handle
;
561 gint64 offset
, newpos
;
565 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
566 (gpointer
*)&file_handle
);
568 g_warning ("%s: error looking up file handle %p", __func__
,
570 SetLastError (ERROR_INVALID_HANDLE
);
571 return(INVALID_SET_FILE_POINTER
);
574 fd
= file_handle
->fd
;
576 if(!(file_handle
->fileaccess
& GENERIC_READ
) &&
577 !(file_handle
->fileaccess
& GENERIC_WRITE
) &&
578 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
579 DEBUG ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
581 SetLastError (ERROR_ACCESS_DENIED
);
582 return(INVALID_SET_FILE_POINTER
);
596 DEBUG("%s: invalid seek type %d", __func__
, method
);
598 SetLastError (ERROR_INVALID_PARAMETER
);
599 return(INVALID_SET_FILE_POINTER
);
602 #ifdef HAVE_LARGE_FILE_SUPPORT
603 if(highmovedistance
==NULL
) {
605 DEBUG("%s: setting offset to %lld (low %d)", __func__
,
606 offset
, movedistance
);
608 offset
=((gint64
) *highmovedistance
<< 32) | (guint32
)movedistance
;
610 DEBUG("%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__
, offset
, offset
, *highmovedistance
, *highmovedistance
, movedistance
, movedistance
);
616 DEBUG ("%s: moving handle %p by %lld bytes from %d", __func__
,
617 handle
, (long long)offset
, whence
);
619 #ifdef PLATFORM_ANDROID
620 /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */
621 newpos
=lseek64(fd
, offset
, whence
);
623 newpos
=lseek(fd
, offset
, whence
);
626 DEBUG("%s: lseek on handle %p returned error %s",
627 __func__
, handle
, strerror(errno
));
629 _wapi_set_last_error_from_errno ();
630 return(INVALID_SET_FILE_POINTER
);
633 DEBUG ("%s: lseek returns %lld", __func__
, newpos
);
635 #ifdef HAVE_LARGE_FILE_SUPPORT
636 ret
=newpos
& 0xFFFFFFFF;
637 if(highmovedistance
!=NULL
) {
638 *highmovedistance
=newpos
>>32;
642 if(highmovedistance
!=NULL
) {
643 /* Accurate, but potentially dodgy :-) */
648 DEBUG ("%s: move of handle %p returning %d/%d", __func__
,
649 handle
, ret
, highmovedistance
==NULL
?0:*highmovedistance
);
654 static gboolean
file_setendoffile(gpointer handle
)
656 struct _WapiHandle_file
*file_handle
;
662 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
663 (gpointer
*)&file_handle
);
665 g_warning ("%s: error looking up file handle %p", __func__
,
667 SetLastError (ERROR_INVALID_HANDLE
);
670 fd
= file_handle
->fd
;
672 if(!(file_handle
->fileaccess
& GENERIC_WRITE
) &&
673 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
674 DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
676 SetLastError (ERROR_ACCESS_DENIED
);
680 /* Find the current file position, and the file length. If
681 * the file position is greater than the length, write to
682 * extend the file with a hole. If the file position is less
683 * than the length, truncate the file.
686 ret
=fstat(fd
, &statbuf
);
688 DEBUG ("%s: handle %p fstat failed: %s", __func__
,
689 handle
, strerror(errno
));
691 _wapi_set_last_error_from_errno ();
694 size
=statbuf
.st_size
;
696 pos
=lseek(fd
, (off_t
)0, SEEK_CUR
);
698 DEBUG("%s: handle %p lseek failed: %s", __func__
,
699 handle
, strerror(errno
));
701 _wapi_set_last_error_from_errno ();
705 #ifdef FTRUNCATE_DOESNT_EXTEND
706 /* I haven't bothered to write the configure.ac stuff for this
707 * because I don't know if any platform needs it. I'm leaving
708 * this code just in case though
711 /* Extend the file. Use write() here, because some
712 * manuals say that ftruncate() behaviour is undefined
713 * when the file needs extending. The POSIX spec says
714 * that on XSI-conformant systems it extends, so if
715 * every system we care about conforms, then we can
719 ret
= write (fd
, "", 1);
720 } while (ret
== -1 && errno
== EINTR
&&
721 !_wapi_thread_cur_apc_pending());
724 DEBUG("%s: handle %p extend write failed: %s", __func__
, handle
, strerror(errno
));
726 _wapi_set_last_error_from_errno ();
730 /* And put the file position back after the write */
731 ret
= lseek (fd
, pos
, SEEK_SET
);
733 DEBUG ("%s: handle %p second lseek failed: %s",
734 __func__
, handle
, strerror(errno
));
736 _wapi_set_last_error_from_errno ();
742 /* Native Client has no ftruncate function, even in standalone sel_ldr. */
743 #ifndef __native_client__
744 /* always truncate, because the extend write() adds an extra
745 * byte to the end of the file
748 ret
=ftruncate(fd
, pos
);
750 while (ret
==-1 && errno
==EINTR
&& !_wapi_thread_cur_apc_pending());
752 DEBUG("%s: handle %p ftruncate failed: %s", __func__
,
753 handle
, strerror(errno
));
755 _wapi_set_last_error_from_errno ();
763 static guint32
file_getfilesize(gpointer handle
, guint32
*highsize
)
765 struct _WapiHandle_file
*file_handle
;
772 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
773 (gpointer
*)&file_handle
);
775 g_warning ("%s: error looking up file handle %p", __func__
,
777 SetLastError (ERROR_INVALID_HANDLE
);
778 return(INVALID_FILE_SIZE
);
780 fd
= file_handle
->fd
;
782 if(!(file_handle
->fileaccess
& GENERIC_READ
) &&
783 !(file_handle
->fileaccess
& GENERIC_WRITE
) &&
784 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
785 DEBUG("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
787 SetLastError (ERROR_ACCESS_DENIED
);
788 return(INVALID_FILE_SIZE
);
791 /* If the file has a size with the low bits 0xFFFFFFFF the
792 * caller can't tell if this is an error, so clear the error
795 SetLastError (ERROR_SUCCESS
);
797 ret
= fstat(fd
, &statbuf
);
799 DEBUG ("%s: handle %p fstat failed: %s", __func__
,
800 handle
, strerror(errno
));
802 _wapi_set_last_error_from_errno ();
803 return(INVALID_FILE_SIZE
);
806 /* fstat indicates block devices as zero-length, so go a different path */
808 if (S_ISBLK(statbuf
.st_mode
)) {
810 if (ioctl(fd
, BLKGETSIZE64
, &bigsize
) < 0) {
811 DEBUG ("%s: handle %p ioctl BLKGETSIZE64 failed: %s",
812 __func__
, handle
, strerror(errno
));
814 _wapi_set_last_error_from_errno ();
815 return(INVALID_FILE_SIZE
);
818 size
= bigsize
& 0xFFFFFFFF;
819 if (highsize
!= NULL
) {
820 *highsize
= bigsize
>>32;
823 DEBUG ("%s: Returning block device size %d/%d",
824 __func__
, size
, *highsize
);
830 #ifdef HAVE_LARGE_FILE_SUPPORT
831 size
= statbuf
.st_size
& 0xFFFFFFFF;
832 if (highsize
!= NULL
) {
833 *highsize
= statbuf
.st_size
>>32;
836 if (highsize
!= NULL
) {
837 /* Accurate, but potentially dodgy :-) */
840 size
= statbuf
.st_size
;
843 DEBUG ("%s: Returning size %d/%d", __func__
, size
, *highsize
);
848 static gboolean
file_getfiletime(gpointer handle
, WapiFileTime
*create_time
,
849 WapiFileTime
*last_access
,
850 WapiFileTime
*last_write
)
852 struct _WapiHandle_file
*file_handle
;
855 guint64 create_ticks
, access_ticks
, write_ticks
;
858 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
859 (gpointer
*)&file_handle
);
861 g_warning ("%s: error looking up file handle %p", __func__
,
863 SetLastError (ERROR_INVALID_HANDLE
);
866 fd
= file_handle
->fd
;
868 if(!(file_handle
->fileaccess
& GENERIC_READ
) &&
869 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
870 DEBUG("%s: handle %p doesn't have GENERIC_READ access: %u",
871 __func__
, handle
, file_handle
->fileaccess
);
873 SetLastError (ERROR_ACCESS_DENIED
);
877 ret
=fstat(fd
, &statbuf
);
879 DEBUG("%s: handle %p fstat failed: %s", __func__
, handle
,
882 _wapi_set_last_error_from_errno ();
886 DEBUG("%s: atime: %ld ctime: %ld mtime: %ld", __func__
,
887 statbuf
.st_atime
, statbuf
.st_ctime
,
890 /* Try and guess a meaningful create time by using the older
893 /* The magic constant comes from msdn documentation
894 * "Converting a time_t Value to a File Time"
896 if(statbuf
.st_atime
< statbuf
.st_ctime
) {
897 create_ticks
=((guint64
)statbuf
.st_atime
*10000000)
898 + 116444736000000000ULL;
900 create_ticks
=((guint64
)statbuf
.st_ctime
*10000000)
901 + 116444736000000000ULL;
904 access_ticks
=((guint64
)statbuf
.st_atime
*10000000)+116444736000000000ULL;
905 write_ticks
=((guint64
)statbuf
.st_mtime
*10000000)+116444736000000000ULL;
907 DEBUG("%s: aticks: %llu cticks: %llu wticks: %llu", __func__
,
908 access_ticks
, create_ticks
, write_ticks
);
910 if(create_time
!=NULL
) {
911 create_time
->dwLowDateTime
= create_ticks
& 0xFFFFFFFF;
912 create_time
->dwHighDateTime
= create_ticks
>> 32;
915 if(last_access
!=NULL
) {
916 last_access
->dwLowDateTime
= access_ticks
& 0xFFFFFFFF;
917 last_access
->dwHighDateTime
= access_ticks
>> 32;
920 if(last_write
!=NULL
) {
921 last_write
->dwLowDateTime
= write_ticks
& 0xFFFFFFFF;
922 last_write
->dwHighDateTime
= write_ticks
>> 32;
928 static gboolean
file_setfiletime(gpointer handle
,
929 const WapiFileTime
*create_time G_GNUC_UNUSED
,
930 const WapiFileTime
*last_access
,
931 const WapiFileTime
*last_write
)
933 struct _WapiHandle_file
*file_handle
;
935 struct utimbuf utbuf
;
937 guint64 access_ticks
, write_ticks
;
940 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
941 (gpointer
*)&file_handle
);
943 g_warning ("%s: error looking up file handle %p", __func__
,
945 SetLastError (ERROR_INVALID_HANDLE
);
948 fd
= file_handle
->fd
;
950 if(!(file_handle
->fileaccess
& GENERIC_WRITE
) &&
951 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
952 DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
954 SetLastError (ERROR_ACCESS_DENIED
);
958 if(file_handle
->filename
== NULL
) {
959 DEBUG("%s: handle %p unknown filename", __func__
, handle
);
961 SetLastError (ERROR_INVALID_HANDLE
);
965 /* Get the current times, so we can put the same times back in
966 * the event that one of the FileTime structs is NULL
968 ret
=fstat (fd
, &statbuf
);
970 DEBUG("%s: handle %p fstat failed: %s", __func__
, handle
,
973 SetLastError (ERROR_INVALID_PARAMETER
);
977 if(last_access
!=NULL
) {
978 access_ticks
=((guint64
)last_access
->dwHighDateTime
<< 32) +
979 last_access
->dwLowDateTime
;
980 /* This is (time_t)0. We can actually go to INT_MIN,
981 * but this will do for now.
983 if (access_ticks
< 116444736000000000ULL) {
984 DEBUG ("%s: attempt to set access time too early",
986 SetLastError (ERROR_INVALID_PARAMETER
);
990 utbuf
.actime
=(access_ticks
- 116444736000000000ULL) / 10000000;
992 utbuf
.actime
=statbuf
.st_atime
;
995 if(last_write
!=NULL
) {
996 write_ticks
=((guint64
)last_write
->dwHighDateTime
<< 32) +
997 last_write
->dwLowDateTime
;
998 /* This is (time_t)0. We can actually go to INT_MIN,
999 * but this will do for now.
1001 if (write_ticks
< 116444736000000000ULL) {
1002 DEBUG ("%s: attempt to set write time too early",
1004 SetLastError (ERROR_INVALID_PARAMETER
);
1008 utbuf
.modtime
=(write_ticks
- 116444736000000000ULL) / 10000000;
1010 utbuf
.modtime
=statbuf
.st_mtime
;
1013 DEBUG ("%s: setting handle %p access %ld write %ld", __func__
,
1014 handle
, utbuf
.actime
, utbuf
.modtime
);
1016 ret
= _wapi_utime (file_handle
->filename
, &utbuf
);
1018 DEBUG ("%s: handle %p [%s] utime failed: %s", __func__
,
1019 handle
, file_handle
->filename
, strerror(errno
));
1021 SetLastError (ERROR_INVALID_PARAMETER
);
1028 static void console_close (gpointer handle
, gpointer data
)
1030 struct _WapiHandle_file
*console_handle
= (struct _WapiHandle_file
*)data
;
1031 int fd
= console_handle
->fd
;
1033 DEBUG("%s: closing console handle %p", __func__
, handle
);
1035 g_free (console_handle
->filename
);
1041 static WapiFileType
console_getfiletype(void)
1043 return(FILE_TYPE_CHAR
);
1046 static gboolean
console_read(gpointer handle
, gpointer buffer
,
1047 guint32 numbytes
, guint32
*bytesread
,
1048 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
1050 struct _WapiHandle_file
*console_handle
;
1054 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_CONSOLE
,
1055 (gpointer
*)&console_handle
);
1057 g_warning ("%s: error looking up console handle %p", __func__
,
1059 SetLastError (ERROR_INVALID_HANDLE
);
1062 fd
= console_handle
->fd
;
1064 if(bytesread
!=NULL
) {
1068 if(!(console_handle
->fileaccess
& GENERIC_READ
) &&
1069 !(console_handle
->fileaccess
& GENERIC_ALL
)) {
1070 DEBUG ("%s: handle %p doesn't have GENERIC_READ access: %u",
1071 __func__
, handle
, console_handle
->fileaccess
);
1073 SetLastError (ERROR_ACCESS_DENIED
);
1078 ret
=read(fd
, buffer
, numbytes
);
1079 } while (ret
==-1 && errno
==EINTR
&& !_wapi_thread_cur_apc_pending());
1082 DEBUG("%s: read of handle %p error: %s", __func__
, handle
,
1085 _wapi_set_last_error_from_errno ();
1089 if(bytesread
!=NULL
) {
1096 static gboolean
console_write(gpointer handle
, gconstpointer buffer
,
1097 guint32 numbytes
, guint32
*byteswritten
,
1098 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
1100 struct _WapiHandle_file
*console_handle
;
1104 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_CONSOLE
,
1105 (gpointer
*)&console_handle
);
1107 g_warning ("%s: error looking up console handle %p", __func__
,
1109 SetLastError (ERROR_INVALID_HANDLE
);
1112 fd
= console_handle
->fd
;
1114 if(byteswritten
!=NULL
) {
1118 if(!(console_handle
->fileaccess
& GENERIC_WRITE
) &&
1119 !(console_handle
->fileaccess
& GENERIC_ALL
)) {
1120 DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, console_handle
->fileaccess
);
1122 SetLastError (ERROR_ACCESS_DENIED
);
1127 ret
= write(fd
, buffer
, numbytes
);
1128 } while (ret
== -1 && errno
== EINTR
&&
1129 !_wapi_thread_cur_apc_pending());
1132 if (errno
== EINTR
) {
1135 _wapi_set_last_error_from_errno ();
1137 DEBUG ("%s: write of handle %p error: %s",
1138 __func__
, handle
, strerror(errno
));
1143 if(byteswritten
!=NULL
) {
1150 static void pipe_close (gpointer handle
, gpointer data
)
1152 struct _WapiHandle_file
*pipe_handle
= (struct _WapiHandle_file
*)data
;
1153 int fd
= pipe_handle
->fd
;
1155 DEBUG("%s: closing pipe handle %p", __func__
, handle
);
1157 /* No filename with pipe handles */
1162 static WapiFileType
pipe_getfiletype(void)
1164 return(FILE_TYPE_PIPE
);
1167 static gboolean
pipe_read (gpointer handle
, gpointer buffer
,
1168 guint32 numbytes
, guint32
*bytesread
,
1169 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
1171 struct _WapiHandle_file
*pipe_handle
;
1175 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_PIPE
,
1176 (gpointer
*)&pipe_handle
);
1178 g_warning ("%s: error looking up pipe handle %p", __func__
,
1180 SetLastError (ERROR_INVALID_HANDLE
);
1183 fd
= pipe_handle
->fd
;
1185 if(bytesread
!=NULL
) {
1189 if(!(pipe_handle
->fileaccess
& GENERIC_READ
) &&
1190 !(pipe_handle
->fileaccess
& GENERIC_ALL
)) {
1191 DEBUG("%s: handle %p doesn't have GENERIC_READ access: %u",
1192 __func__
, handle
, pipe_handle
->fileaccess
);
1194 SetLastError (ERROR_ACCESS_DENIED
);
1198 DEBUG ("%s: reading up to %d bytes from pipe %p", __func__
,
1202 ret
=read(fd
, buffer
, numbytes
);
1203 } while (ret
==-1 && errno
==EINTR
&& !_wapi_thread_cur_apc_pending());
1206 if (errno
== EINTR
) {
1209 _wapi_set_last_error_from_errno ();
1211 DEBUG("%s: read of handle %p error: %s", __func__
,
1212 handle
, strerror(errno
));
1218 DEBUG ("%s: read %d bytes from pipe", __func__
, ret
);
1220 if(bytesread
!=NULL
) {
1227 static gboolean
pipe_write(gpointer handle
, gconstpointer buffer
,
1228 guint32 numbytes
, guint32
*byteswritten
,
1229 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
1231 struct _WapiHandle_file
*pipe_handle
;
1235 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_PIPE
,
1236 (gpointer
*)&pipe_handle
);
1238 g_warning ("%s: error looking up pipe handle %p", __func__
,
1240 SetLastError (ERROR_INVALID_HANDLE
);
1243 fd
= pipe_handle
->fd
;
1245 if(byteswritten
!=NULL
) {
1249 if(!(pipe_handle
->fileaccess
& GENERIC_WRITE
) &&
1250 !(pipe_handle
->fileaccess
& GENERIC_ALL
)) {
1251 DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, pipe_handle
->fileaccess
);
1253 SetLastError (ERROR_ACCESS_DENIED
);
1257 DEBUG ("%s: writing up to %d bytes to pipe %p", __func__
, numbytes
,
1261 ret
= write (fd
, buffer
, numbytes
);
1262 } while (ret
== -1 && errno
== EINTR
&&
1263 !_wapi_thread_cur_apc_pending());
1266 if (errno
== EINTR
) {
1269 _wapi_set_last_error_from_errno ();
1271 DEBUG("%s: write of handle %p error: %s", __func__
,
1272 handle
, strerror(errno
));
1277 if(byteswritten
!=NULL
) {
1284 static int convert_flags(guint32 fileaccess
, guint32 createmode
)
1288 switch(fileaccess
) {
1295 case GENERIC_READ
|GENERIC_WRITE
:
1299 DEBUG("%s: Unknown access type 0x%x", __func__
,
1304 switch(createmode
) {
1306 flags
|=O_CREAT
|O_EXCL
;
1309 flags
|=O_CREAT
|O_TRUNC
;
1316 case TRUNCATE_EXISTING
:
1320 DEBUG("%s: Unknown create mode 0x%x", __func__
,
1329 static mode_t
convert_perms(guint32 sharemode
)
1333 if(sharemode
&FILE_SHARE_READ
) {
1336 if(sharemode
&FILE_SHARE_WRITE
) {
1344 static gboolean
share_allows_open (struct stat
*statbuf
, guint32 sharemode
,
1346 struct _WapiFileShare
**share_info
)
1348 gboolean file_already_shared
;
1349 guint32 file_existing_share
, file_existing_access
;
1351 file_already_shared
= _wapi_handle_get_or_set_share (statbuf
->st_dev
, statbuf
->st_ino
, sharemode
, fileaccess
, &file_existing_share
, &file_existing_access
, share_info
);
1353 if (file_already_shared
) {
1354 /* The reference to this share info was incremented
1355 * when we looked it up, so be careful to put it back
1356 * if we conclude we can't use this file.
1358 if (file_existing_share
== 0) {
1359 /* Quick and easy, no possibility to share */
1360 DEBUG ("%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__
, fileaccess
);
1362 _wapi_handle_share_release (*share_info
);
1367 if (((file_existing_share
== FILE_SHARE_READ
) &&
1368 (fileaccess
!= GENERIC_READ
)) ||
1369 ((file_existing_share
== FILE_SHARE_WRITE
) &&
1370 (fileaccess
!= GENERIC_WRITE
))) {
1371 /* New access mode doesn't match up */
1372 DEBUG ("%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__
, fileaccess
, file_existing_share
);
1374 _wapi_handle_share_release (*share_info
);
1379 if (((file_existing_access
& GENERIC_READ
) &&
1380 !(sharemode
& FILE_SHARE_READ
)) ||
1381 ((file_existing_access
& GENERIC_WRITE
) &&
1382 !(sharemode
& FILE_SHARE_WRITE
))) {
1383 /* New share mode doesn't match up */
1384 DEBUG ("%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__
, sharemode
, file_existing_access
);
1386 _wapi_handle_share_release (*share_info
);
1391 DEBUG ("%s: New file!", __func__
);
1399 share_allows_delete (struct stat
*statbuf
, struct _WapiFileShare
**share_info
)
1401 gboolean file_already_shared
;
1402 guint32 file_existing_share
, file_existing_access
;
1404 file_already_shared
= _wapi_handle_get_or_set_share (statbuf
->st_dev
, statbuf
->st_ino
, FILE_SHARE_DELETE
, GENERIC_READ
, &file_existing_share
, &file_existing_access
, share_info
);
1406 if (file_already_shared
) {
1407 /* The reference to this share info was incremented
1408 * when we looked it up, so be careful to put it back
1409 * if we conclude we can't use this file.
1411 if (file_existing_share
== 0) {
1412 /* Quick and easy, no possibility to share */
1413 DEBUG ("%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__
, fileaccess
);
1415 _wapi_handle_share_release (*share_info
);
1420 if (!(file_existing_share
& FILE_SHARE_DELETE
)) {
1421 /* New access mode doesn't match up */
1422 DEBUG ("%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__
, fileaccess
, file_existing_share
);
1424 _wapi_handle_share_release (*share_info
);
1429 DEBUG ("%s: New file!", __func__
);
1434 static gboolean
share_check (struct stat
*statbuf
, guint32 sharemode
,
1436 struct _WapiFileShare
**share_info
, int fd
)
1438 if (share_allows_open (statbuf
, sharemode
, fileaccess
,
1439 share_info
) == TRUE
) {
1443 /* Got a share violation. Double check that the file is still
1444 * open by someone, in case a process crashed while still
1445 * holding a file handle. This will also cope with someone
1446 * using Mono.Posix to close the file. This is cheaper and
1447 * less intrusive to other processes than initiating a handle
1451 _wapi_handle_check_share (*share_info
, fd
);
1452 if (share_allows_open (statbuf
, sharemode
, fileaccess
,
1453 share_info
) == TRUE
) {
1457 /* Still violating. It's possible that a process crashed
1458 * while still holding a file handle, and that a non-mono
1459 * process has the file open. (For example, C-c mcs while
1460 * editing a source file.) As a last resort, run a handle
1461 * collection, which will remove stale share entries.
1463 _wapi_handle_collect ();
1465 return(share_allows_open (statbuf
, sharemode
, fileaccess
, share_info
));
1470 * @name: a pointer to a NULL-terminated unicode string, that names
1471 * the file or other object to create.
1472 * @fileaccess: specifies the file access mode
1473 * @sharemode: whether the file should be shared. This parameter is
1474 * currently ignored.
1475 * @security: Ignored for now.
1476 * @createmode: specifies whether to create a new file, whether to
1477 * overwrite an existing file, whether to truncate the file, etc.
1478 * @attrs: specifies file attributes and flags. On win32 attributes
1479 * are characteristics of the file, not the handle, and are ignored
1480 * when an existing file is opened. Flags give the library hints on
1481 * how to process a file to optimise performance.
1482 * @template: the handle of an open %GENERIC_READ file that specifies
1483 * attributes to apply to a newly created file, ignoring @attrs.
1484 * Normally this parameter is NULL. This parameter is ignored when an
1485 * existing file is opened.
1487 * Creates a new file handle. This only applies to normal files:
1488 * pipes are handled by CreatePipe(), and console handles are created
1489 * with GetStdHandle().
1491 * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1493 gpointer
CreateFile(const gunichar2
*name
, guint32 fileaccess
,
1494 guint32 sharemode
, WapiSecurityAttributes
*security
,
1495 guint32 createmode
, guint32 attrs
,
1496 gpointer
template G_GNUC_UNUSED
)
1498 struct _WapiHandle_file file_handle
= {0};
1500 int flags
=convert_flags(fileaccess
, createmode
);
1501 /*mode_t perms=convert_perms(sharemode);*/
1502 /* we don't use sharemode, because that relates to sharing of
1503 * the file when the file is open and is already handled by
1504 * other code, perms instead are the on-disk permissions and
1505 * this is a sane default.
1511 struct stat statbuf
;
1513 mono_once (&io_ops_once
, io_ops_init
);
1515 if (attrs
& FILE_ATTRIBUTE_TEMPORARY
)
1518 if (attrs
& FILE_ATTRIBUTE_ENCRYPTED
){
1519 SetLastError (ERROR_ENCRYPTION_FAILED
);
1520 return INVALID_HANDLE_VALUE
;
1524 DEBUG ("%s: name is NULL", __func__
);
1526 SetLastError (ERROR_INVALID_NAME
);
1527 return(INVALID_HANDLE_VALUE
);
1530 filename
= mono_unicode_to_external (name
);
1531 if (filename
== NULL
) {
1532 DEBUG("%s: unicode conversion returned NULL", __func__
);
1534 SetLastError (ERROR_INVALID_NAME
);
1535 return(INVALID_HANDLE_VALUE
);
1538 DEBUG ("%s: Opening %s with share 0x%x and access 0x%x", __func__
,
1539 filename
, sharemode
, fileaccess
);
1541 fd
= _wapi_open (filename
, flags
, perms
);
1543 /* If we were trying to open a directory with write permissions
1544 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1545 * EISDIR. However, this is a bit bogus because calls to
1546 * manipulate the directory (e.g. SetFileTime) will still work on
1547 * the directory because they use other API calls
1548 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1549 * to open the directory again without write permission.
1551 if (fd
== -1 && errno
== EISDIR
)
1553 /* Try again but don't try to make it writable */
1554 fd
= _wapi_open (filename
, flags
& ~(O_RDWR
|O_WRONLY
), perms
);
1558 DEBUG("%s: Error opening file %s: %s", __func__
, filename
,
1560 _wapi_set_last_path_error_from_errno (NULL
, filename
);
1563 return(INVALID_HANDLE_VALUE
);
1566 if (fd
>= _wapi_fd_reserve
) {
1567 DEBUG ("%s: File descriptor is too big", __func__
);
1569 SetLastError (ERROR_TOO_MANY_OPEN_FILES
);
1574 return(INVALID_HANDLE_VALUE
);
1577 ret
= fstat (fd
, &statbuf
);
1579 DEBUG ("%s: fstat error of file %s: %s", __func__
,
1580 filename
, strerror (errno
));
1581 _wapi_set_last_error_from_errno ();
1585 return(INVALID_HANDLE_VALUE
);
1587 #ifdef __native_client__
1588 /* Workaround: Native Client currently returns the same fake inode
1589 * for all files, so do a simple hash on the filename so we don't
1590 * use the same share info for each file.
1592 statbuf
.st_ino
= g_str_hash(filename
);
1595 if (share_check (&statbuf
, sharemode
, fileaccess
,
1596 &file_handle
.share_info
, fd
) == FALSE
) {
1597 SetLastError (ERROR_SHARING_VIOLATION
);
1601 return (INVALID_HANDLE_VALUE
);
1603 if (file_handle
.share_info
== NULL
) {
1604 /* No space, so no more files can be opened */
1605 DEBUG ("%s: No space in the share table", __func__
);
1607 SetLastError (ERROR_TOO_MANY_OPEN_FILES
);
1611 return(INVALID_HANDLE_VALUE
);
1614 file_handle
.filename
= filename
;
1616 if(security
!=NULL
) {
1617 //file_handle->security_attributes=_wapi_handle_scratch_store (
1618 //security, sizeof(WapiSecurityAttributes));
1621 file_handle
.fd
= fd
;
1622 file_handle
.fileaccess
=fileaccess
;
1623 file_handle
.sharemode
=sharemode
;
1624 file_handle
.attrs
=attrs
;
1626 #ifdef HAVE_POSIX_FADVISE
1627 if (attrs
& FILE_FLAG_SEQUENTIAL_SCAN
)
1628 posix_fadvise (fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
1629 if (attrs
& FILE_FLAG_RANDOM_ACCESS
)
1630 posix_fadvise (fd
, 0, 0, POSIX_FADV_RANDOM
);
1634 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
1636 if (S_ISFIFO (statbuf
.st_mode
)) {
1637 handle_type
= WAPI_HANDLE_PIPE
;
1638 } else if (S_ISCHR (statbuf
.st_mode
)) {
1639 handle_type
= WAPI_HANDLE_CONSOLE
;
1641 handle_type
= WAPI_HANDLE_FILE
;
1644 handle
= _wapi_handle_new_fd (handle_type
, fd
, &file_handle
);
1645 if (handle
== _WAPI_HANDLE_INVALID
) {
1646 g_warning ("%s: error creating file handle", __func__
);
1650 SetLastError (ERROR_GEN_FAILURE
);
1651 return(INVALID_HANDLE_VALUE
);
1654 DEBUG("%s: returning handle %p", __func__
, handle
);
1661 * @name: a pointer to a NULL-terminated unicode string, that names
1662 * the file to be deleted.
1664 * Deletes file @name.
1666 * Return value: %TRUE on success, %FALSE otherwise.
1668 gboolean
DeleteFile(const gunichar2
*name
)
1672 gboolean ret
= FALSE
;
1675 struct stat statbuf
;
1676 struct _WapiFileShare
*shareinfo
;
1680 DEBUG("%s: name is NULL", __func__
);
1682 SetLastError (ERROR_INVALID_NAME
);
1686 filename
=mono_unicode_to_external(name
);
1687 if(filename
==NULL
) {
1688 DEBUG("%s: unicode conversion returned NULL", __func__
);
1690 SetLastError (ERROR_INVALID_NAME
);
1694 attrs
= GetFileAttributes (name
);
1695 if (attrs
== INVALID_FILE_ATTRIBUTES
) {
1696 DEBUG ("%s: file attributes error", __func__
);
1697 /* Error set by GetFileAttributes() */
1703 /* Check to make sure sharing allows us to open the file for
1704 * writing. See bug 323389.
1706 * Do the checks that don't need an open file descriptor, for
1707 * simplicity's sake. If we really have to do the full checks
1708 * then we can implement that later.
1710 if (_wapi_stat (filename
, &statbuf
) < 0) {
1711 _wapi_set_last_path_error_from_errno (NULL
, filename
);
1716 if (share_allows_open (&statbuf
, 0, GENERIC_WRITE
,
1717 &shareinfo
) == FALSE
) {
1718 SetLastError (ERROR_SHARING_VIOLATION
);
1723 _wapi_handle_share_release (shareinfo
);
1726 retval
= _wapi_unlink (filename
);
1729 _wapi_set_last_path_error_from_errno (NULL
, filename
);
1741 * @name: a pointer to a NULL-terminated unicode string, that names
1742 * the file to be moved.
1743 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1744 * new name for the file.
1746 * Renames file @name to @dest_name.
1747 * MoveFile sets ERROR_ALREADY_EXISTS if the destination exists, except
1748 * when it is the same file as the source. In that case it silently succeeds.
1750 * Return value: %TRUE on success, %FALSE otherwise.
1752 gboolean
MoveFile (const gunichar2
*name
, const gunichar2
*dest_name
)
1754 gchar
*utf8_name
, *utf8_dest_name
;
1755 int result
, errno_copy
;
1756 struct stat stat_src
, stat_dest
;
1757 gboolean ret
= FALSE
;
1758 struct _WapiFileShare
*shareinfo
;
1761 DEBUG("%s: name is NULL", __func__
);
1763 SetLastError (ERROR_INVALID_NAME
);
1767 utf8_name
= mono_unicode_to_external (name
);
1768 if (utf8_name
== NULL
) {
1769 DEBUG ("%s: unicode conversion returned NULL", __func__
);
1771 SetLastError (ERROR_INVALID_NAME
);
1775 if(dest_name
==NULL
) {
1776 DEBUG("%s: name is NULL", __func__
);
1779 SetLastError (ERROR_INVALID_NAME
);
1783 utf8_dest_name
= mono_unicode_to_external (dest_name
);
1784 if (utf8_dest_name
== NULL
) {
1785 DEBUG ("%s: unicode conversion returned NULL", __func__
);
1788 SetLastError (ERROR_INVALID_NAME
);
1793 * In C# land we check for the existence of src, but not for dest.
1794 * We check it here and return the failure if dest exists and is not
1795 * the same file as src.
1797 if (_wapi_stat (utf8_name
, &stat_src
) < 0) {
1798 if (errno
!= ENOENT
|| _wapi_lstat (utf8_name
, &stat_src
) < 0) {
1799 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
1801 g_free (utf8_dest_name
);
1806 if (!_wapi_stat (utf8_dest_name
, &stat_dest
)) {
1807 if (stat_dest
.st_dev
!= stat_src
.st_dev
||
1808 stat_dest
.st_ino
!= stat_src
.st_ino
) {
1810 g_free (utf8_dest_name
);
1811 SetLastError (ERROR_ALREADY_EXISTS
);
1816 /* Check to make that we have delete sharing permission.
1817 * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
1819 * Do the checks that don't need an open file descriptor, for
1820 * simplicity's sake. If we really have to do the full checks
1821 * then we can implement that later.
1823 if (share_allows_delete (&stat_src
, &shareinfo
) == FALSE
) {
1824 SetLastError (ERROR_SHARING_VIOLATION
);
1828 _wapi_handle_share_release (shareinfo
);
1830 result
= _wapi_rename (utf8_name
, utf8_dest_name
);
1834 switch(errno_copy
) {
1836 SetLastError (ERROR_ALREADY_EXISTS
);
1840 /* Ignore here, it is dealt with below */
1844 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
1849 g_free (utf8_dest_name
);
1851 if (result
!= 0 && errno_copy
== EXDEV
) {
1852 if (S_ISDIR (stat_src
.st_mode
)) {
1853 SetLastError (ERROR_NOT_SAME_DEVICE
);
1856 /* Try a copy to the new location, and delete the source */
1857 if (CopyFile (name
, dest_name
, TRUE
)==FALSE
) {
1858 /* CopyFile will set the error */
1862 return(DeleteFile (name
));
1873 write_file (int src_fd
, int dest_fd
, struct stat
*st_src
, gboolean report_errors
)
1877 int buf_size
= st_src
->st_blksize
;
1879 buf_size
= buf_size
< 8192 ? 8192 : (buf_size
> 65536 ? 65536 : buf_size
);
1880 buf
= (char *) malloc (buf_size
);
1883 remain
= read (src_fd
, buf
, buf_size
);
1885 if (errno
== EINTR
&& !_wapi_thread_cur_apc_pending ())
1889 _wapi_set_last_error_from_errno ();
1899 while (remain
> 0) {
1900 if ((n
= write (dest_fd
, wbuf
, remain
)) < 0) {
1901 if (errno
== EINTR
&& !_wapi_thread_cur_apc_pending ())
1905 _wapi_set_last_error_from_errno ();
1906 DEBUG ("%s: write failed.", __func__
);
1922 * @name: a pointer to a NULL-terminated unicode string, that names
1923 * the file to be copied.
1924 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1925 * new name for the file.
1926 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1928 * Copies file @name to @dest_name
1930 * Return value: %TRUE on success, %FALSE otherwise.
1932 gboolean
CopyFile (const gunichar2
*name
, const gunichar2
*dest_name
,
1933 gboolean fail_if_exists
)
1935 gchar
*utf8_src
, *utf8_dest
;
1936 int src_fd
, dest_fd
;
1937 struct stat st
, dest_st
;
1938 struct utimbuf dest_time
;
1939 gboolean ret
= TRUE
;
1943 DEBUG("%s: name is NULL", __func__
);
1945 SetLastError (ERROR_INVALID_NAME
);
1949 utf8_src
= mono_unicode_to_external (name
);
1950 if (utf8_src
== NULL
) {
1951 DEBUG ("%s: unicode conversion of source returned NULL",
1954 SetLastError (ERROR_INVALID_PARAMETER
);
1958 if(dest_name
==NULL
) {
1959 DEBUG("%s: dest is NULL", __func__
);
1962 SetLastError (ERROR_INVALID_NAME
);
1966 utf8_dest
= mono_unicode_to_external (dest_name
);
1967 if (utf8_dest
== NULL
) {
1968 DEBUG ("%s: unicode conversion of dest returned NULL",
1971 SetLastError (ERROR_INVALID_PARAMETER
);
1978 src_fd
= _wapi_open (utf8_src
, O_RDONLY
, 0);
1980 _wapi_set_last_path_error_from_errno (NULL
, utf8_src
);
1988 if (fstat (src_fd
, &st
) < 0) {
1989 _wapi_set_last_error_from_errno ();
1998 /* Before trying to open/create the dest, we need to report a 'file busy'
1999 * error if src and dest are actually the same file. We do the check here to take
2000 * advantage of the IOMAP capability */
2001 if (!_wapi_stat (utf8_dest
, &dest_st
) && st
.st_dev
== dest_st
.st_dev
&&
2002 st
.st_ino
== dest_st
.st_ino
) {
2008 SetLastError (ERROR_SHARING_VIOLATION
);
2012 if (fail_if_exists
) {
2013 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_CREAT
| O_EXCL
, st
.st_mode
);
2015 /* FIXME: it kinda sucks that this code path potentially scans
2016 * the directory twice due to the weird SetLastError()
2018 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_TRUNC
, st
.st_mode
);
2020 /* The file does not exist, try creating it */
2021 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_CREAT
| O_TRUNC
, st
.st_mode
);
2023 /* Apparently this error is set if we
2024 * overwrite the dest file
2026 SetLastError (ERROR_ALREADY_EXISTS
);
2030 _wapi_set_last_error_from_errno ();
2039 if (!write_file (src_fd
, dest_fd
, &st
, TRUE
))
2045 dest_time
.modtime
= st
.st_mtime
;
2046 dest_time
.actime
= st
.st_atime
;
2047 ret_utime
= utime (utf8_dest
, &dest_time
);
2048 if (ret_utime
== -1)
2049 DEBUG ("%s: file [%s] utime failed: %s", __func__
, utf8_dest
, strerror(errno
));
2058 convert_arg_to_utf8 (const gunichar2
*arg
, const gchar
*arg_name
)
2063 DEBUG ("%s: %s is NULL", __func__
, arg_name
);
2064 SetLastError (ERROR_INVALID_NAME
);
2068 utf8_ret
= mono_unicode_to_external (arg
);
2069 if (utf8_ret
== NULL
) {
2070 DEBUG ("%s: unicode conversion of %s returned NULL",
2071 __func__
, arg_name
);
2072 SetLastError (ERROR_INVALID_PARAMETER
);
2080 ReplaceFile (const gunichar2
*replacedFileName
, const gunichar2
*replacementFileName
,
2081 const gunichar2
*backupFileName
, guint32 replaceFlags
,
2082 gpointer exclude
, gpointer reserved
)
2084 int result
, backup_fd
= -1,replaced_fd
= -1;
2085 gchar
*utf8_replacedFileName
, *utf8_replacementFileName
= NULL
, *utf8_backupFileName
= NULL
;
2086 struct stat stBackup
;
2087 gboolean ret
= FALSE
;
2089 if (!(utf8_replacedFileName
= convert_arg_to_utf8 (replacedFileName
, "replacedFileName")))
2091 if (!(utf8_replacementFileName
= convert_arg_to_utf8 (replacementFileName
, "replacementFileName")))
2092 goto replace_cleanup
;
2093 if (backupFileName
!= NULL
) {
2094 if (!(utf8_backupFileName
= convert_arg_to_utf8 (backupFileName
, "backupFileName")))
2095 goto replace_cleanup
;
2098 if (utf8_backupFileName
) {
2099 // Open the backup file for read so we can restore the file if an error occurs.
2100 backup_fd
= _wapi_open (utf8_backupFileName
, O_RDONLY
, 0);
2101 result
= _wapi_rename (utf8_replacedFileName
, utf8_backupFileName
);
2103 goto replace_cleanup
;
2106 result
= _wapi_rename (utf8_replacementFileName
, utf8_replacedFileName
);
2108 _wapi_set_last_path_error_from_errno (NULL
, utf8_replacementFileName
);
2109 _wapi_rename (utf8_backupFileName
, utf8_replacedFileName
);
2110 if (backup_fd
!= -1 && !fstat (backup_fd
, &stBackup
)) {
2111 replaced_fd
= _wapi_open (utf8_backupFileName
, O_WRONLY
| O_CREAT
| O_TRUNC
,
2114 if (replaced_fd
== -1)
2115 goto replace_cleanup
;
2117 write_file (backup_fd
, replaced_fd
, &stBackup
, FALSE
);
2120 goto replace_cleanup
;
2126 g_free (utf8_replacedFileName
);
2127 g_free (utf8_replacementFileName
);
2128 g_free (utf8_backupFileName
);
2129 if (backup_fd
!= -1)
2131 if (replaced_fd
!= -1)
2132 close (replaced_fd
);
2138 * @stdhandle: specifies the file descriptor
2140 * Returns a handle for stdin, stdout, or stderr. Always returns the
2141 * same handle for the same @stdhandle.
2143 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2146 static mono_mutex_t stdhandle_mutex
;
2148 gpointer
GetStdHandle(WapiStdHandle stdhandle
)
2150 struct _WapiHandle_file
*file_handle
;
2157 case STD_INPUT_HANDLE
:
2162 case STD_OUTPUT_HANDLE
:
2167 case STD_ERROR_HANDLE
:
2173 DEBUG("%s: unknown standard handle type", __func__
);
2175 SetLastError (ERROR_INVALID_PARAMETER
);
2176 return(INVALID_HANDLE_VALUE
);
2179 handle
= GINT_TO_POINTER (fd
);
2181 thr_ret
= mono_mutex_lock (&stdhandle_mutex
);
2182 g_assert (thr_ret
== 0);
2184 ok
= _wapi_lookup_handle (handle
, WAPI_HANDLE_CONSOLE
,
2185 (gpointer
*)&file_handle
);
2187 /* Need to create this console handle */
2188 handle
= _wapi_stdhandle_create (fd
, name
);
2190 if (handle
== INVALID_HANDLE_VALUE
) {
2191 SetLastError (ERROR_NO_MORE_FILES
);
2195 /* Add a reference to this handle */
2196 _wapi_handle_ref (handle
);
2200 thr_ret
= mono_mutex_unlock (&stdhandle_mutex
);
2201 g_assert (thr_ret
== 0);
2208 * @handle: The file handle to read from. The handle must have
2209 * %GENERIC_READ access.
2210 * @buffer: The buffer to store read data in
2211 * @numbytes: The maximum number of bytes to read
2212 * @bytesread: The actual number of bytes read is stored here. This
2213 * value can be zero if the handle is positioned at the end of the
2215 * @overlapped: points to a required %WapiOverlapped structure if
2216 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2219 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2220 * function reads up to @numbytes bytes from the file from the current
2221 * file position, and stores them in @buffer. If there are not enough
2222 * bytes left in the file, just the amount available will be read.
2223 * The actual number of bytes read is stored in @bytesread.
2225 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2226 * file position is ignored and the read position is taken from data
2227 * in the @overlapped structure.
2229 * Return value: %TRUE if the read succeeds (even if no bytes were
2230 * read due to an attempt to read past the end of the file), %FALSE on
2233 gboolean
ReadFile(gpointer handle
, gpointer buffer
, guint32 numbytes
,
2234 guint32
*bytesread
, WapiOverlapped
*overlapped
)
2236 WapiHandleType type
;
2238 type
= _wapi_handle_type (handle
);
2240 if(io_ops
[type
].readfile
==NULL
) {
2241 SetLastError (ERROR_INVALID_HANDLE
);
2245 return(io_ops
[type
].readfile (handle
, buffer
, numbytes
, bytesread
,
2251 * @handle: The file handle to write to. The handle must have
2252 * %GENERIC_WRITE access.
2253 * @buffer: The buffer to read data from.
2254 * @numbytes: The maximum number of bytes to write.
2255 * @byteswritten: The actual number of bytes written is stored here.
2256 * If the handle is positioned at the file end, the length of the file
2257 * is extended. This parameter may be %NULL.
2258 * @overlapped: points to a required %WapiOverlapped structure if
2259 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2262 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2263 * function writes up to @numbytes bytes from @buffer to the file at
2264 * the current file position. If @handle is positioned at the end of
2265 * the file, the file is extended. The actual number of bytes written
2266 * is stored in @byteswritten.
2268 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2269 * file position is ignored and the write position is taken from data
2270 * in the @overlapped structure.
2272 * Return value: %TRUE if the write succeeds, %FALSE on error.
2274 gboolean
WriteFile(gpointer handle
, gconstpointer buffer
, guint32 numbytes
,
2275 guint32
*byteswritten
, WapiOverlapped
*overlapped
)
2277 WapiHandleType type
;
2279 type
= _wapi_handle_type (handle
);
2281 if(io_ops
[type
].writefile
==NULL
) {
2282 SetLastError (ERROR_INVALID_HANDLE
);
2286 return(io_ops
[type
].writefile (handle
, buffer
, numbytes
, byteswritten
,
2292 * @handle: Handle to open file. The handle must have
2293 * %GENERIC_WRITE access.
2295 * Flushes buffers of the file and causes all unwritten data to
2298 * Return value: %TRUE on success, %FALSE otherwise.
2300 gboolean
FlushFileBuffers(gpointer handle
)
2302 WapiHandleType type
;
2304 type
= _wapi_handle_type (handle
);
2306 if(io_ops
[type
].flushfile
==NULL
) {
2307 SetLastError (ERROR_INVALID_HANDLE
);
2311 return(io_ops
[type
].flushfile (handle
));
2316 * @handle: The file handle to set. The handle must have
2317 * %GENERIC_WRITE access.
2319 * Moves the end-of-file position to the current position of the file
2320 * pointer. This function is used to truncate or extend a file.
2322 * Return value: %TRUE on success, %FALSE otherwise.
2324 gboolean
SetEndOfFile(gpointer handle
)
2326 WapiHandleType type
;
2328 type
= _wapi_handle_type (handle
);
2330 if (io_ops
[type
].setendoffile
== NULL
) {
2331 SetLastError (ERROR_INVALID_HANDLE
);
2335 return(io_ops
[type
].setendoffile (handle
));
2340 * @handle: The file handle to set. The handle must have
2341 * %GENERIC_READ or %GENERIC_WRITE access.
2342 * @movedistance: Low 32 bits of a signed value that specifies the
2343 * number of bytes to move the file pointer.
2344 * @highmovedistance: Pointer to the high 32 bits of a signed value
2345 * that specifies the number of bytes to move the file pointer, or
2347 * @method: The starting point for the file pointer move.
2349 * Sets the file pointer of an open file.
2351 * The distance to move the file pointer is calculated from
2352 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2353 * @movedistance is the 32-bit signed value; otherwise, @movedistance
2354 * is the low 32 bits and @highmovedistance a pointer to the high 32
2355 * bits of a 64 bit signed value. A positive distance moves the file
2356 * pointer forward from the position specified by @method; a negative
2357 * distance moves the file pointer backward.
2359 * If the library is compiled without large file support,
2360 * @highmovedistance is ignored and its value is set to zero on a
2361 * successful return.
2363 * Return value: On success, the low 32 bits of the new file pointer.
2364 * If @highmovedistance is not %NULL, the high 32 bits of the new file
2365 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
2367 guint32
SetFilePointer(gpointer handle
, gint32 movedistance
,
2368 gint32
*highmovedistance
, WapiSeekMethod method
)
2370 WapiHandleType type
;
2372 type
= _wapi_handle_type (handle
);
2374 if (io_ops
[type
].seek
== NULL
) {
2375 SetLastError (ERROR_INVALID_HANDLE
);
2376 return(INVALID_SET_FILE_POINTER
);
2379 return(io_ops
[type
].seek (handle
, movedistance
, highmovedistance
,
2385 * @handle: The file handle to test.
2387 * Finds the type of file @handle.
2389 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2390 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
2391 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2392 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2394 WapiFileType
GetFileType(gpointer handle
)
2396 WapiHandleType type
;
2398 if (!_WAPI_PRIVATE_HAVE_SLOT (handle
)) {
2399 SetLastError (ERROR_INVALID_HANDLE
);
2400 return(FILE_TYPE_UNKNOWN
);
2403 type
= _wapi_handle_type (handle
);
2405 if (io_ops
[type
].getfiletype
== NULL
) {
2406 SetLastError (ERROR_INVALID_HANDLE
);
2407 return(FILE_TYPE_UNKNOWN
);
2410 return(io_ops
[type
].getfiletype ());
2415 * @handle: The file handle to query. The handle must have
2416 * %GENERIC_READ or %GENERIC_WRITE access.
2417 * @highsize: If non-%NULL, the high 32 bits of the file size are
2420 * Retrieves the size of the file @handle.
2422 * If the library is compiled without large file support, @highsize
2423 * has its value set to zero on a successful return.
2425 * Return value: On success, the low 32 bits of the file size. If
2426 * @highsize is non-%NULL then the high 32 bits of the file size are
2427 * stored here. On failure %INVALID_FILE_SIZE is returned.
2429 guint32
GetFileSize(gpointer handle
, guint32
*highsize
)
2431 WapiHandleType type
;
2433 type
= _wapi_handle_type (handle
);
2435 if (io_ops
[type
].getfilesize
== NULL
) {
2436 SetLastError (ERROR_INVALID_HANDLE
);
2437 return(INVALID_FILE_SIZE
);
2440 return(io_ops
[type
].getfilesize (handle
, highsize
));
2445 * @handle: The file handle to query. The handle must have
2446 * %GENERIC_READ access.
2447 * @create_time: Points to a %WapiFileTime structure to receive the
2448 * number of ticks since the epoch that file was created. May be
2450 * @last_access: Points to a %WapiFileTime structure to receive the
2451 * number of ticks since the epoch when file was last accessed. May be
2453 * @last_write: Points to a %WapiFileTime structure to receive the
2454 * number of ticks since the epoch when file was last written to. May
2457 * Finds the number of ticks since the epoch that the file referenced
2458 * by @handle was created, last accessed and last modified. A tick is
2459 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2462 * Create time isn't recorded on POSIX file systems or reported by
2463 * stat(2), so that time is guessed by returning the oldest of the
2466 * Return value: %TRUE on success, %FALSE otherwise.
2468 gboolean
GetFileTime(gpointer handle
, WapiFileTime
*create_time
,
2469 WapiFileTime
*last_access
, WapiFileTime
*last_write
)
2471 WapiHandleType type
;
2473 type
= _wapi_handle_type (handle
);
2475 if (io_ops
[type
].getfiletime
== NULL
) {
2476 SetLastError (ERROR_INVALID_HANDLE
);
2480 return(io_ops
[type
].getfiletime (handle
, create_time
, last_access
,
2486 * @handle: The file handle to set. The handle must have
2487 * %GENERIC_WRITE access.
2488 * @create_time: Points to a %WapiFileTime structure that contains the
2489 * number of ticks since the epoch that the file was created. May be
2491 * @last_access: Points to a %WapiFileTime structure that contains the
2492 * number of ticks since the epoch when the file was last accessed.
2494 * @last_write: Points to a %WapiFileTime structure that contains the
2495 * number of ticks since the epoch when the file was last written to.
2498 * Sets the number of ticks since the epoch that the file referenced
2499 * by @handle was created, last accessed or last modified. A tick is
2500 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2503 * Create time isn't recorded on POSIX file systems, and is ignored.
2505 * Return value: %TRUE on success, %FALSE otherwise.
2507 gboolean
SetFileTime(gpointer handle
, const WapiFileTime
*create_time
,
2508 const WapiFileTime
*last_access
,
2509 const WapiFileTime
*last_write
)
2511 WapiHandleType type
;
2513 type
= _wapi_handle_type (handle
);
2515 if (io_ops
[type
].setfiletime
== NULL
) {
2516 SetLastError (ERROR_INVALID_HANDLE
);
2520 return(io_ops
[type
].setfiletime (handle
, create_time
, last_access
,
2524 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2525 * January 1 1601 GMT
2528 #define TICKS_PER_MILLISECOND 10000L
2529 #define TICKS_PER_SECOND 10000000L
2530 #define TICKS_PER_MINUTE 600000000L
2531 #define TICKS_PER_HOUR 36000000000LL
2532 #define TICKS_PER_DAY 864000000000LL
2534 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2536 static const guint16 mon_yday
[2][13]={
2537 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2538 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2542 * FileTimeToSystemTime:
2543 * @file_time: Points to a %WapiFileTime structure that contains the
2544 * number of ticks to convert.
2545 * @system_time: Points to a %WapiSystemTime structure to receive the
2548 * Converts a tick count into broken-out time values.
2550 * Return value: %TRUE on success, %FALSE otherwise.
2552 gboolean
FileTimeToSystemTime(const WapiFileTime
*file_time
,
2553 WapiSystemTime
*system_time
)
2555 gint64 file_ticks
, totaldays
, rem
, y
;
2558 if(system_time
==NULL
) {
2559 DEBUG("%s: system_time NULL", __func__
);
2561 SetLastError (ERROR_INVALID_PARAMETER
);
2565 file_ticks
=((gint64
)file_time
->dwHighDateTime
<< 32) +
2566 file_time
->dwLowDateTime
;
2568 /* Really compares if file_ticks>=0x8000000000000000
2569 * (LLONG_MAX+1) but we're working with a signed value for the
2570 * year and day calculation to work later
2573 DEBUG("%s: file_time too big", __func__
);
2575 SetLastError (ERROR_INVALID_PARAMETER
);
2579 totaldays
=(file_ticks
/ TICKS_PER_DAY
);
2580 rem
= file_ticks
% TICKS_PER_DAY
;
2581 DEBUG("%s: totaldays: %lld rem: %lld", __func__
, totaldays
, rem
);
2583 system_time
->wHour
=rem
/TICKS_PER_HOUR
;
2584 rem
%= TICKS_PER_HOUR
;
2585 DEBUG("%s: Hour: %d rem: %lld", __func__
, system_time
->wHour
, rem
);
2587 system_time
->wMinute
= rem
/ TICKS_PER_MINUTE
;
2588 rem
%= TICKS_PER_MINUTE
;
2589 DEBUG("%s: Minute: %d rem: %lld", __func__
, system_time
->wMinute
,
2592 system_time
->wSecond
= rem
/ TICKS_PER_SECOND
;
2593 rem
%= TICKS_PER_SECOND
;
2594 DEBUG("%s: Second: %d rem: %lld", __func__
, system_time
->wSecond
,
2597 system_time
->wMilliseconds
= rem
/ TICKS_PER_MILLISECOND
;
2598 DEBUG("%s: Milliseconds: %d", __func__
,
2599 system_time
->wMilliseconds
);
2601 /* January 1, 1601 was a Monday, according to Emacs calendar */
2602 system_time
->wDayOfWeek
= ((1 + totaldays
) % 7) + 1;
2603 DEBUG("%s: Day of week: %d", __func__
, system_time
->wDayOfWeek
);
2605 /* This algorithm to find year and month given days from epoch
2610 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2611 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2613 while(totaldays
< 0 || totaldays
>= (isleap(y
)?366:365)) {
2614 /* Guess a corrected year, assuming 365 days per year */
2615 gint64 yg
= y
+ totaldays
/ 365 - (totaldays
% 365 < 0);
2616 DEBUG("%s: totaldays: %lld yg: %lld y: %lld", __func__
,
2619 g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__
,
2620 LEAPS_THRU_END_OF(yg
-1), LEAPS_THRU_END_OF(y
-1));
2622 /* Adjust days and y to match the guessed year. */
2623 totaldays
-= ((yg
- y
) * 365
2624 + LEAPS_THRU_END_OF (yg
- 1)
2625 - LEAPS_THRU_END_OF (y
- 1));
2626 DEBUG("%s: totaldays: %lld", __func__
, totaldays
);
2628 DEBUG("%s: y: %lld", __func__
, y
);
2631 system_time
->wYear
= y
;
2632 DEBUG("%s: Year: %d", __func__
, system_time
->wYear
);
2634 ip
= mon_yday
[isleap(y
)];
2636 for(y
=11; totaldays
< ip
[y
]; --y
) {
2640 DEBUG("%s: totaldays: %lld", __func__
, totaldays
);
2642 system_time
->wMonth
= y
+ 1;
2643 DEBUG("%s: Month: %d", __func__
, system_time
->wMonth
);
2645 system_time
->wDay
= totaldays
+ 1;
2646 DEBUG("%s: Day: %d", __func__
, system_time
->wDay
);
2651 gpointer
FindFirstFile (const gunichar2
*pattern
, WapiFindData
*find_data
)
2653 struct _WapiHandle_find find_handle
= {0};
2655 gchar
*utf8_pattern
= NULL
, *dir_part
, *entry_part
;
2658 if (pattern
== NULL
) {
2659 DEBUG ("%s: pattern is NULL", __func__
);
2661 SetLastError (ERROR_PATH_NOT_FOUND
);
2662 return(INVALID_HANDLE_VALUE
);
2665 utf8_pattern
= mono_unicode_to_external (pattern
);
2666 if (utf8_pattern
== NULL
) {
2667 DEBUG ("%s: unicode conversion returned NULL", __func__
);
2669 SetLastError (ERROR_INVALID_NAME
);
2670 return(INVALID_HANDLE_VALUE
);
2673 DEBUG ("%s: looking for [%s]", __func__
, utf8_pattern
);
2675 /* Figure out which bit of the pattern is the directory */
2676 dir_part
= _wapi_dirname (utf8_pattern
);
2677 entry_part
= _wapi_basename (utf8_pattern
);
2680 /* Don't do this check for now, it breaks if directories
2681 * really do have metachars in their names (see bug 58116).
2682 * FIXME: Figure out a better solution to keep some checks...
2684 if (strchr (dir_part
, '*') || strchr (dir_part
, '?')) {
2685 SetLastError (ERROR_INVALID_NAME
);
2687 g_free (entry_part
);
2688 g_free (utf8_pattern
);
2689 return(INVALID_HANDLE_VALUE
);
2693 /* The pattern can specify a directory or a set of files.
2695 * The pattern can have wildcard characters ? and *, but only
2696 * in the section after the last directory delimiter. (Return
2697 * ERROR_INVALID_NAME if there are wildcards in earlier path
2698 * sections.) "*" has the usual 0-or-more chars meaning. "?"
2699 * means "match one character", "??" seems to mean "match one
2700 * or two characters", "???" seems to mean "match one, two or
2701 * three characters", etc. Windows will also try and match
2702 * the mangled "short name" of files, so 8 character patterns
2703 * with wildcards will show some surprising results.
2705 * All the written documentation I can find says that '?'
2706 * should only match one character, and doesn't mention '??',
2707 * '???' etc. I'm going to assume that the strict behaviour
2708 * (ie '???' means three and only three characters) is the
2709 * correct one, because that lets me use fnmatch(3) rather
2710 * than mess around with regexes.
2713 find_handle
.namelist
= NULL
;
2714 result
= _wapi_io_scandir (dir_part
, entry_part
,
2715 &find_handle
.namelist
);
2718 /* No files, which windows seems to call
2721 SetLastError (ERROR_FILE_NOT_FOUND
);
2722 g_free (utf8_pattern
);
2723 g_free (entry_part
);
2725 return (INVALID_HANDLE_VALUE
);
2729 #ifdef DEBUG_ENABLED
2730 gint errnum
= errno
;
2732 _wapi_set_last_path_error_from_errno (dir_part
, NULL
);
2733 DEBUG ("%s: scandir error: %s", __func__
,
2734 g_strerror (errnum
));
2735 g_free (utf8_pattern
);
2736 g_free (entry_part
);
2738 return (INVALID_HANDLE_VALUE
);
2741 g_free (utf8_pattern
);
2742 g_free (entry_part
);
2744 DEBUG ("%s: Got %d matches", __func__
, result
);
2746 find_handle
.dir_part
= dir_part
;
2747 find_handle
.num
= result
;
2748 find_handle
.count
= 0;
2750 handle
= _wapi_handle_new (WAPI_HANDLE_FIND
, &find_handle
);
2751 if (handle
== _WAPI_HANDLE_INVALID
) {
2752 g_warning ("%s: error creating find handle", __func__
);
2754 g_free (entry_part
);
2755 g_free (utf8_pattern
);
2756 SetLastError (ERROR_GEN_FAILURE
);
2758 return(INVALID_HANDLE_VALUE
);
2761 if (handle
!= INVALID_HANDLE_VALUE
&&
2762 !FindNextFile (handle
, find_data
)) {
2764 SetLastError (ERROR_NO_MORE_FILES
);
2765 handle
= INVALID_HANDLE_VALUE
;
2771 gboolean
FindNextFile (gpointer handle
, WapiFindData
*find_data
)
2773 struct _WapiHandle_find
*find_handle
;
2775 struct stat buf
, linkbuf
;
2778 gchar
*utf8_filename
, *utf8_basename
;
2779 gunichar2
*utf16_basename
;
2783 gboolean ret
= FALSE
;
2785 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FIND
,
2786 (gpointer
*)&find_handle
);
2788 g_warning ("%s: error looking up find handle %p", __func__
,
2790 SetLastError (ERROR_INVALID_HANDLE
);
2794 thr_ret
= _wapi_handle_lock_handle (handle
);
2795 g_assert (thr_ret
== 0);
2798 if (find_handle
->count
>= find_handle
->num
) {
2799 SetLastError (ERROR_NO_MORE_FILES
);
2803 /* stat next match */
2805 filename
= g_build_filename (find_handle
->dir_part
, find_handle
->namelist
[find_handle
->count
++], NULL
);
2807 result
= _wapi_stat (filename
, &buf
);
2808 if (result
== -1 && errno
== ENOENT
) {
2809 /* Might be a dangling symlink */
2810 result
= _wapi_lstat (filename
, &buf
);
2814 DEBUG ("%s: stat failed: %s", __func__
, filename
);
2820 #ifndef __native_client__
2821 result
= _wapi_lstat (filename
, &linkbuf
);
2823 DEBUG ("%s: lstat failed: %s", __func__
, filename
);
2830 utf8_filename
= mono_utf8_from_external (filename
);
2831 if (utf8_filename
== NULL
) {
2832 /* We couldn't turn this filename into utf8 (eg the
2833 * encoding of the name wasn't convertible), so just
2836 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__
, filename
);
2843 DEBUG ("%s: Found [%s]", __func__
, utf8_filename
);
2845 /* fill data block */
2847 if (buf
.st_mtime
< buf
.st_ctime
)
2848 create_time
= buf
.st_mtime
;
2850 create_time
= buf
.st_ctime
;
2852 #ifdef __native_client__
2853 find_data
->dwFileAttributes
= _wapi_stat_to_file_attributes (utf8_filename
, &buf
, NULL
);
2855 find_data
->dwFileAttributes
= _wapi_stat_to_file_attributes (utf8_filename
, &buf
, &linkbuf
);
2858 _wapi_time_t_to_filetime (create_time
, &find_data
->ftCreationTime
);
2859 _wapi_time_t_to_filetime (buf
.st_atime
, &find_data
->ftLastAccessTime
);
2860 _wapi_time_t_to_filetime (buf
.st_mtime
, &find_data
->ftLastWriteTime
);
2862 if (find_data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
2863 find_data
->nFileSizeHigh
= 0;
2864 find_data
->nFileSizeLow
= 0;
2866 find_data
->nFileSizeHigh
= buf
.st_size
>> 32;
2867 find_data
->nFileSizeLow
= buf
.st_size
& 0xFFFFFFFF;
2870 find_data
->dwReserved0
= 0;
2871 find_data
->dwReserved1
= 0;
2873 utf8_basename
= _wapi_basename (utf8_filename
);
2874 utf16_basename
= g_utf8_to_utf16 (utf8_basename
, -1, NULL
, &bytes
,
2876 if(utf16_basename
==NULL
) {
2877 g_free (utf8_basename
);
2878 g_free (utf8_filename
);
2883 /* utf16 is 2 * utf8 */
2886 memset (find_data
->cFileName
, '\0', (MAX_PATH
*2));
2888 /* Truncating a utf16 string like this might leave the last
2891 memcpy (find_data
->cFileName
, utf16_basename
,
2892 bytes
<(MAX_PATH
*2)-2?bytes
:(MAX_PATH
*2)-2);
2894 find_data
->cAlternateFileName
[0] = 0; /* not used */
2896 g_free (utf8_basename
);
2897 g_free (utf8_filename
);
2898 g_free (utf16_basename
);
2901 thr_ret
= _wapi_handle_unlock_handle (handle
);
2902 g_assert (thr_ret
== 0);
2909 * @wapi_handle: the find handle to close.
2911 * Closes find handle @wapi_handle
2913 * Return value: %TRUE on success, %FALSE otherwise.
2915 gboolean
FindClose (gpointer handle
)
2917 struct _WapiHandle_find
*find_handle
;
2921 if (handle
== NULL
) {
2922 SetLastError (ERROR_INVALID_HANDLE
);
2926 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FIND
,
2927 (gpointer
*)&find_handle
);
2929 g_warning ("%s: error looking up find handle %p", __func__
,
2931 SetLastError (ERROR_INVALID_HANDLE
);
2935 thr_ret
= _wapi_handle_lock_handle (handle
);
2936 g_assert (thr_ret
== 0);
2938 g_strfreev (find_handle
->namelist
);
2939 g_free (find_handle
->dir_part
);
2941 thr_ret
= _wapi_handle_unlock_handle (handle
);
2942 g_assert (thr_ret
== 0);
2944 _wapi_handle_unref (handle
);
2951 * @name: a pointer to a NULL-terminated unicode string, that names
2952 * the directory to be created.
2953 * @security: ignored for now
2955 * Creates directory @name
2957 * Return value: %TRUE on success, %FALSE otherwise.
2959 gboolean
CreateDirectory (const gunichar2
*name
,
2960 WapiSecurityAttributes
*security
)
2966 DEBUG("%s: name is NULL", __func__
);
2968 SetLastError (ERROR_INVALID_NAME
);
2972 utf8_name
= mono_unicode_to_external (name
);
2973 if (utf8_name
== NULL
) {
2974 DEBUG ("%s: unicode conversion returned NULL", __func__
);
2976 SetLastError (ERROR_INVALID_NAME
);
2980 result
= _wapi_mkdir (utf8_name
, 0777);
2987 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
2994 * @name: a pointer to a NULL-terminated unicode string, that names
2995 * the directory to be removed.
2997 * Removes directory @name
2999 * Return value: %TRUE on success, %FALSE otherwise.
3001 gboolean
RemoveDirectory (const gunichar2
*name
)
3007 DEBUG("%s: name is NULL", __func__
);
3009 SetLastError (ERROR_INVALID_NAME
);
3013 utf8_name
= mono_unicode_to_external (name
);
3014 if (utf8_name
== NULL
) {
3015 DEBUG ("%s: unicode conversion returned NULL", __func__
);
3017 SetLastError (ERROR_INVALID_NAME
);
3021 result
= _wapi_rmdir (utf8_name
);
3023 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3034 * GetFileAttributes:
3035 * @name: a pointer to a NULL-terminated unicode filename.
3037 * Gets the attributes for @name;
3039 * Return value: %INVALID_FILE_ATTRIBUTES on failure
3041 guint32
GetFileAttributes (const gunichar2
*name
)
3044 struct stat buf
, linkbuf
;
3049 DEBUG("%s: name is NULL", __func__
);
3051 SetLastError (ERROR_INVALID_NAME
);
3055 utf8_name
= mono_unicode_to_external (name
);
3056 if (utf8_name
== NULL
) {
3057 DEBUG ("%s: unicode conversion returned NULL", __func__
);
3059 SetLastError (ERROR_INVALID_PARAMETER
);
3060 return (INVALID_FILE_ATTRIBUTES
);
3063 result
= _wapi_stat (utf8_name
, &buf
);
3064 if (result
== -1 && errno
== ENOENT
) {
3065 /* Might be a dangling symlink... */
3066 result
= _wapi_lstat (utf8_name
, &buf
);
3070 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3072 return (INVALID_FILE_ATTRIBUTES
);
3075 #ifndef __native_client__
3076 result
= _wapi_lstat (utf8_name
, &linkbuf
);
3078 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3080 return (INVALID_FILE_ATTRIBUTES
);
3084 #ifdef __native_client__
3085 ret
= _wapi_stat_to_file_attributes (utf8_name
, &buf
, NULL
);
3087 ret
= _wapi_stat_to_file_attributes (utf8_name
, &buf
, &linkbuf
);
3096 * GetFileAttributesEx:
3097 * @name: a pointer to a NULL-terminated unicode filename.
3098 * @level: must be GetFileExInfoStandard
3099 * @info: pointer to a WapiFileAttributesData structure
3101 * Gets attributes, size and filetimes for @name;
3103 * Return value: %TRUE on success, %FALSE on failure
3105 gboolean
GetFileAttributesEx (const gunichar2
*name
, WapiGetFileExInfoLevels level
, gpointer info
)
3108 WapiFileAttributesData
*data
;
3110 struct stat buf
, linkbuf
;
3114 if (level
!= GetFileExInfoStandard
) {
3115 DEBUG ("%s: info level %d not supported.", __func__
,
3118 SetLastError (ERROR_INVALID_PARAMETER
);
3123 DEBUG("%s: name is NULL", __func__
);
3125 SetLastError (ERROR_INVALID_NAME
);
3129 utf8_name
= mono_unicode_to_external (name
);
3130 if (utf8_name
== NULL
) {
3131 DEBUG ("%s: unicode conversion returned NULL", __func__
);
3133 SetLastError (ERROR_INVALID_PARAMETER
);
3137 result
= _wapi_stat (utf8_name
, &buf
);
3138 if (result
== -1 && errno
== ENOENT
) {
3139 /* Might be a dangling symlink... */
3140 result
= _wapi_lstat (utf8_name
, &buf
);
3144 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3149 result
= _wapi_lstat (utf8_name
, &linkbuf
);
3151 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3156 /* fill data block */
3158 data
= (WapiFileAttributesData
*)info
;
3160 if (buf
.st_mtime
< buf
.st_ctime
)
3161 create_time
= buf
.st_mtime
;
3163 create_time
= buf
.st_ctime
;
3165 data
->dwFileAttributes
= _wapi_stat_to_file_attributes (utf8_name
,
3171 _wapi_time_t_to_filetime (create_time
, &data
->ftCreationTime
);
3172 _wapi_time_t_to_filetime (buf
.st_atime
, &data
->ftLastAccessTime
);
3173 _wapi_time_t_to_filetime (buf
.st_mtime
, &data
->ftLastWriteTime
);
3175 if (data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
3176 data
->nFileSizeHigh
= 0;
3177 data
->nFileSizeLow
= 0;
3180 data
->nFileSizeHigh
= buf
.st_size
>> 32;
3181 data
->nFileSizeLow
= buf
.st_size
& 0xFFFFFFFF;
3189 * @name: name of file
3190 * @attrs: attributes to set
3192 * Changes the attributes on a named file.
3194 * Return value: %TRUE on success, %FALSE on failure.
3196 extern gboolean
SetFileAttributes (const gunichar2
*name
, guint32 attrs
)
3198 /* FIXME: think of something clever to do on unix */
3204 * Currently we only handle one *internal* case, with a value that is
3205 * not standard: 0x80000000, which means `set executable bit'
3209 DEBUG("%s: name is NULL", __func__
);
3211 SetLastError (ERROR_INVALID_NAME
);
3215 utf8_name
= mono_unicode_to_external (name
);
3216 if (utf8_name
== NULL
) {
3217 DEBUG ("%s: unicode conversion returned NULL", __func__
);
3219 SetLastError (ERROR_INVALID_NAME
);
3223 result
= _wapi_stat (utf8_name
, &buf
);
3224 if (result
== -1 && errno
== ENOENT
) {
3225 /* Might be a dangling symlink... */
3226 result
= _wapi_lstat (utf8_name
, &buf
);
3230 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3235 /* Contrary to the documentation, ms allows NORMAL to be
3236 * specified along with other attributes, so dont bother to
3237 * catch that case here.
3239 if (attrs
& FILE_ATTRIBUTE_READONLY
) {
3240 result
= _wapi_chmod (utf8_name
, buf
.st_mode
& ~(S_IWUSR
| S_IWOTH
| S_IWGRP
));
3242 result
= _wapi_chmod (utf8_name
, buf
.st_mode
| S_IWUSR
);
3245 /* Ignore the other attributes for now */
3247 if (attrs
& 0x80000000){
3248 mode_t exec_mask
= 0;
3250 if ((buf
.st_mode
& S_IRUSR
) != 0)
3251 exec_mask
|= S_IXUSR
;
3253 if ((buf
.st_mode
& S_IRGRP
) != 0)
3254 exec_mask
|= S_IXGRP
;
3256 if ((buf
.st_mode
& S_IROTH
) != 0)
3257 exec_mask
|= S_IXOTH
;
3259 result
= chmod (utf8_name
, buf
.st_mode
| exec_mask
);
3261 /* Don't bother to reset executable (might need to change this
3271 * GetCurrentDirectory
3272 * @length: size of the buffer
3273 * @buffer: pointer to buffer that recieves path
3275 * Retrieves the current directory for the current process.
3277 * Return value: number of characters in buffer on success, zero on failure
3279 extern guint32
GetCurrentDirectory (guint32 length
, gunichar2
*buffer
)
3281 gunichar2
*utf16_path
;
3285 #ifdef __native_client__
3286 gchar
*path
= g_get_current_dir ();
3287 if (length
< strlen(path
) + 1 || path
== NULL
)
3289 memcpy (buffer
, path
, strlen(path
) + 1);
3291 if (getcwd ((char*)buffer
, length
) == NULL
) {
3292 if (errno
== ERANGE
) { /*buffer length is not big enough */
3293 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*/
3296 utf16_path
= mono_unicode_from_external (path
, &bytes
);
3297 g_free (utf16_path
);
3301 _wapi_set_last_error_from_errno ();
3306 utf16_path
= mono_unicode_from_external ((gchar
*)buffer
, &bytes
);
3307 count
= (bytes
/2)+1;
3308 g_assert (count
<= length
); /*getcwd must have failed before with ERANGE*/
3310 /* Add the terminator */
3311 memset (buffer
, '\0', bytes
+2);
3312 memcpy (buffer
, utf16_path
, bytes
);
3314 g_free (utf16_path
);
3320 * SetCurrentDirectory
3321 * @path: path to new directory
3323 * Changes the directory path for the current process.
3325 * Return value: %TRUE on success, %FALSE on failure.
3327 extern gboolean
SetCurrentDirectory (const gunichar2
*path
)
3333 SetLastError (ERROR_INVALID_PARAMETER
);
3337 utf8_path
= mono_unicode_to_external (path
);
3338 if (_wapi_chdir (utf8_path
) != 0) {
3339 _wapi_set_last_error_from_errno ();
3349 gboolean
CreatePipe (gpointer
*readpipe
, gpointer
*writepipe
,
3350 WapiSecurityAttributes
*security G_GNUC_UNUSED
, guint32 size
)
3352 struct _WapiHandle_file pipe_read_handle
= {0};
3353 struct _WapiHandle_file pipe_write_handle
= {0};
3354 gpointer read_handle
;
3355 gpointer write_handle
;
3359 mono_once (&io_ops_once
, io_ops_init
);
3361 DEBUG ("%s: Creating pipe", __func__
);
3365 DEBUG ("%s: Error creating pipe: %s", __func__
,
3368 _wapi_set_last_error_from_errno ();
3372 if (filedes
[0] >= _wapi_fd_reserve
||
3373 filedes
[1] >= _wapi_fd_reserve
) {
3374 DEBUG ("%s: File descriptor is too big", __func__
);
3376 SetLastError (ERROR_TOO_MANY_OPEN_FILES
);
3384 /* filedes[0] is open for reading, filedes[1] for writing */
3386 pipe_read_handle
.fd
= filedes
[0];
3387 pipe_read_handle
.fileaccess
= GENERIC_READ
;
3388 read_handle
= _wapi_handle_new_fd (WAPI_HANDLE_PIPE
, filedes
[0],
3390 if (read_handle
== _WAPI_HANDLE_INVALID
) {
3391 g_warning ("%s: error creating pipe read handle", __func__
);
3394 SetLastError (ERROR_GEN_FAILURE
);
3399 pipe_write_handle
.fd
= filedes
[1];
3400 pipe_write_handle
.fileaccess
= GENERIC_WRITE
;
3401 write_handle
= _wapi_handle_new_fd (WAPI_HANDLE_PIPE
, filedes
[1],
3402 &pipe_write_handle
);
3403 if (write_handle
== _WAPI_HANDLE_INVALID
) {
3404 g_warning ("%s: error creating pipe write handle", __func__
);
3405 _wapi_handle_unref (read_handle
);
3409 SetLastError (ERROR_GEN_FAILURE
);
3414 *readpipe
= read_handle
;
3415 *writepipe
= write_handle
;
3417 DEBUG ("%s: Returning pipe: read handle %p, write handle %p",
3418 __func__
, read_handle
, write_handle
);
3423 guint32
GetTempPath (guint32 len
, gunichar2
*buf
)
3425 gchar
*tmpdir
=g_strdup (g_get_tmp_dir ());
3426 gunichar2
*tmpdir16
=NULL
;
3431 if(tmpdir
[strlen (tmpdir
)]!='/') {
3433 tmpdir
=g_strdup_printf ("%s/", g_get_tmp_dir ());
3436 tmpdir16
=mono_unicode_from_external (tmpdir
, &bytes
);
3437 if(tmpdir16
==NULL
) {
3444 DEBUG ("%s: Size %d smaller than needed (%ld)",
3445 __func__
, len
, dirlen
+1);
3449 /* Add the terminator */
3450 memset (buf
, '\0', bytes
+2);
3451 memcpy (buf
, tmpdir16
, bytes
);
3457 if(tmpdir16
!=NULL
) {
3465 #ifdef HAVE_GETFSSTAT
3466 /* Darwin has getfsstat */
3467 gint32
GetLogicalDriveStrings (guint32 len
, gunichar2
*buf
)
3469 struct statfs
*stats
;
3472 glong length
, total
= 0;
3474 n
= getfsstat (NULL
, 0, MNT_NOWAIT
);
3477 size
= n
* sizeof (struct statfs
);
3478 stats
= (struct statfs
*) g_malloc (size
);
3481 if (getfsstat (stats
, size
, MNT_NOWAIT
) == -1){
3485 for (i
= 0; i
< n
; i
++){
3486 dir
= g_utf8_to_utf16 (stats
[i
].f_mntonname
, -1, NULL
, &length
, NULL
);
3487 if (total
+ length
< len
){
3488 memcpy (buf
+ total
, dir
, sizeof (gunichar2
) * length
);
3489 buf
[total
+length
] = 0;
3492 total
+= length
+ 1;
3501 /* In-place octal sequence replacement */
3503 unescape_octal (gchar
*str
)
3512 while (*rptr
!= '\0') {
3513 if (*rptr
== '\\') {
3516 c
= (*(rptr
++) - '0') << 6;
3517 c
+= (*(rptr
++) - '0') << 3;
3518 c
+= *(rptr
++) - '0';
3520 } else if (wptr
!= rptr
) {
3528 static gint32
GetLogicalDriveStrings_Mtab (guint32 len
, gunichar2
*buf
);
3531 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
3532 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
3533 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
3538 guint32 buffer_index
;
3539 guint32 mountpoint_index
;
3540 guint32 field_number
;
3541 guint32 allocated_size
;
3542 guint32 fsname_index
;
3543 guint32 fstype_index
;
3544 gchar mountpoint
[GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER
+ 1];
3545 gchar
*mountpoint_allocated
;
3546 gchar buffer
[GET_LOGICAL_DRIVE_STRINGS_BUFFER
];
3547 gchar fsname
[GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
+ 1];
3548 gchar fstype
[GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
+ 1];
3551 gboolean check_mount_source
;
3552 } LinuxMountInfoParseState
;
3554 static gboolean
GetLogicalDriveStrings_Mounts (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3555 static gboolean
GetLogicalDriveStrings_MountInfo (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3556 static void append_to_mountpoint (LinuxMountInfoParseState
*state
);
3557 static gboolean
add_drive_string (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
);
3559 gint32
GetLogicalDriveStrings (guint32 len
, gunichar2
*buf
)
3563 LinuxMountInfoParseState state
;
3564 gboolean (*parser
)(guint32
, gunichar2
*, LinuxMountInfoParseState
*) = NULL
;
3566 memset (buf
, 0, len
* sizeof (gunichar2
));
3567 fd
= open ("/proc/self/mountinfo", O_RDONLY
);
3569 parser
= GetLogicalDriveStrings_MountInfo
;
3571 fd
= open ("/proc/mounts", O_RDONLY
);
3573 parser
= GetLogicalDriveStrings_Mounts
;
3577 ret
= GetLogicalDriveStrings_Mtab (len
, buf
);
3581 memset (&state
, 0, sizeof (LinuxMountInfoParseState
));
3582 state
.field_number
= 1;
3583 state
.delimiter
= ' ';
3585 while ((state
.nbytes
= read (fd
, state
.buffer
, GET_LOGICAL_DRIVE_STRINGS_BUFFER
)) > 0) {
3586 state
.buffer_index
= 0;
3588 while ((*parser
)(len
, buf
, &state
)) {
3589 if (state
.buffer
[state
.buffer_index
] == '\n') {
3590 gboolean quit
= add_drive_string (len
, buf
, &state
);
3591 state
.field_number
= 1;
3592 state
.buffer_index
++;
3593 if (state
.mountpoint_allocated
) {
3594 g_free (state
.mountpoint_allocated
);
3595 state
.mountpoint_allocated
= NULL
;
3612 static gboolean
GetLogicalDriveStrings_Mounts (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
3616 if (state
->field_number
== 1)
3617 state
->check_mount_source
= TRUE
;
3619 while (state
->buffer_index
< (guint32
)state
->nbytes
) {
3620 if (state
->buffer
[state
->buffer_index
] == state
->delimiter
) {
3621 state
->field_number
++;
3622 switch (state
->field_number
) {
3624 state
->mountpoint_index
= 0;
3628 if (state
->mountpoint_allocated
)
3629 state
->mountpoint_allocated
[state
->mountpoint_index
] = 0;
3631 state
->mountpoint
[state
->mountpoint_index
] = 0;
3635 ptr
= (gchar
*)memchr (state
->buffer
+ state
->buffer_index
, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER
- state
->buffer_index
);
3637 state
->buffer_index
= (ptr
- (gchar
*)state
->buffer
) - 1;
3639 state
->buffer_index
= state
->nbytes
;
3642 state
->buffer_index
++;
3644 } else if (state
->buffer
[state
->buffer_index
] == '\n')
3647 switch (state
->field_number
) {
3649 if (state
->check_mount_source
) {
3650 if (state
->fsname_index
== 0 && state
->buffer
[state
->buffer_index
] == '/') {
3651 /* We can ignore the rest, it's a device
3653 state
->check_mount_source
= FALSE
;
3654 state
->fsname
[state
->fsname_index
++] = '/';
3657 if (state
->fsname_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
3658 state
->fsname
[state
->fsname_index
++] = state
->buffer
[state
->buffer_index
];
3663 append_to_mountpoint (state
);
3667 if (state
->fstype_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
3668 state
->fstype
[state
->fstype_index
++] = state
->buffer
[state
->buffer_index
];
3672 state
->buffer_index
++;
3678 static gboolean
GetLogicalDriveStrings_MountInfo (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
3680 while (state
->buffer_index
< (guint32
)state
->nbytes
) {
3681 if (state
->buffer
[state
->buffer_index
] == state
->delimiter
) {
3682 state
->field_number
++;
3683 switch (state
->field_number
) {
3685 state
->mountpoint_index
= 0;
3689 if (state
->mountpoint_allocated
)
3690 state
->mountpoint_allocated
[state
->mountpoint_index
] = 0;
3692 state
->mountpoint
[state
->mountpoint_index
] = 0;
3696 state
->delimiter
= '-';
3700 state
->delimiter
= ' ';
3704 state
->check_mount_source
= TRUE
;
3707 state
->buffer_index
++;
3709 } else if (state
->buffer
[state
->buffer_index
] == '\n')
3712 switch (state
->field_number
) {
3714 append_to_mountpoint (state
);
3718 if (state
->fstype_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
3719 state
->fstype
[state
->fstype_index
++] = state
->buffer
[state
->buffer_index
];
3723 if (state
->check_mount_source
) {
3724 if (state
->fsname_index
== 0 && state
->buffer
[state
->buffer_index
] == '/') {
3725 /* We can ignore the rest, it's a device
3727 state
->check_mount_source
= FALSE
;
3728 state
->fsname
[state
->fsname_index
++] = '/';
3731 if (state
->fsname_index
< GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER
)
3732 state
->fsname
[state
->fsname_index
++] = state
->buffer
[state
->buffer_index
];
3737 state
->buffer_index
++;
3744 append_to_mountpoint (LinuxMountInfoParseState
*state
)
3746 gchar ch
= state
->buffer
[state
->buffer_index
];
3747 if (state
->mountpoint_allocated
) {
3748 if (state
->mountpoint_index
>= state
->allocated_size
) {
3749 guint32 newsize
= (state
->allocated_size
<< 1) + 1;
3750 gchar
*newbuf
= g_malloc0 (newsize
* sizeof (gchar
));
3752 memcpy (newbuf
, state
->mountpoint_allocated
, state
->mountpoint_index
);
3753 g_free (state
->mountpoint_allocated
);
3754 state
->mountpoint_allocated
= newbuf
;
3755 state
->allocated_size
= newsize
;
3757 state
->mountpoint_allocated
[state
->mountpoint_index
++] = ch
;
3759 if (state
->mountpoint_index
>= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER
) {
3760 state
->allocated_size
= (state
->mountpoint_index
<< 1) + 1;
3761 state
->mountpoint_allocated
= g_malloc0 (state
->allocated_size
* sizeof (gchar
));
3762 memcpy (state
->mountpoint_allocated
, state
->mountpoint
, state
->mountpoint_index
);
3763 state
->mountpoint_allocated
[state
->mountpoint_index
++] = ch
;
3765 state
->mountpoint
[state
->mountpoint_index
++] = ch
;
3770 add_drive_string (guint32 len
, gunichar2
*buf
, LinuxMountInfoParseState
*state
)
3772 gboolean quit
= FALSE
;
3773 gboolean ignore_entry
;
3775 if (state
->fsname_index
== 1 && state
->fsname
[0] == '/')
3776 ignore_entry
= FALSE
;
3777 else if (state
->fsname_index
== 0 || memcmp ("none", state
->fsname
, state
->fsname_index
) == 0)
3778 ignore_entry
= TRUE
;
3779 else if (state
->fstype_index
>= 5 && memcmp ("fuse.", state
->fstype
, 5) == 0) {
3780 /* Ignore GNOME's gvfs */
3781 if (state
->fstype_index
== 21 && memcmp ("fuse.gvfs-fuse-daemon", state
->fstype
, state
->fstype_index
) == 0)
3782 ignore_entry
= TRUE
;
3784 ignore_entry
= FALSE
;
3785 } else if (state
->fstype_index
== 3 && memcmp ("nfs", state
->fstype
, state
->fstype_index
) == 0)
3786 ignore_entry
= FALSE
;
3788 ignore_entry
= TRUE
;
3790 if (!ignore_entry
) {
3793 gchar
*mountpoint
= state
->mountpoint_allocated
? state
->mountpoint_allocated
: state
->mountpoint
;
3795 unescape_octal (mountpoint
);
3796 dir
= g_utf8_to_utf16 (mountpoint
, -1, NULL
, &length
, NULL
);
3797 if (state
->total
+ length
+ 1 > len
) {
3799 state
->total
= len
* 2;
3802 memcpy (buf
+ state
->total
, dir
, sizeof (gunichar2
) * length
);
3803 state
->total
+= length
;
3807 state
->fsname_index
= 0;
3808 state
->fstype_index
= 0;
3814 GetLogicalDriveStrings (guint32 len
, gunichar2
*buf
)
3816 return GetLogicalDriveStrings_Mtab (len
, buf
);
3820 GetLogicalDriveStrings_Mtab (guint32 len
, gunichar2
*buf
)
3823 gunichar2
*ptr
, *dir
;
3824 glong length
, total
= 0;
3828 memset (buf
, 0, sizeof (gunichar2
) * (len
+ 1));
3833 /* Sigh, mntent and friends don't work well.
3834 * It stops on the first line that doesn't begin with a '/'.
3835 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
3836 fp
= fopen ("/etc/mtab", "rt");
3838 fp
= fopen ("/etc/mnttab", "rt");
3844 while (fgets (buffer
, 512, fp
) != NULL
) {
3848 splitted
= g_strsplit (buffer
, " ", 0);
3849 if (!*splitted
|| !*(splitted
+ 1)) {
3850 g_strfreev (splitted
);
3854 unescape_octal (*(splitted
+ 1));
3855 dir
= g_utf8_to_utf16 (*(splitted
+ 1), -1, NULL
, &length
, NULL
);
3856 g_strfreev (splitted
);
3857 if (total
+ length
+ 1 > len
) {
3860 return len
* 2; /* guess */
3863 memcpy (ptr
+ total
, dir
, sizeof (gunichar2
) * length
);
3865 total
+= length
+ 1;
3870 /* Commented out, does not work with my mtab!!! - Gonz */
3871 #ifdef NOTENABLED /* HAVE_MNTENT_H */
3875 gunichar2
*ptr
, *dir
;
3876 glong len
, total
= 0;
3879 fp
= setmntent ("/etc/mtab", "rt");
3881 fp
= setmntent ("/etc/mnttab", "rt");
3887 while ((mnt
= getmntent (fp
)) != NULL
) {
3888 g_print ("GOT %s\n", mnt
->mnt_dir
);
3889 dir
= g_utf8_to_utf16 (mnt
->mnt_dir
, &len
, NULL
, NULL
, NULL
);
3890 if (total
+ len
+ 1 > len
) {
3891 return len
* 2; /* guess */
3894 memcpy (ptr
+ total
, dir
, sizeof (gunichar2
) * len
);
3906 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
3907 gboolean
GetDiskFreeSpaceEx(const gunichar2
*path_name
, WapiULargeInteger
*free_bytes_avail
,
3908 WapiULargeInteger
*total_number_of_bytes
,
3909 WapiULargeInteger
*total_number_of_free_bytes
)
3912 struct statvfs fsstat
;
3913 #elif defined(HAVE_STATFS)
3914 struct statfs fsstat
;
3916 gboolean isreadonly
;
3917 gchar
*utf8_path_name
;
3919 unsigned long block_size
;
3921 if (path_name
== NULL
) {
3922 utf8_path_name
= g_strdup (g_get_current_dir());
3923 if (utf8_path_name
== NULL
) {
3924 SetLastError (ERROR_DIRECTORY
);
3929 utf8_path_name
= mono_unicode_to_external (path_name
);
3930 if (utf8_path_name
== NULL
) {
3931 DEBUG("%s: unicode conversion returned NULL", __func__
);
3933 SetLastError (ERROR_INVALID_NAME
);
3940 ret
= statvfs (utf8_path_name
, &fsstat
);
3941 isreadonly
= ((fsstat
.f_flag
& ST_RDONLY
) == ST_RDONLY
);
3942 block_size
= fsstat
.f_frsize
;
3943 #elif defined(HAVE_STATFS)
3944 ret
= statfs (utf8_path_name
, &fsstat
);
3945 #if defined (MNT_RDONLY)
3946 isreadonly
= ((fsstat
.f_flags
& MNT_RDONLY
) == MNT_RDONLY
);
3947 #elif defined (MS_RDONLY)
3948 isreadonly
= ((fsstat
.f_flags
& MS_RDONLY
) == MS_RDONLY
);
3950 block_size
= fsstat
.f_bsize
;
3952 } while(ret
== -1 && errno
== EINTR
);
3954 g_free(utf8_path_name
);
3957 _wapi_set_last_error_from_errno ();
3958 DEBUG ("%s: statvfs failed: %s", __func__
, strerror (errno
));
3962 /* total number of free bytes for non-root */
3963 if (free_bytes_avail
!= NULL
) {
3965 free_bytes_avail
->QuadPart
= 0;
3968 free_bytes_avail
->QuadPart
= block_size
* (guint64
)fsstat
.f_bavail
;
3972 /* total number of bytes available for non-root */
3973 if (total_number_of_bytes
!= NULL
) {
3974 total_number_of_bytes
->QuadPart
= block_size
* (guint64
)fsstat
.f_blocks
;
3977 /* total number of bytes available for root */
3978 if (total_number_of_free_bytes
!= NULL
) {
3980 total_number_of_free_bytes
->QuadPart
= 0;
3983 total_number_of_free_bytes
->QuadPart
= block_size
* (guint64
)fsstat
.f_bfree
;
3990 gboolean
GetDiskFreeSpaceEx(const gunichar2
*path_name
, WapiULargeInteger
*free_bytes_avail
,
3991 WapiULargeInteger
*total_number_of_bytes
,
3992 WapiULargeInteger
*total_number_of_free_bytes
)
3994 if (free_bytes_avail
!= NULL
) {
3995 free_bytes_avail
->QuadPart
= (guint64
) -1;
3998 if (total_number_of_bytes
!= NULL
) {
3999 total_number_of_bytes
->QuadPart
= (guint64
) -1;
4002 if (total_number_of_free_bytes
!= NULL
) {
4003 total_number_of_free_bytes
->QuadPart
= (guint64
) -1;
4011 * General Unix support
4016 const long fstypeid
;
4018 const gchar
* fstype
;
4021 static _wapi_drive_type _wapi_drive_types
[] = {
4023 { DRIVE_REMOTE
, "afp" },
4024 { DRIVE_REMOTE
, "autofs" },
4025 { DRIVE_CDROM
, "cddafs" },
4026 { DRIVE_CDROM
, "cd9660" },
4027 { DRIVE_RAMDISK
, "devfs" },
4028 { DRIVE_FIXED
, "exfat" },
4029 { DRIVE_RAMDISK
, "fdesc" },
4030 { DRIVE_REMOTE
, "ftp" },
4031 { DRIVE_FIXED
, "hfs" },
4032 { DRIVE_FIXED
, "msdos" },
4033 { DRIVE_REMOTE
, "nfs" },
4034 { DRIVE_FIXED
, "ntfs" },
4035 { DRIVE_REMOTE
, "smbfs" },
4036 { DRIVE_FIXED
, "udf" },
4037 { DRIVE_REMOTE
, "webdav" },
4038 { DRIVE_UNKNOWN
, NULL
}
4040 { DRIVE_FIXED
, ADFS_SUPER_MAGIC
, "adfs"},
4041 { DRIVE_FIXED
, AFFS_SUPER_MAGIC
, "affs"},
4042 { DRIVE_REMOTE
, AFS_SUPER_MAGIC
, "afs"},
4043 { DRIVE_RAMDISK
, AUTOFS_SUPER_MAGIC
, "autofs"},
4044 { DRIVE_RAMDISK
, AUTOFS_SBI_MAGIC
, "autofs4"},
4045 { DRIVE_REMOTE
, CODA_SUPER_MAGIC
, "coda" },
4046 { DRIVE_RAMDISK
, CRAMFS_MAGIC
, "cramfs"},
4047 { DRIVE_RAMDISK
, CRAMFS_MAGIC_WEND
, "cramfs"},
4048 { DRIVE_REMOTE
, CIFS_MAGIC_NUMBER
, "cifs"},
4049 { DRIVE_RAMDISK
, DEBUGFS_MAGIC
, "debugfs"},
4050 { DRIVE_RAMDISK
, SYSFS_MAGIC
, "sysfs"},
4051 { DRIVE_RAMDISK
, SECURITYFS_MAGIC
, "securityfs"},
4052 { DRIVE_RAMDISK
, SELINUX_MAGIC
, "selinuxfs"},
4053 { DRIVE_RAMDISK
, RAMFS_MAGIC
, "ramfs"},
4054 { DRIVE_FIXED
, SQUASHFS_MAGIC
, "squashfs"},
4055 { DRIVE_FIXED
, EFS_SUPER_MAGIC
, "efs"},
4056 { DRIVE_FIXED
, EXT2_SUPER_MAGIC
, "ext"},
4057 { DRIVE_FIXED
, EXT3_SUPER_MAGIC
, "ext"},
4058 { DRIVE_FIXED
, EXT4_SUPER_MAGIC
, "ext"},
4059 { DRIVE_REMOTE
, XENFS_SUPER_MAGIC
, "xenfs"},
4060 { DRIVE_FIXED
, BTRFS_SUPER_MAGIC
, "btrfs"},
4061 { DRIVE_FIXED
, HFS_SUPER_MAGIC
, "hfs"},
4062 { DRIVE_FIXED
, HFSPLUS_SUPER_MAGIC
, "hfsplus"},
4063 { DRIVE_FIXED
, HPFS_SUPER_MAGIC
, "hpfs"},
4064 { DRIVE_RAMDISK
, HUGETLBFS_MAGIC
, "hugetlbfs"},
4065 { DRIVE_CDROM
, ISOFS_SUPER_MAGIC
, "iso"},
4066 { DRIVE_FIXED
, JFFS2_SUPER_MAGIC
, "jffs2"},
4067 { DRIVE_RAMDISK
, ANON_INODE_FS_MAGIC
, "anon_inode"},
4068 { DRIVE_FIXED
, JFS_SUPER_MAGIC
, "jfs"},
4069 { DRIVE_FIXED
, MINIX_SUPER_MAGIC
, "minix"},
4070 { DRIVE_FIXED
, MINIX_SUPER_MAGIC2
, "minix v2"},
4071 { DRIVE_FIXED
, MINIX2_SUPER_MAGIC
, "minix2"},
4072 { DRIVE_FIXED
, MINIX2_SUPER_MAGIC2
, "minix2 v2"},
4073 { DRIVE_FIXED
, MINIX3_SUPER_MAGIC
, "minix3"},
4074 { DRIVE_FIXED
, MSDOS_SUPER_MAGIC
, "msdos"},
4075 { DRIVE_REMOTE
, NCP_SUPER_MAGIC
, "ncp"},
4076 { DRIVE_REMOTE
, NFS_SUPER_MAGIC
, "nfs"},
4077 { DRIVE_FIXED
, NTFS_SB_MAGIC
, "ntfs"},
4078 { DRIVE_RAMDISK
, OPENPROM_SUPER_MAGIC
, "openpromfs"},
4079 { DRIVE_RAMDISK
, PROC_SUPER_MAGIC
, "proc"},
4080 { DRIVE_FIXED
, QNX4_SUPER_MAGIC
, "qnx4"},
4081 { DRIVE_FIXED
, REISERFS_SUPER_MAGIC
, "reiserfs"},
4082 { DRIVE_RAMDISK
, ROMFS_MAGIC
, "romfs"},
4083 { DRIVE_REMOTE
, SMB_SUPER_MAGIC
, "samba"},
4084 { DRIVE_RAMDISK
, CGROUP_SUPER_MAGIC
, "cgroupfs"},
4085 { DRIVE_RAMDISK
, FUTEXFS_SUPER_MAGIC
, "futexfs"},
4086 { DRIVE_FIXED
, SYSV2_SUPER_MAGIC
, "sysv2"},
4087 { DRIVE_FIXED
, SYSV4_SUPER_MAGIC
, "sysv4"},
4088 { DRIVE_RAMDISK
, TMPFS_MAGIC
, "tmpfs"},
4089 { DRIVE_RAMDISK
, DEVPTS_SUPER_MAGIC
, "devpts"},
4090 { DRIVE_CDROM
, UDF_SUPER_MAGIC
, "udf"},
4091 { DRIVE_FIXED
, UFS_MAGIC
, "ufs"},
4092 { DRIVE_FIXED
, UFS_MAGIC_BW
, "ufs"},
4093 { DRIVE_FIXED
, UFS2_MAGIC
, "ufs2"},
4094 { DRIVE_FIXED
, UFS_CIGAM
, "ufs"},
4095 { DRIVE_RAMDISK
, USBDEVICE_SUPER_MAGIC
, "usbdev"},
4096 { DRIVE_FIXED
, XENIX_SUPER_MAGIC
, "xenix"},
4097 { DRIVE_FIXED
, XFS_SB_MAGIC
, "xfs"},
4098 { DRIVE_RAMDISK
, FUSE_SUPER_MAGIC
, "fuse"},
4099 { DRIVE_FIXED
, V9FS_MAGIC
, "9p"},
4100 { DRIVE_REMOTE
, CEPH_SUPER_MAGIC
, "ceph"},
4101 { DRIVE_RAMDISK
, CONFIGFS_MAGIC
, "configfs"},
4102 { DRIVE_RAMDISK
, ECRYPTFS_SUPER_MAGIC
, "eCryptfs"},
4103 { DRIVE_FIXED
, EXOFS_SUPER_MAGIC
, "exofs"},
4104 { DRIVE_FIXED
, VXFS_SUPER_MAGIC
, "vxfs"},
4105 { DRIVE_FIXED
, VXFS_OLT_MAGIC
, "vxfs_olt"},
4106 { DRIVE_REMOTE
, GFS2_MAGIC
, "gfs2"},
4107 { DRIVE_FIXED
, LOGFS_MAGIC_U32
, "logfs"},
4108 { DRIVE_FIXED
, OCFS2_SUPER_MAGIC
, "ocfs2"},
4109 { DRIVE_FIXED
, OMFS_MAGIC
, "omfs"},
4110 { DRIVE_FIXED
, UBIFS_SUPER_MAGIC
, "ubifs"},
4111 { DRIVE_UNKNOWN
, 0, NULL
}
4113 { DRIVE_RAMDISK
, "ramfs" },
4114 { DRIVE_RAMDISK
, "tmpfs" },
4115 { DRIVE_RAMDISK
, "proc" },
4116 { DRIVE_RAMDISK
, "sysfs" },
4117 { DRIVE_RAMDISK
, "debugfs" },
4118 { DRIVE_RAMDISK
, "devpts" },
4119 { DRIVE_RAMDISK
, "securityfs" },
4120 { DRIVE_CDROM
, "iso9660" },
4121 { DRIVE_FIXED
, "ext2" },
4122 { DRIVE_FIXED
, "ext3" },
4123 { DRIVE_FIXED
, "ext4" },
4124 { DRIVE_FIXED
, "sysv" },
4125 { DRIVE_FIXED
, "reiserfs" },
4126 { DRIVE_FIXED
, "ufs" },
4127 { DRIVE_FIXED
, "vfat" },
4128 { DRIVE_FIXED
, "msdos" },
4129 { DRIVE_FIXED
, "udf" },
4130 { DRIVE_FIXED
, "hfs" },
4131 { DRIVE_FIXED
, "hpfs" },
4132 { DRIVE_FIXED
, "qnx4" },
4133 { DRIVE_FIXED
, "ntfs" },
4134 { DRIVE_FIXED
, "ntfs-3g" },
4135 { DRIVE_REMOTE
, "smbfs" },
4136 { DRIVE_REMOTE
, "fuse" },
4137 { DRIVE_REMOTE
, "nfs" },
4138 { DRIVE_REMOTE
, "nfs4" },
4139 { DRIVE_REMOTE
, "cifs" },
4140 { DRIVE_REMOTE
, "ncpfs" },
4141 { DRIVE_REMOTE
, "coda" },
4142 { DRIVE_REMOTE
, "afs" },
4143 { DRIVE_UNKNOWN
, NULL
}
4148 static guint32
_wapi_get_drive_type(long f_type
)
4150 _wapi_drive_type
*current
;
4152 current
= &_wapi_drive_types
[0];
4153 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4154 if (current
->fstypeid
== f_type
)
4155 return current
->drive_type
;
4159 return DRIVE_UNKNOWN
;
4162 static guint32
_wapi_get_drive_type(const gchar
* fstype
)
4164 _wapi_drive_type
*current
;
4166 current
= &_wapi_drive_types
[0];
4167 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4168 if (strcmp (current
->fstype
, fstype
) == 0)
4174 return current
->drive_type
;
4178 #if defined (PLATFORM_MACOSX) || defined (__linux__)
4180 GetDriveTypeFromPath (const char *utf8_root_path_name
)
4184 if (statfs (utf8_root_path_name
, &buf
) == -1)
4185 return DRIVE_UNKNOWN
;
4187 return _wapi_get_drive_type (buf
.f_fstypename
);
4189 return _wapi_get_drive_type (buf
.f_type
);
4194 GetDriveTypeFromPath (const gchar
*utf8_root_path_name
)
4201 fp
= fopen ("/etc/mtab", "rt");
4203 fp
= fopen ("/etc/mnttab", "rt");
4205 return(DRIVE_UNKNOWN
);
4208 drive_type
= DRIVE_NO_ROOT_DIR
;
4209 while (fgets (buffer
, 512, fp
) != NULL
) {
4210 splitted
= g_strsplit (buffer
, " ", 0);
4211 if (!*splitted
|| !*(splitted
+ 1) || !*(splitted
+ 2)) {
4212 g_strfreev (splitted
);
4216 /* compare given root_path_name with the one from mtab,
4217 if length of utf8_root_path_name is zero it must be the root dir */
4218 if (strcmp (*(splitted
+ 1), utf8_root_path_name
) == 0 ||
4219 (strcmp (*(splitted
+ 1), "/") == 0 && strlen (utf8_root_path_name
) == 0)) {
4220 drive_type
= _wapi_get_drive_type (*(splitted
+ 2));
4221 /* it is possible this path might be mounted again with
4222 a known type...keep looking */
4223 if (drive_type
!= DRIVE_UNKNOWN
) {
4224 g_strfreev (splitted
);
4229 g_strfreev (splitted
);
4237 guint32
GetDriveType(const gunichar2
*root_path_name
)
4239 gchar
*utf8_root_path_name
;
4242 if (root_path_name
== NULL
) {
4243 utf8_root_path_name
= g_strdup (g_get_current_dir());
4244 if (utf8_root_path_name
== NULL
) {
4245 return(DRIVE_NO_ROOT_DIR
);
4249 utf8_root_path_name
= mono_unicode_to_external (root_path_name
);
4250 if (utf8_root_path_name
== NULL
) {
4251 DEBUG("%s: unicode conversion returned NULL", __func__
);
4252 return(DRIVE_NO_ROOT_DIR
);
4255 /* strip trailing slash for compare below */
4256 if (g_str_has_suffix(utf8_root_path_name
, "/") && utf8_root_path_name
[1] != 0) {
4257 utf8_root_path_name
[strlen(utf8_root_path_name
) - 1] = 0;
4260 drive_type
= GetDriveTypeFromPath (utf8_root_path_name
);
4261 g_free (utf8_root_path_name
);
4263 return (drive_type
);
4266 #if defined (PLATFORM_MACOSX) || defined (__linux__) || defined(PLATFORM_BSD) || defined(__native_client__) || defined(__FreeBSD_kernel__)
4268 get_fstypename (gchar
*utfpath
)
4270 #if defined (PLATFORM_MACOSX) || defined (__linux__)
4273 _wapi_drive_type
*current
;
4275 if (statfs (utfpath
, &stat
) == -1)
4278 return g_strdup (stat
.f_fstypename
);
4280 current
= &_wapi_drive_types
[0];
4281 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
4282 if (stat
.f_type
== current
->fstypeid
)
4283 return g_strdup (current
->fstype
);
4293 /* Linux has struct statfs which has a different layout */
4295 GetVolumeInformation (const gunichar2
*path
, gunichar2
*volumename
, int volumesize
, int *outserial
, int *maxcomp
, int *fsflags
, gunichar2
*fsbuffer
, int fsbuffersize
)
4299 gboolean status
= FALSE
;
4302 // We only support getting the file system type
4303 if (fsbuffer
== NULL
)
4306 utfpath
= mono_unicode_to_external (path
);
4307 if ((fstypename
= get_fstypename (utfpath
)) != NULL
){
4308 gunichar2
*ret
= g_utf8_to_utf16 (fstypename
, -1, NULL
, &len
, NULL
);
4309 if (ret
!= NULL
&& len
< fsbuffersize
){
4310 memcpy (fsbuffer
, ret
, len
* sizeof (gunichar2
));
4316 g_free (fstypename
);
4325 _wapi_io_init (void)
4327 mono_mutex_init (&stdhandle_mutex
);