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.
18 #ifdef HAVE_SYS_STATVFS_H
19 #include <sys/statvfs.h>
20 #elif defined(HAVE_SYS_STATFS_H)
21 #include <sys/statfs.h>
22 #elif defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
23 #include <sys/param.h>
24 #include <sys/mount.h>
26 #include <sys/types.h>
32 #include <mono/io-layer/wapi.h>
33 #include <mono/io-layer/wapi-private.h>
34 #include <mono/io-layer/handles-private.h>
35 #include <mono/io-layer/io-private.h>
36 #include <mono/io-layer/timefuncs-private.h>
37 #include <mono/io-layer/thread-private.h>
38 #include <mono/io-layer/io-portability.h>
39 #include <mono/utils/strenc.h>
43 static void file_close (gpointer handle
, gpointer data
);
44 static WapiFileType
file_getfiletype(void);
45 static gboolean
file_read(gpointer handle
, gpointer buffer
,
46 guint32 numbytes
, guint32
*bytesread
,
47 WapiOverlapped
*overlapped
);
48 static gboolean
file_write(gpointer handle
, gconstpointer buffer
,
49 guint32 numbytes
, guint32
*byteswritten
,
50 WapiOverlapped
*overlapped
);
51 static gboolean
file_flush(gpointer handle
);
52 static guint32
file_seek(gpointer handle
, gint32 movedistance
,
53 gint32
*highmovedistance
, WapiSeekMethod method
);
54 static gboolean
file_setendoffile(gpointer handle
);
55 static guint32
file_getfilesize(gpointer handle
, guint32
*highsize
);
56 static gboolean
file_getfiletime(gpointer handle
, WapiFileTime
*create_time
,
57 WapiFileTime
*last_access
,
58 WapiFileTime
*last_write
);
59 static gboolean
file_setfiletime(gpointer handle
,
60 const WapiFileTime
*create_time
,
61 const WapiFileTime
*last_access
,
62 const WapiFileTime
*last_write
);
64 /* File handle is only signalled for overlapped IO */
65 struct _WapiHandleOps _wapi_file_ops
= {
66 file_close
, /* close */
70 NULL
, /* special_wait */
74 void _wapi_file_details (gpointer handle_info
)
76 struct _WapiHandle_file
*file
= (struct _WapiHandle_file
*)handle_info
;
78 g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u",
80 file
->fileaccess
&GENERIC_READ
?'R':'.',
81 file
->fileaccess
&GENERIC_WRITE
?'W':'.',
82 file
->fileaccess
&GENERIC_EXECUTE
?'X':'.',
83 file
->sharemode
&FILE_SHARE_READ
?'R':'.',
84 file
->sharemode
&FILE_SHARE_WRITE
?'W':'.',
85 file
->sharemode
&FILE_SHARE_DELETE
?'D':'.',
89 static void console_close (gpointer handle
, gpointer data
);
90 static WapiFileType
console_getfiletype(void);
91 static gboolean
console_read(gpointer handle
, gpointer buffer
,
92 guint32 numbytes
, guint32
*bytesread
,
93 WapiOverlapped
*overlapped
);
94 static gboolean
console_write(gpointer handle
, gconstpointer buffer
,
95 guint32 numbytes
, guint32
*byteswritten
,
96 WapiOverlapped
*overlapped
);
98 /* Console is mostly the same as file, except it can block waiting for
101 struct _WapiHandleOps _wapi_console_ops
= {
102 console_close
, /* close */
106 NULL
, /* special_wait */
110 void _wapi_console_details (gpointer handle_info
)
112 _wapi_file_details (handle_info
);
115 /* Find handle has no ops.
117 struct _WapiHandleOps _wapi_find_ops
= {
122 NULL
, /* special_wait */
126 static void pipe_close (gpointer handle
, gpointer data
);
127 static WapiFileType
pipe_getfiletype (void);
128 static gboolean
pipe_read (gpointer handle
, gpointer buffer
, guint32 numbytes
,
129 guint32
*bytesread
, WapiOverlapped
*overlapped
);
130 static gboolean
pipe_write (gpointer handle
, gconstpointer buffer
,
131 guint32 numbytes
, guint32
*byteswritten
,
132 WapiOverlapped
*overlapped
);
136 struct _WapiHandleOps _wapi_pipe_ops
= {
137 pipe_close
, /* close */
141 NULL
, /* special_wait */
145 void _wapi_pipe_details (gpointer handle_info
)
147 _wapi_file_details (handle_info
);
150 static const struct {
151 /* File, console and pipe handles */
152 WapiFileType (*getfiletype
)(void);
154 /* File, console and pipe handles */
155 gboolean (*readfile
)(gpointer handle
, gpointer buffer
,
156 guint32 numbytes
, guint32
*bytesread
,
157 WapiOverlapped
*overlapped
);
158 gboolean (*writefile
)(gpointer handle
, gconstpointer buffer
,
159 guint32 numbytes
, guint32
*byteswritten
,
160 WapiOverlapped
*overlapped
);
161 gboolean (*flushfile
)(gpointer handle
);
164 guint32 (*seek
)(gpointer handle
, gint32 movedistance
,
165 gint32
*highmovedistance
, WapiSeekMethod method
);
166 gboolean (*setendoffile
)(gpointer handle
);
167 guint32 (*getfilesize
)(gpointer handle
, guint32
*highsize
);
168 gboolean (*getfiletime
)(gpointer handle
, WapiFileTime
*create_time
,
169 WapiFileTime
*last_access
,
170 WapiFileTime
*last_write
);
171 gboolean (*setfiletime
)(gpointer handle
,
172 const WapiFileTime
*create_time
,
173 const WapiFileTime
*last_access
,
174 const WapiFileTime
*last_write
);
175 } io_ops
[WAPI_HANDLE_COUNT
]={
176 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
179 file_read
, file_write
,
180 file_flush
, file_seek
,
186 {console_getfiletype
,
189 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
191 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
193 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
195 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
197 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
198 /* socket (will need at least read and write) */
199 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
201 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
203 {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
208 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
},
211 static mono_once_t io_ops_once
=MONO_ONCE_INIT
;
212 static gboolean lock_while_writing
= FALSE
;
214 static void io_ops_init (void)
216 /* _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
217 /* WAPI_HANDLE_CAP_WAIT); */
218 /* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
219 /* WAPI_HANDLE_CAP_WAIT); */
221 if (g_getenv ("MONO_STRICT_IO_EMULATION") != NULL
) {
222 lock_while_writing
= TRUE
;
226 /* Some utility functions.
229 static guint32
_wapi_stat_to_file_attributes (const gchar
*pathname
,
236 /* FIXME: this could definitely be better, but there seems to
237 * be no pattern to the attributes that are set
240 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
241 if (S_ISSOCK (buf
->st_mode
))
242 buf
->st_mode
&= ~S_IFSOCK
; /* don't consider socket protection */
244 filename
= _wapi_basename (pathname
);
246 if (S_ISDIR (buf
->st_mode
)) {
247 attrs
= FILE_ATTRIBUTE_DIRECTORY
;
248 if (!(buf
->st_mode
& S_IWUSR
)) {
249 attrs
|= FILE_ATTRIBUTE_READONLY
;
251 if (filename
[0] == '.') {
252 attrs
|= FILE_ATTRIBUTE_HIDDEN
;
255 if (!(buf
->st_mode
& S_IWUSR
)) {
256 attrs
= FILE_ATTRIBUTE_READONLY
;
258 if (filename
[0] == '.') {
259 attrs
|= FILE_ATTRIBUTE_HIDDEN
;
261 } else if (filename
[0] == '.') {
262 attrs
= FILE_ATTRIBUTE_HIDDEN
;
264 attrs
= FILE_ATTRIBUTE_NORMAL
;
269 if (S_ISLNK (lbuf
->st_mode
)) {
270 attrs
|= FILE_ATTRIBUTE_REPARSE_POINT
;
280 _wapi_set_last_error_from_errno (void)
282 SetLastError (_wapi_get_win32_file_error (errno
));
285 static void _wapi_set_last_path_error_from_errno (const gchar
*dir
,
288 if (errno
== ENOENT
) {
289 /* Check the path - if it's a missing directory then
290 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
296 dirname
= _wapi_dirname (path
);
298 dirname
= g_strdup (dir
);
301 if (_wapi_access (dirname
, F_OK
) == 0) {
302 SetLastError (ERROR_FILE_NOT_FOUND
);
304 SetLastError (ERROR_PATH_NOT_FOUND
);
309 _wapi_set_last_error_from_errno ();
315 static void file_close (gpointer handle
, gpointer data
)
317 struct _WapiHandle_file
*file_handle
= (struct _WapiHandle_file
*)data
;
320 g_message("%s: closing file handle %p [%s]", __func__
, handle
,
321 file_handle
->filename
);
324 if (file_handle
->attrs
& FILE_FLAG_DELETE_ON_CLOSE
)
325 _wapi_unlink (file_handle
->filename
);
327 g_free (file_handle
->filename
);
329 _wapi_handle_share_release (file_handle
->share_info
);
331 close (GPOINTER_TO_UINT(handle
));
334 static WapiFileType
file_getfiletype(void)
336 return(FILE_TYPE_DISK
);
339 static gboolean
file_read(gpointer handle
, gpointer buffer
,
340 guint32 numbytes
, guint32
*bytesread
,
341 WapiOverlapped
*overlapped
)
343 struct _WapiHandle_file
*file_handle
;
345 int fd
= GPOINTER_TO_UINT(handle
);
348 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
349 (gpointer
*)&file_handle
);
351 g_warning ("%s: error looking up file handle %p", __func__
,
353 SetLastError (ERROR_INVALID_HANDLE
);
357 if(bytesread
!=NULL
) {
361 if(!(file_handle
->fileaccess
& GENERIC_READ
) &&
362 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
364 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
365 __func__
, handle
, file_handle
->fileaccess
);
368 SetLastError (ERROR_ACCESS_DENIED
);
373 ret
= read (fd
, buffer
, numbytes
);
374 } while (ret
== -1 && errno
== EINTR
&&
375 !_wapi_thread_cur_apc_pending());
381 g_message("%s: read of handle %p error: %s", __func__
,
382 handle
, strerror(err
));
384 SetLastError (_wapi_get_win32_file_error (err
));
388 if (bytesread
!= NULL
) {
395 static gboolean
file_write(gpointer handle
, gconstpointer buffer
,
396 guint32 numbytes
, guint32
*byteswritten
,
397 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
399 struct _WapiHandle_file
*file_handle
;
402 off_t current_pos
= 0;
403 int fd
= GPOINTER_TO_UINT(handle
);
405 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
406 (gpointer
*)&file_handle
);
408 g_warning ("%s: error looking up file handle %p", __func__
,
410 SetLastError (ERROR_INVALID_HANDLE
);
414 if(byteswritten
!=NULL
) {
418 if(!(file_handle
->fileaccess
& GENERIC_WRITE
) &&
419 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
421 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
424 SetLastError (ERROR_ACCESS_DENIED
);
428 if (lock_while_writing
) {
429 /* Need to lock the region we're about to write to,
430 * because we only do advisory locking on POSIX
433 current_pos
= lseek (fd
, (off_t
)0, SEEK_CUR
);
434 if (current_pos
== -1) {
436 g_message ("%s: handle %p lseek failed: %s", __func__
,
437 handle
, strerror (errno
));
439 _wapi_set_last_error_from_errno ();
443 if (_wapi_lock_file_region (fd
, current_pos
,
444 numbytes
) == FALSE
) {
445 /* The error has already been set */
451 ret
= write (fd
, buffer
, numbytes
);
452 } while (ret
== -1 && errno
== EINTR
&&
453 !_wapi_thread_cur_apc_pending());
455 if (lock_while_writing
) {
456 _wapi_unlock_file_region (fd
, current_pos
, numbytes
);
460 if (errno
== EINTR
) {
463 _wapi_set_last_error_from_errno ();
466 g_message("%s: write of handle %p error: %s",
467 __func__
, handle
, strerror(errno
));
473 if (byteswritten
!= NULL
) {
479 static gboolean
file_flush(gpointer handle
)
481 struct _WapiHandle_file
*file_handle
;
484 int fd
= GPOINTER_TO_UINT(handle
);
486 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
487 (gpointer
*)&file_handle
);
489 g_warning ("%s: error looking up file handle %p", __func__
,
491 SetLastError (ERROR_INVALID_HANDLE
);
495 if(!(file_handle
->fileaccess
& GENERIC_WRITE
) &&
496 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
498 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
501 SetLastError (ERROR_ACCESS_DENIED
);
508 g_message("%s: fsync of handle %p error: %s", __func__
, handle
,
512 _wapi_set_last_error_from_errno ();
519 static guint32
file_seek(gpointer handle
, gint32 movedistance
,
520 gint32
*highmovedistance
, WapiSeekMethod method
)
522 struct _WapiHandle_file
*file_handle
;
524 off_t offset
, newpos
;
527 int fd
= GPOINTER_TO_UINT(handle
);
529 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
530 (gpointer
*)&file_handle
);
532 g_warning ("%s: error looking up file handle %p", __func__
,
534 SetLastError (ERROR_INVALID_HANDLE
);
535 return(INVALID_SET_FILE_POINTER
);
538 if(!(file_handle
->fileaccess
& GENERIC_READ
) &&
539 !(file_handle
->fileaccess
& GENERIC_WRITE
) &&
540 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
542 g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
545 SetLastError (ERROR_ACCESS_DENIED
);
546 return(INVALID_SET_FILE_POINTER
);
561 g_message("%s: invalid seek type %d", __func__
, method
);
564 SetLastError (ERROR_INVALID_PARAMETER
);
565 return(INVALID_SET_FILE_POINTER
);
568 #ifdef HAVE_LARGE_FILE_SUPPORT
569 if(highmovedistance
==NULL
) {
572 g_message("%s: setting offset to %lld (low %d)", __func__
,
573 offset
, movedistance
);
576 offset
=((gint64
) *highmovedistance
<< 32) | (guint32
)movedistance
;
579 g_message("%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__
, offset
, offset
, *highmovedistance
, *highmovedistance
, movedistance
, movedistance
);
587 #ifdef HAVE_LARGE_FILE_SUPPORT
588 g_message("%s: moving handle %p by %lld bytes from %d", __func__
,
589 handle
, offset
, whence
);
591 g_message("%s: moving handle %p fd %d by %ld bytes from %d", __func__
,
592 handle
, offset
, whence
);
596 newpos
=lseek(fd
, offset
, whence
);
599 g_message("%s: lseek on handle %p returned error %s",
600 __func__
, handle
, strerror(errno
));
603 _wapi_set_last_error_from_errno ();
604 return(INVALID_SET_FILE_POINTER
);
608 #ifdef HAVE_LARGE_FILE_SUPPORT
609 g_message("%s: lseek returns %lld", __func__
, newpos
);
611 g_message ("%s: lseek returns %ld", __func__
, newpos
);
615 #ifdef HAVE_LARGE_FILE_SUPPORT
616 ret
=newpos
& 0xFFFFFFFF;
617 if(highmovedistance
!=NULL
) {
618 *highmovedistance
=newpos
>>32;
622 if(highmovedistance
!=NULL
) {
623 /* Accurate, but potentially dodgy :-) */
629 g_message ("%s: move of handle %p returning %d/%d", __func__
,
630 handle
, ret
, highmovedistance
==NULL
?0:*highmovedistance
);
636 static gboolean
file_setendoffile(gpointer handle
)
638 struct _WapiHandle_file
*file_handle
;
643 int fd
= GPOINTER_TO_UINT(handle
);
645 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
646 (gpointer
*)&file_handle
);
648 g_warning ("%s: error looking up file handle %p", __func__
,
650 SetLastError (ERROR_INVALID_HANDLE
);
654 if(!(file_handle
->fileaccess
& GENERIC_WRITE
) &&
655 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
657 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
660 SetLastError (ERROR_ACCESS_DENIED
);
664 /* Find the current file position, and the file length. If
665 * the file position is greater than the length, write to
666 * extend the file with a hole. If the file position is less
667 * than the length, truncate the file.
670 ret
=fstat(fd
, &statbuf
);
673 g_message ("%s: handle %p fstat failed: %s", __func__
,
674 handle
, strerror(errno
));
677 _wapi_set_last_error_from_errno ();
680 size
=statbuf
.st_size
;
682 pos
=lseek(fd
, (off_t
)0, SEEK_CUR
);
685 g_message("%s: handle %p lseek failed: %s", __func__
,
686 handle
, strerror(errno
));
689 _wapi_set_last_error_from_errno ();
693 #ifdef FTRUNCATE_DOESNT_EXTEND
694 /* I haven't bothered to write the configure.in stuff for this
695 * because I don't know if any platform needs it. I'm leaving
696 * this code just in case though
699 /* Extend the file. Use write() here, because some
700 * manuals say that ftruncate() behaviour is undefined
701 * when the file needs extending. The POSIX spec says
702 * that on XSI-conformant systems it extends, so if
703 * every system we care about conforms, then we can
707 ret
= write (fd
, "", 1);
708 } while (ret
== -1 && errno
== EINTR
&&
709 !_wapi_thread_cur_apc_pending());
713 g_message("%s: handle %p extend write failed: %s", __func__
, handle
, strerror(errno
));
716 _wapi_set_last_error_from_errno ();
720 /* And put the file position back after the write */
721 ret
= lseek (fd
, pos
, SEEK_SET
);
724 g_message ("%s: handle %p second lseek failed: %s",
725 __func__
, handle
, strerror(errno
));
728 _wapi_set_last_error_from_errno ();
734 /* always truncate, because the extend write() adds an extra
735 * byte to the end of the file
738 ret
=ftruncate(fd
, pos
);
740 while (ret
==-1 && errno
==EINTR
&& !_wapi_thread_cur_apc_pending());
743 g_message("%s: handle %p ftruncate failed: %s", __func__
,
744 handle
, strerror(errno
));
747 _wapi_set_last_error_from_errno ();
754 static guint32
file_getfilesize(gpointer handle
, guint32
*highsize
)
756 struct _WapiHandle_file
*file_handle
;
761 int fd
= GPOINTER_TO_UINT(handle
);
763 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
764 (gpointer
*)&file_handle
);
766 g_warning ("%s: error looking up file handle %p", __func__
,
768 SetLastError (ERROR_INVALID_HANDLE
);
769 return(INVALID_FILE_SIZE
);
772 if(!(file_handle
->fileaccess
& GENERIC_READ
) &&
773 !(file_handle
->fileaccess
& GENERIC_WRITE
) &&
774 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
776 g_message("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
779 SetLastError (ERROR_ACCESS_DENIED
);
780 return(INVALID_FILE_SIZE
);
783 /* If the file has a size with the low bits 0xFFFFFFFF the
784 * caller can't tell if this is an error, so clear the error
787 SetLastError (ERROR_SUCCESS
);
789 ret
= fstat(fd
, &statbuf
);
792 g_message ("%s: handle %p fstat failed: %s", __func__
,
793 handle
, strerror(errno
));
796 _wapi_set_last_error_from_errno ();
797 return(INVALID_FILE_SIZE
);
800 #ifdef HAVE_LARGE_FILE_SUPPORT
801 size
= statbuf
.st_size
& 0xFFFFFFFF;
802 if (highsize
!= NULL
) {
803 *highsize
= statbuf
.st_size
>>32;
806 if (highsize
!= NULL
) {
807 /* Accurate, but potentially dodgy :-) */
810 size
= statbuf
.st_size
;
814 g_message ("%s: Returning size %d/%d", __func__
, size
, *highsize
);
820 static gboolean
file_getfiletime(gpointer handle
, WapiFileTime
*create_time
,
821 WapiFileTime
*last_access
,
822 WapiFileTime
*last_write
)
824 struct _WapiHandle_file
*file_handle
;
827 guint64 create_ticks
, access_ticks
, write_ticks
;
829 int fd
= GPOINTER_TO_UINT(handle
);
831 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
832 (gpointer
*)&file_handle
);
834 g_warning ("%s: error looking up file handle %p", __func__
,
836 SetLastError (ERROR_INVALID_HANDLE
);
840 if(!(file_handle
->fileaccess
& GENERIC_READ
) &&
841 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
843 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
844 __func__
, handle
, file_handle
->fileaccess
);
847 SetLastError (ERROR_ACCESS_DENIED
);
851 ret
=fstat(fd
, &statbuf
);
854 g_message("%s: handle %p fstat failed: %s", __func__
, handle
,
858 _wapi_set_last_error_from_errno ();
863 g_message("%s: atime: %ld ctime: %ld mtime: %ld", __func__
,
864 statbuf
.st_atime
, statbuf
.st_ctime
,
868 /* Try and guess a meaningful create time by using the older
871 /* The magic constant comes from msdn documentation
872 * "Converting a time_t Value to a File Time"
874 if(statbuf
.st_atime
< statbuf
.st_ctime
) {
875 create_ticks
=((guint64
)statbuf
.st_atime
*10000000)
876 + 116444736000000000ULL;
878 create_ticks
=((guint64
)statbuf
.st_ctime
*10000000)
879 + 116444736000000000ULL;
882 access_ticks
=((guint64
)statbuf
.st_atime
*10000000)+116444736000000000ULL;
883 write_ticks
=((guint64
)statbuf
.st_mtime
*10000000)+116444736000000000ULL;
886 g_message("%s: aticks: %llu cticks: %llu wticks: %llu", __func__
,
887 access_ticks
, create_ticks
, write_ticks
);
890 if(create_time
!=NULL
) {
891 create_time
->dwLowDateTime
= create_ticks
& 0xFFFFFFFF;
892 create_time
->dwHighDateTime
= create_ticks
>> 32;
895 if(last_access
!=NULL
) {
896 last_access
->dwLowDateTime
= access_ticks
& 0xFFFFFFFF;
897 last_access
->dwHighDateTime
= access_ticks
>> 32;
900 if(last_write
!=NULL
) {
901 last_write
->dwLowDateTime
= write_ticks
& 0xFFFFFFFF;
902 last_write
->dwHighDateTime
= write_ticks
>> 32;
908 static gboolean
file_setfiletime(gpointer handle
,
909 const WapiFileTime
*create_time G_GNUC_UNUSED
,
910 const WapiFileTime
*last_access
,
911 const WapiFileTime
*last_write
)
913 struct _WapiHandle_file
*file_handle
;
915 struct utimbuf utbuf
;
917 guint64 access_ticks
, write_ticks
;
919 int fd
= GPOINTER_TO_UINT(handle
);
921 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FILE
,
922 (gpointer
*)&file_handle
);
924 g_warning ("%s: error looking up file handle %p", __func__
,
926 SetLastError (ERROR_INVALID_HANDLE
);
930 if(!(file_handle
->fileaccess
& GENERIC_WRITE
) &&
931 !(file_handle
->fileaccess
& GENERIC_ALL
)) {
933 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, file_handle
->fileaccess
);
936 SetLastError (ERROR_ACCESS_DENIED
);
940 if(file_handle
->filename
== NULL
) {
942 g_message("%s: handle %p unknown filename", __func__
, handle
);
945 SetLastError (ERROR_INVALID_HANDLE
);
949 /* Get the current times, so we can put the same times back in
950 * the event that one of the FileTime structs is NULL
952 ret
=fstat (fd
, &statbuf
);
955 g_message("%s: handle %p fstat failed: %s", __func__
, handle
,
959 SetLastError (ERROR_INVALID_PARAMETER
);
963 if(last_access
!=NULL
) {
964 access_ticks
=((guint64
)last_access
->dwHighDateTime
<< 32) +
965 last_access
->dwLowDateTime
;
966 /* This is (time_t)0. We can actually go to INT_MIN,
967 * but this will do for now.
969 if (access_ticks
< 116444736000000000ULL) {
971 g_message ("%s: attempt to set access time too early",
974 SetLastError (ERROR_INVALID_PARAMETER
);
978 utbuf
.actime
=(access_ticks
- 116444736000000000ULL) / 10000000;
980 utbuf
.actime
=statbuf
.st_atime
;
983 if(last_write
!=NULL
) {
984 write_ticks
=((guint64
)last_write
->dwHighDateTime
<< 32) +
985 last_write
->dwLowDateTime
;
986 /* This is (time_t)0. We can actually go to INT_MIN,
987 * but this will do for now.
989 if (write_ticks
< 116444736000000000ULL) {
991 g_message ("%s: attempt to set write time too early",
994 SetLastError (ERROR_INVALID_PARAMETER
);
998 utbuf
.modtime
=(write_ticks
- 116444736000000000ULL) / 10000000;
1000 utbuf
.modtime
=statbuf
.st_mtime
;
1004 g_message ("%s: setting handle %p access %ld write %ld", __func__
,
1005 handle
, utbuf
.actime
, utbuf
.modtime
);
1008 ret
= _wapi_utime (file_handle
->filename
, &utbuf
);
1011 g_message ("%s: handle %p [%s] utime failed: %s", __func__
,
1012 handle
, file_handle
->filename
, strerror(errno
));
1015 SetLastError (ERROR_INVALID_PARAMETER
);
1022 static void console_close (gpointer handle
, gpointer data
)
1024 struct _WapiHandle_file
*console_handle
= (struct _WapiHandle_file
*)data
;
1027 g_message("%s: closing console handle %p", __func__
, handle
);
1030 g_free (console_handle
->filename
);
1032 close (GPOINTER_TO_UINT(handle
));
1035 static WapiFileType
console_getfiletype(void)
1037 return(FILE_TYPE_CHAR
);
1040 static gboolean
console_read(gpointer handle
, gpointer buffer
,
1041 guint32 numbytes
, guint32
*bytesread
,
1042 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
1044 struct _WapiHandle_file
*console_handle
;
1047 int fd
= GPOINTER_TO_UINT(handle
);
1049 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_CONSOLE
,
1050 (gpointer
*)&console_handle
);
1052 g_warning ("%s: error looking up console handle %p", __func__
,
1054 SetLastError (ERROR_INVALID_HANDLE
);
1058 if(bytesread
!=NULL
) {
1062 if(!(console_handle
->fileaccess
& GENERIC_READ
) &&
1063 !(console_handle
->fileaccess
& GENERIC_ALL
)) {
1065 g_message ("%s: handle %p doesn't have GENERIC_READ access: %u",
1066 __func__
, handle
, console_handle
->fileaccess
);
1069 SetLastError (ERROR_ACCESS_DENIED
);
1074 ret
=read(fd
, buffer
, numbytes
);
1075 } while (ret
==-1 && errno
==EINTR
&& !_wapi_thread_cur_apc_pending());
1079 g_message("%s: read of handle %p error: %s", __func__
, handle
,
1083 _wapi_set_last_error_from_errno ();
1087 if(bytesread
!=NULL
) {
1094 static gboolean
console_write(gpointer handle
, gconstpointer buffer
,
1095 guint32 numbytes
, guint32
*byteswritten
,
1096 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
1098 struct _WapiHandle_file
*console_handle
;
1101 int fd
= GPOINTER_TO_UINT(handle
);
1103 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_CONSOLE
,
1104 (gpointer
*)&console_handle
);
1106 g_warning ("%s: error looking up console handle %p", __func__
,
1108 SetLastError (ERROR_INVALID_HANDLE
);
1112 if(byteswritten
!=NULL
) {
1116 if(!(console_handle
->fileaccess
& GENERIC_WRITE
) &&
1117 !(console_handle
->fileaccess
& GENERIC_ALL
)) {
1119 g_message("%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 ();
1138 g_message ("%s: write of handle %p error: %s",
1139 __func__
, handle
, strerror(errno
));
1145 if(byteswritten
!=NULL
) {
1152 static void pipe_close (gpointer handle
, gpointer data G_GNUC_UNUSED
)
1155 g_message("%s: closing pipe handle %p", __func__
, handle
);
1158 /* No filename with pipe handles */
1160 close(GPOINTER_TO_UINT(handle
));
1163 static WapiFileType
pipe_getfiletype(void)
1165 return(FILE_TYPE_PIPE
);
1168 static gboolean
pipe_read (gpointer handle
, gpointer buffer
,
1169 guint32 numbytes
, guint32
*bytesread
,
1170 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
1172 struct _WapiHandle_file
*pipe_handle
;
1175 int fd
= GPOINTER_TO_UINT(handle
);
1177 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_PIPE
,
1178 (gpointer
*)&pipe_handle
);
1180 g_warning ("%s: error looking up pipe handle %p", __func__
,
1182 SetLastError (ERROR_INVALID_HANDLE
);
1186 if(bytesread
!=NULL
) {
1190 if(!(pipe_handle
->fileaccess
& GENERIC_READ
) &&
1191 !(pipe_handle
->fileaccess
& GENERIC_ALL
)) {
1193 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
1194 __func__
, handle
, pipe_handle
->fileaccess
);
1197 SetLastError (ERROR_ACCESS_DENIED
);
1202 g_message ("%s: reading up to %d bytes from pipe %p", __func__
,
1207 ret
=read(fd
, buffer
, numbytes
);
1208 } while (ret
==-1 && errno
==EINTR
&& !_wapi_thread_cur_apc_pending());
1211 if (errno
== EINTR
) {
1214 _wapi_set_last_error_from_errno ();
1217 g_message("%s: read of handle %p error: %s", __func__
,
1218 handle
, strerror(errno
));
1226 g_message ("%s: read %d bytes from pipe", __func__
, ret
);
1229 if(bytesread
!=NULL
) {
1236 static gboolean
pipe_write(gpointer handle
, gconstpointer buffer
,
1237 guint32 numbytes
, guint32
*byteswritten
,
1238 WapiOverlapped
*overlapped G_GNUC_UNUSED
)
1240 struct _WapiHandle_file
*pipe_handle
;
1243 int fd
= GPOINTER_TO_UINT(handle
);
1245 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_PIPE
,
1246 (gpointer
*)&pipe_handle
);
1248 g_warning ("%s: error looking up pipe handle %p", __func__
,
1250 SetLastError (ERROR_INVALID_HANDLE
);
1254 if(byteswritten
!=NULL
) {
1258 if(!(pipe_handle
->fileaccess
& GENERIC_WRITE
) &&
1259 !(pipe_handle
->fileaccess
& GENERIC_ALL
)) {
1261 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__
, handle
, pipe_handle
->fileaccess
);
1264 SetLastError (ERROR_ACCESS_DENIED
);
1269 g_message ("%s: writing up to %d bytes to pipe %p", __func__
, numbytes
,
1274 ret
= write (fd
, buffer
, numbytes
);
1275 } while (ret
== -1 && errno
== EINTR
&&
1276 !_wapi_thread_cur_apc_pending());
1279 if (errno
== EINTR
) {
1282 _wapi_set_last_error_from_errno ();
1285 g_message("%s: write of handle %p error: %s", __func__
,
1286 handle
, strerror(errno
));
1292 if(byteswritten
!=NULL
) {
1299 static int convert_flags(guint32 fileaccess
, guint32 createmode
)
1303 switch(fileaccess
) {
1310 case GENERIC_READ
|GENERIC_WRITE
:
1315 g_message("%s: Unknown access type 0x%x", __func__
,
1321 switch(createmode
) {
1323 flags
|=O_CREAT
|O_EXCL
;
1326 flags
|=O_CREAT
|O_TRUNC
;
1333 case TRUNCATE_EXISTING
:
1338 g_message("%s: Unknown create mode 0x%x", __func__
,
1348 static mode_t
convert_perms(guint32 sharemode
)
1352 if(sharemode
&FILE_SHARE_READ
) {
1355 if(sharemode
&FILE_SHARE_WRITE
) {
1363 static gboolean
share_allows_open (struct stat
*statbuf
, guint32 sharemode
,
1365 struct _WapiFileShare
**share_info
)
1367 gboolean file_already_shared
;
1368 guint32 file_existing_share
, file_existing_access
;
1370 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
);
1372 if (file_already_shared
) {
1373 /* The reference to this share info was incremented
1374 * when we looked it up, so be careful to put it back
1375 * if we conclude we can't use this file.
1377 if (file_existing_share
== 0) {
1378 /* Quick and easy, no possibility to share */
1380 g_message ("%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__
, fileaccess
);
1383 _wapi_handle_share_release (*share_info
);
1388 if (((file_existing_share
== FILE_SHARE_READ
) &&
1389 (fileaccess
!= GENERIC_READ
)) ||
1390 ((file_existing_share
== FILE_SHARE_WRITE
) &&
1391 (fileaccess
!= GENERIC_WRITE
))) {
1392 /* New access mode doesn't match up */
1394 g_message ("%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__
, fileaccess
, file_existing_share
);
1397 _wapi_handle_share_release (*share_info
);
1402 if (((file_existing_access
& GENERIC_READ
) &&
1403 !(sharemode
& FILE_SHARE_READ
)) ||
1404 ((file_existing_access
& GENERIC_WRITE
) &&
1405 !(sharemode
& FILE_SHARE_WRITE
))) {
1406 /* New share mode doesn't match up */
1408 g_message ("%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__
, sharemode
, file_existing_access
);
1411 _wapi_handle_share_release (*share_info
);
1417 g_message ("%s: New file!", __func__
);
1424 static gboolean
share_check (struct stat
*statbuf
, guint32 sharemode
,
1426 struct _WapiFileShare
**share_info
, int fd
)
1428 if (share_allows_open (statbuf
, sharemode
, fileaccess
,
1429 share_info
) == TRUE
) {
1433 /* Got a share violation. Double check that the file is still
1434 * open by someone, in case a process crashed while still
1435 * holding a file handle. This will also cope with someone
1436 * using Mono.Posix to close the file. This is cheaper and
1437 * less intrusive to other processes than initiating a handle
1441 _wapi_handle_check_share (*share_info
, fd
);
1442 if (share_allows_open (statbuf
, sharemode
, fileaccess
,
1443 share_info
) == TRUE
) {
1447 /* Still violating. It's possible that a process crashed
1448 * while still holding a file handle, and that a non-mono
1449 * process has the file open. (For example, C-c mcs while
1450 * editing a source file.) As a last resort, run a handle
1451 * collection, which will remove stale share entries.
1453 _wapi_handle_collect ();
1455 return(share_allows_open (statbuf
, sharemode
, fileaccess
, share_info
));
1460 * @name: a pointer to a NULL-terminated unicode string, that names
1461 * the file or other object to create.
1462 * @fileaccess: specifies the file access mode
1463 * @sharemode: whether the file should be shared. This parameter is
1464 * currently ignored.
1465 * @security: Ignored for now.
1466 * @createmode: specifies whether to create a new file, whether to
1467 * overwrite an existing file, whether to truncate the file, etc.
1468 * @attrs: specifies file attributes and flags. On win32 attributes
1469 * are characteristics of the file, not the handle, and are ignored
1470 * when an existing file is opened. Flags give the library hints on
1471 * how to process a file to optimise performance.
1472 * @template: the handle of an open %GENERIC_READ file that specifies
1473 * attributes to apply to a newly created file, ignoring @attrs.
1474 * Normally this parameter is NULL. This parameter is ignored when an
1475 * existing file is opened.
1477 * Creates a new file handle. This only applies to normal files:
1478 * pipes are handled by CreatePipe(), and console handles are created
1479 * with GetStdHandle().
1481 * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1483 gpointer
CreateFile(const gunichar2
*name
, guint32 fileaccess
,
1484 guint32 sharemode
, WapiSecurityAttributes
*security
,
1485 guint32 createmode
, guint32 attrs
,
1486 gpointer
template G_GNUC_UNUSED
)
1488 struct _WapiHandle_file file_handle
= {0};
1490 int flags
=convert_flags(fileaccess
, createmode
);
1491 /*mode_t perms=convert_perms(sharemode);*/
1492 /* we don't use sharemode, because that relates to sharing of
1493 * the file when the file is open and is already handled by
1494 * other code, perms instead are the on-disk permissions and
1495 * this is a sane default.
1501 struct stat statbuf
;
1503 mono_once (&io_ops_once
, io_ops_init
);
1505 if (attrs
& FILE_ATTRIBUTE_TEMPORARY
)
1508 if (attrs
& FILE_ATTRIBUTE_ENCRYPTED
){
1509 SetLastError (ERROR_ENCRYPTION_FAILED
);
1510 return INVALID_HANDLE_VALUE
;
1515 g_message ("%s: name is NULL", __func__
);
1518 SetLastError (ERROR_INVALID_NAME
);
1519 return(INVALID_HANDLE_VALUE
);
1522 filename
= mono_unicode_to_external (name
);
1523 if (filename
== NULL
) {
1525 g_message("%s: unicode conversion returned NULL", __func__
);
1528 SetLastError (ERROR_INVALID_NAME
);
1529 return(INVALID_HANDLE_VALUE
);
1533 g_message ("%s: Opening %s with share 0x%x and access 0x%x", __func__
,
1534 filename
, sharemode
, fileaccess
);
1537 fd
= _wapi_open (filename
, flags
, perms
);
1539 /* If we were trying to open a directory with write permissions
1540 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1541 * EISDIR. However, this is a bit bogus because calls to
1542 * manipulate the directory (e.g. SetFileTime) will still work on
1543 * the directory because they use other API calls
1544 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1545 * to open the directory again without write permission.
1547 if (fd
== -1 && errno
== EISDIR
)
1549 /* Try again but don't try to make it writable */
1550 fd
= _wapi_open (filename
, flags
& ~(O_RDWR
|O_WRONLY
), perms
);
1555 g_message("%s: Error opening file %s: %s", __func__
, filename
,
1558 _wapi_set_last_path_error_from_errno (NULL
, filename
);
1561 return(INVALID_HANDLE_VALUE
);
1564 if (fd
>= _wapi_fd_reserve
) {
1566 g_message ("%s: File descriptor is too big", __func__
);
1569 SetLastError (ERROR_TOO_MANY_OPEN_FILES
);
1574 return(INVALID_HANDLE_VALUE
);
1577 ret
= fstat (fd
, &statbuf
);
1580 g_message ("%s: fstat error of file %s: %s", __func__
,
1581 filename
, strerror (errno
));
1583 _wapi_set_last_error_from_errno ();
1587 return(INVALID_HANDLE_VALUE
);
1590 if (share_check (&statbuf
, sharemode
, fileaccess
,
1591 &file_handle
.share_info
, fd
) == FALSE
) {
1592 SetLastError (ERROR_SHARING_VIOLATION
);
1596 return (INVALID_HANDLE_VALUE
);
1598 if (file_handle
.share_info
== NULL
) {
1599 /* No space, so no more files can be opened */
1601 g_message ("%s: No space in the share table", __func__
);
1604 SetLastError (ERROR_TOO_MANY_OPEN_FILES
);
1608 return(INVALID_HANDLE_VALUE
);
1611 file_handle
.filename
= filename
;
1613 if(security
!=NULL
) {
1614 //file_handle->security_attributes=_wapi_handle_scratch_store (
1615 //security, sizeof(WapiSecurityAttributes));
1618 file_handle
.fileaccess
=fileaccess
;
1619 file_handle
.sharemode
=sharemode
;
1620 file_handle
.attrs
=attrs
;
1622 #ifdef HAVE_POSIX_FADVISE
1623 if (attrs
& FILE_FLAG_SEQUENTIAL_SCAN
)
1624 posix_fadvise (fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
1625 if (attrs
& FILE_FLAG_RANDOM_ACCESS
)
1626 posix_fadvise (fd
, 0, 0, POSIX_FADV_RANDOM
);
1630 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
1632 if (S_ISFIFO (statbuf
.st_mode
)) {
1633 handle_type
= WAPI_HANDLE_PIPE
;
1634 } else if (S_ISCHR (statbuf
.st_mode
)) {
1635 handle_type
= WAPI_HANDLE_CONSOLE
;
1637 handle_type
= WAPI_HANDLE_FILE
;
1640 handle
= _wapi_handle_new_fd (handle_type
, fd
, &file_handle
);
1641 if (handle
== _WAPI_HANDLE_INVALID
) {
1642 g_warning ("%s: error creating file handle", __func__
);
1646 SetLastError (ERROR_GEN_FAILURE
);
1647 return(INVALID_HANDLE_VALUE
);
1651 g_message("%s: returning handle %p", __func__
, handle
);
1659 * @name: a pointer to a NULL-terminated unicode string, that names
1660 * the file to be deleted.
1662 * Deletes file @name.
1664 * Return value: %TRUE on success, %FALSE otherwise.
1666 gboolean
DeleteFile(const gunichar2
*name
)
1670 gboolean ret
= FALSE
;
1673 struct stat statbuf
;
1674 struct _WapiFileShare
*shareinfo
;
1679 g_message("%s: name is NULL", __func__
);
1682 SetLastError (ERROR_INVALID_NAME
);
1686 filename
=mono_unicode_to_external(name
);
1687 if(filename
==NULL
) {
1689 g_message("%s: unicode conversion returned NULL", __func__
);
1692 SetLastError (ERROR_INVALID_NAME
);
1696 attrs
= GetFileAttributes (name
);
1697 if (attrs
== INVALID_FILE_ATTRIBUTES
) {
1699 g_message ("%s: file attributes error", __func__
);
1701 /* Error set by GetFileAttributes() */
1706 if (attrs
& FILE_ATTRIBUTE_READONLY
) {
1708 g_message ("%s: file %s is readonly", __func__
, filename
);
1710 SetLastError (ERROR_ACCESS_DENIED
);
1716 /* Check to make sure sharing allows us to open the file for
1717 * writing. See bug 323389.
1719 * Do the checks that don't need an open file descriptor, for
1720 * simplicity's sake. If we really have to do the full checks
1721 * then we can implement that later.
1723 if (_wapi_stat (filename
, &statbuf
) < 0) {
1724 _wapi_set_last_path_error_from_errno (NULL
, filename
);
1729 if (share_allows_open (&statbuf
, 0, GENERIC_WRITE
,
1730 &shareinfo
) == FALSE
) {
1731 SetLastError (ERROR_SHARING_VIOLATION
);
1735 _wapi_handle_share_release (shareinfo
);
1738 retval
= _wapi_unlink (filename
);
1741 _wapi_set_last_path_error_from_errno (NULL
, filename
);
1753 * @name: a pointer to a NULL-terminated unicode string, that names
1754 * the file to be moved.
1755 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1756 * new name for the file.
1758 * Renames file @name to @dest_name.
1759 * MoveFile sets ERROR_ALREADY_EXISTS if the destination exists, except
1760 * when it is the same file as the source. In that case it silently succeeds.
1762 * Return value: %TRUE on success, %FALSE otherwise.
1764 gboolean
MoveFile (const gunichar2
*name
, const gunichar2
*dest_name
)
1766 gchar
*utf8_name
, *utf8_dest_name
;
1767 int result
, errno_copy
;
1768 struct stat stat_src
, stat_dest
;
1769 gboolean ret
= FALSE
;
1770 struct _WapiFileShare
*shareinfo
;
1774 g_message("%s: name is NULL", __func__
);
1777 SetLastError (ERROR_INVALID_NAME
);
1781 utf8_name
= mono_unicode_to_external (name
);
1782 if (utf8_name
== NULL
) {
1784 g_message ("%s: unicode conversion returned NULL", __func__
);
1787 SetLastError (ERROR_INVALID_NAME
);
1791 if(dest_name
==NULL
) {
1793 g_message("%s: name is NULL", __func__
);
1797 SetLastError (ERROR_INVALID_NAME
);
1801 utf8_dest_name
= mono_unicode_to_external (dest_name
);
1802 if (utf8_dest_name
== NULL
) {
1804 g_message ("%s: unicode conversion returned NULL", __func__
);
1808 SetLastError (ERROR_INVALID_NAME
);
1813 * In C# land we check for the existence of src, but not for dest.
1814 * We check it here and return the failure if dest exists and is not
1815 * the same file as src.
1817 if (_wapi_stat (utf8_name
, &stat_src
) < 0) {
1818 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
1820 g_free (utf8_dest_name
);
1824 if (!_wapi_stat (utf8_dest_name
, &stat_dest
)) {
1825 if (stat_dest
.st_dev
!= stat_src
.st_dev
||
1826 stat_dest
.st_ino
!= stat_src
.st_ino
) {
1828 g_free (utf8_dest_name
);
1829 SetLastError (ERROR_ALREADY_EXISTS
);
1834 /* Check to make sure sharing allows us to open the file for
1835 * writing. See bug 377049.
1837 * Do the checks that don't need an open file descriptor, for
1838 * simplicity's sake. If we really have to do the full checks
1839 * then we can implement that later.
1841 if (share_allows_open (&stat_src
, 0, GENERIC_WRITE
,
1842 &shareinfo
) == FALSE
) {
1843 SetLastError (ERROR_SHARING_VIOLATION
);
1846 _wapi_handle_share_release (shareinfo
);
1848 result
= _wapi_rename (utf8_name
, utf8_dest_name
);
1852 switch(errno_copy
) {
1854 SetLastError (ERROR_ALREADY_EXISTS
);
1858 /* Ignore here, it is dealt with below */
1862 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
1867 g_free (utf8_dest_name
);
1869 if (result
!= 0 && errno_copy
== EXDEV
) {
1870 if (S_ISDIR (stat_src
.st_mode
)) {
1871 SetLastError (ERROR_NOT_SAME_DEVICE
);
1874 /* Try a copy to the new location, and delete the source */
1875 if (CopyFile (name
, dest_name
, TRUE
)==FALSE
) {
1876 /* CopyFile will set the error */
1880 return(DeleteFile (name
));
1891 write_file (int src_fd
, int dest_fd
, struct stat
*st_src
, gboolean report_errors
)
1895 int buf_size
= st_src
->st_blksize
;
1897 buf_size
= buf_size
< 8192 ? 8192 : (buf_size
> 65536 ? 65536 : buf_size
);
1898 buf
= (char *) malloc (buf_size
);
1901 remain
= read (src_fd
, buf
, buf_size
);
1903 if (errno
== EINTR
&& !_wapi_thread_cur_apc_pending ())
1907 _wapi_set_last_error_from_errno ();
1917 while (remain
> 0) {
1918 if ((n
= write (dest_fd
, wbuf
, remain
)) < 0) {
1919 if (errno
== EINTR
&& !_wapi_thread_cur_apc_pending ())
1923 _wapi_set_last_error_from_errno ();
1925 g_message ("%s: write failed.", __func__
);
1942 * @name: a pointer to a NULL-terminated unicode string, that names
1943 * the file to be copied.
1944 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1945 * new name for the file.
1946 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1948 * Copies file @name to @dest_name
1950 * Return value: %TRUE on success, %FALSE otherwise.
1952 gboolean
CopyFile (const gunichar2
*name
, const gunichar2
*dest_name
,
1953 gboolean fail_if_exists
)
1955 gchar
*utf8_src
, *utf8_dest
;
1956 int src_fd
, dest_fd
;
1957 struct stat st
, dest_st
;
1958 gboolean ret
= TRUE
;
1962 g_message("%s: name is NULL", __func__
);
1965 SetLastError (ERROR_INVALID_NAME
);
1969 utf8_src
= mono_unicode_to_external (name
);
1970 if (utf8_src
== NULL
) {
1972 g_message ("%s: unicode conversion of source returned NULL",
1976 SetLastError (ERROR_INVALID_PARAMETER
);
1980 if(dest_name
==NULL
) {
1982 g_message("%s: dest is NULL", __func__
);
1986 SetLastError (ERROR_INVALID_NAME
);
1990 utf8_dest
= mono_unicode_to_external (dest_name
);
1991 if (utf8_dest
== NULL
) {
1993 g_message ("%s: unicode conversion of dest returned NULL",
1997 SetLastError (ERROR_INVALID_PARAMETER
);
2004 src_fd
= _wapi_open (utf8_src
, O_RDONLY
, 0);
2006 _wapi_set_last_path_error_from_errno (NULL
, utf8_src
);
2014 if (fstat (src_fd
, &st
) < 0) {
2015 _wapi_set_last_error_from_errno ();
2024 /* Before trying to open/create the dest, we need to report a 'file busy'
2025 * error if src and dest are actually the same file. We do the check here to take
2026 * advantage of the IOMAP capability */
2027 if (!_wapi_stat (utf8_dest
, &dest_st
) && st
.st_dev
== dest_st
.st_dev
&&
2028 st
.st_ino
== dest_st
.st_ino
) {
2034 SetLastError (ERROR_SHARING_VIOLATION
);
2038 if (fail_if_exists
) {
2039 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_CREAT
| O_EXCL
, st
.st_mode
);
2041 /* FIXME: it kinda sucks that this code path potentially scans
2042 * the directory twice due to the weird SetLastError()
2044 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_TRUNC
, st
.st_mode
);
2046 /* The file does not exist, try creating it */
2047 dest_fd
= _wapi_open (utf8_dest
, O_WRONLY
| O_CREAT
| O_TRUNC
, st
.st_mode
);
2049 /* Apparently this error is set if we
2050 * overwrite the dest file
2052 SetLastError (ERROR_ALREADY_EXISTS
);
2056 _wapi_set_last_error_from_errno ();
2065 if (!write_file (src_fd
, dest_fd
, &st
, TRUE
))
2077 convert_arg_to_utf8 (const gunichar2
*arg
, const gchar
*arg_name
)
2083 g_message ("%s: %s is NULL", __func__
, arg_name
);
2085 SetLastError (ERROR_INVALID_NAME
);
2089 utf8_ret
= mono_unicode_to_external (arg
);
2090 if (utf8_ret
== NULL
) {
2092 g_message ("%s: unicode conversion of %s returned NULL",
2093 __func__
, arg_name
);
2095 SetLastError (ERROR_INVALID_PARAMETER
);
2103 ReplaceFile (const gunichar2
*replacedFileName
, const gunichar2
*replacementFileName
,
2104 const gunichar2
*backupFileName
, guint32 replaceFlags
,
2105 gpointer exclude
, gpointer reserved
)
2107 int result
, errno_copy
, backup_fd
= -1,replaced_fd
= -1;
2108 gchar
*utf8_replacedFileName
, *utf8_replacementFileName
= NULL
, *utf8_backupFileName
= NULL
;
2109 struct stat stBackup
;
2110 gboolean ret
= FALSE
;
2112 if (!(utf8_replacedFileName
= convert_arg_to_utf8 (replacedFileName
, "replacedFileName")))
2114 if (!(utf8_replacementFileName
= convert_arg_to_utf8 (replacementFileName
, "replacementFileName")))
2115 goto replace_cleanup
;
2116 if (backupFileName
!= NULL
) {
2117 if (!(utf8_backupFileName
= convert_arg_to_utf8 (backupFileName
, "backupFileName")))
2118 goto replace_cleanup
;
2121 if (utf8_backupFileName
) {
2122 // Open the backup file for read so we can restore the file if an error occurs.
2123 backup_fd
= _wapi_open (utf8_backupFileName
, O_RDONLY
, 0);
2124 result
= _wapi_rename (utf8_replacedFileName
, utf8_backupFileName
);
2127 goto replace_cleanup
;
2130 result
= _wapi_rename (utf8_replacementFileName
, utf8_replacedFileName
);
2133 _wapi_set_last_path_error_from_errno (NULL
, utf8_replacementFileName
);
2134 _wapi_rename (utf8_backupFileName
, utf8_replacedFileName
);
2135 if (backup_fd
!= -1 && !fstat (backup_fd
, &stBackup
)) {
2136 replaced_fd
= _wapi_open (utf8_backupFileName
, O_WRONLY
| O_CREAT
| O_TRUNC
,
2139 if (replaced_fd
== -1)
2140 goto replace_cleanup
;
2142 write_file (backup_fd
, replaced_fd
, &stBackup
, FALSE
);
2145 goto replace_cleanup
;
2151 g_free (utf8_replacedFileName
);
2152 g_free (utf8_replacementFileName
);
2153 g_free (utf8_backupFileName
);
2154 if (backup_fd
!= -1)
2156 if (replaced_fd
!= -1)
2157 close (replaced_fd
);
2163 * @stdhandle: specifies the file descriptor
2165 * Returns a handle for stdin, stdout, or stderr. Always returns the
2166 * same handle for the same @stdhandle.
2168 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2171 static mono_mutex_t stdhandle_mutex
= MONO_MUTEX_INITIALIZER
;
2173 gpointer
GetStdHandle(WapiStdHandle stdhandle
)
2175 struct _WapiHandle_file
*file_handle
;
2182 case STD_INPUT_HANDLE
:
2187 case STD_OUTPUT_HANDLE
:
2192 case STD_ERROR_HANDLE
:
2199 g_message("%s: unknown standard handle type", __func__
);
2202 SetLastError (ERROR_INVALID_PARAMETER
);
2203 return(INVALID_HANDLE_VALUE
);
2206 handle
= GINT_TO_POINTER (fd
);
2208 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup
,
2209 (void *)&stdhandle_mutex
);
2210 thr_ret
= mono_mutex_lock (&stdhandle_mutex
);
2211 g_assert (thr_ret
== 0);
2213 ok
= _wapi_lookup_handle (handle
, WAPI_HANDLE_CONSOLE
,
2214 (gpointer
*)&file_handle
);
2216 /* Need to create this console handle */
2217 handle
= _wapi_stdhandle_create (fd
, name
);
2219 if (handle
== INVALID_HANDLE_VALUE
) {
2220 SetLastError (ERROR_NO_MORE_FILES
);
2224 /* Add a reference to this handle */
2225 _wapi_handle_ref (handle
);
2229 thr_ret
= mono_mutex_unlock (&stdhandle_mutex
);
2230 g_assert (thr_ret
== 0);
2231 pthread_cleanup_pop (0);
2238 * @handle: The file handle to read from. The handle must have
2239 * %GENERIC_READ access.
2240 * @buffer: The buffer to store read data in
2241 * @numbytes: The maximum number of bytes to read
2242 * @bytesread: The actual number of bytes read is stored here. This
2243 * value can be zero if the handle is positioned at the end of the
2245 * @overlapped: points to a required %WapiOverlapped structure if
2246 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2249 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2250 * function reads up to @numbytes bytes from the file from the current
2251 * file position, and stores them in @buffer. If there are not enough
2252 * bytes left in the file, just the amount available will be read.
2253 * The actual number of bytes read is stored in @bytesread.
2255 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2256 * file position is ignored and the read position is taken from data
2257 * in the @overlapped structure.
2259 * Return value: %TRUE if the read succeeds (even if no bytes were
2260 * read due to an attempt to read past the end of the file), %FALSE on
2263 gboolean
ReadFile(gpointer handle
, gpointer buffer
, guint32 numbytes
,
2264 guint32
*bytesread
, WapiOverlapped
*overlapped
)
2266 WapiHandleType type
;
2268 type
= _wapi_handle_type (handle
);
2270 if(io_ops
[type
].readfile
==NULL
) {
2271 SetLastError (ERROR_INVALID_HANDLE
);
2275 return(io_ops
[type
].readfile (handle
, buffer
, numbytes
, bytesread
,
2281 * @handle: The file handle to write to. The handle must have
2282 * %GENERIC_WRITE access.
2283 * @buffer: The buffer to read data from.
2284 * @numbytes: The maximum number of bytes to write.
2285 * @byteswritten: The actual number of bytes written is stored here.
2286 * If the handle is positioned at the file end, the length of the file
2287 * is extended. This parameter may be %NULL.
2288 * @overlapped: points to a required %WapiOverlapped structure if
2289 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2292 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2293 * function writes up to @numbytes bytes from @buffer to the file at
2294 * the current file position. If @handle is positioned at the end of
2295 * the file, the file is extended. The actual number of bytes written
2296 * is stored in @byteswritten.
2298 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2299 * file position is ignored and the write position is taken from data
2300 * in the @overlapped structure.
2302 * Return value: %TRUE if the write succeeds, %FALSE on error.
2304 gboolean
WriteFile(gpointer handle
, gconstpointer buffer
, guint32 numbytes
,
2305 guint32
*byteswritten
, WapiOverlapped
*overlapped
)
2307 WapiHandleType type
;
2309 type
= _wapi_handle_type (handle
);
2311 if(io_ops
[type
].writefile
==NULL
) {
2312 SetLastError (ERROR_INVALID_HANDLE
);
2316 return(io_ops
[type
].writefile (handle
, buffer
, numbytes
, byteswritten
,
2322 * @handle: Handle to open file. The handle must have
2323 * %GENERIC_WRITE access.
2325 * Flushes buffers of the file and causes all unwritten data to
2328 * Return value: %TRUE on success, %FALSE otherwise.
2330 gboolean
FlushFileBuffers(gpointer handle
)
2332 WapiHandleType type
;
2334 type
= _wapi_handle_type (handle
);
2336 if(io_ops
[type
].flushfile
==NULL
) {
2337 SetLastError (ERROR_INVALID_HANDLE
);
2341 return(io_ops
[type
].flushfile (handle
));
2346 * @handle: The file handle to set. The handle must have
2347 * %GENERIC_WRITE access.
2349 * Moves the end-of-file position to the current position of the file
2350 * pointer. This function is used to truncate or extend a file.
2352 * Return value: %TRUE on success, %FALSE otherwise.
2354 gboolean
SetEndOfFile(gpointer handle
)
2356 WapiHandleType type
;
2358 type
= _wapi_handle_type (handle
);
2360 if (io_ops
[type
].setendoffile
== NULL
) {
2361 SetLastError (ERROR_INVALID_HANDLE
);
2365 return(io_ops
[type
].setendoffile (handle
));
2370 * @handle: The file handle to set. The handle must have
2371 * %GENERIC_READ or %GENERIC_WRITE access.
2372 * @movedistance: Low 32 bits of a signed value that specifies the
2373 * number of bytes to move the file pointer.
2374 * @highmovedistance: Pointer to the high 32 bits of a signed value
2375 * that specifies the number of bytes to move the file pointer, or
2377 * @method: The starting point for the file pointer move.
2379 * Sets the file pointer of an open file.
2381 * The distance to move the file pointer is calculated from
2382 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2383 * @movedistance is the 32-bit signed value; otherwise, @movedistance
2384 * is the low 32 bits and @highmovedistance a pointer to the high 32
2385 * bits of a 64 bit signed value. A positive distance moves the file
2386 * pointer forward from the position specified by @method; a negative
2387 * distance moves the file pointer backward.
2389 * If the library is compiled without large file support,
2390 * @highmovedistance is ignored and its value is set to zero on a
2391 * successful return.
2393 * Return value: On success, the low 32 bits of the new file pointer.
2394 * If @highmovedistance is not %NULL, the high 32 bits of the new file
2395 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
2397 guint32
SetFilePointer(gpointer handle
, gint32 movedistance
,
2398 gint32
*highmovedistance
, WapiSeekMethod method
)
2400 WapiHandleType type
;
2402 type
= _wapi_handle_type (handle
);
2404 if (io_ops
[type
].seek
== NULL
) {
2405 SetLastError (ERROR_INVALID_HANDLE
);
2406 return(INVALID_SET_FILE_POINTER
);
2409 return(io_ops
[type
].seek (handle
, movedistance
, highmovedistance
,
2415 * @handle: The file handle to test.
2417 * Finds the type of file @handle.
2419 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2420 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
2421 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2422 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2424 WapiFileType
GetFileType(gpointer handle
)
2426 WapiHandleType type
;
2428 if (!_WAPI_PRIVATE_HAVE_SLOT (handle
)) {
2429 SetLastError (ERROR_INVALID_HANDLE
);
2430 return(FILE_TYPE_UNKNOWN
);
2433 type
= _wapi_handle_type (handle
);
2435 if (io_ops
[type
].getfiletype
== NULL
) {
2436 SetLastError (ERROR_INVALID_HANDLE
);
2437 return(FILE_TYPE_UNKNOWN
);
2440 return(io_ops
[type
].getfiletype ());
2445 * @handle: The file handle to query. The handle must have
2446 * %GENERIC_READ or %GENERIC_WRITE access.
2447 * @highsize: If non-%NULL, the high 32 bits of the file size are
2450 * Retrieves the size of the file @handle.
2452 * If the library is compiled without large file support, @highsize
2453 * has its value set to zero on a successful return.
2455 * Return value: On success, the low 32 bits of the file size. If
2456 * @highsize is non-%NULL then the high 32 bits of the file size are
2457 * stored here. On failure %INVALID_FILE_SIZE is returned.
2459 guint32
GetFileSize(gpointer handle
, guint32
*highsize
)
2461 WapiHandleType type
;
2463 type
= _wapi_handle_type (handle
);
2465 if (io_ops
[type
].getfilesize
== NULL
) {
2466 SetLastError (ERROR_INVALID_HANDLE
);
2467 return(INVALID_FILE_SIZE
);
2470 return(io_ops
[type
].getfilesize (handle
, highsize
));
2475 * @handle: The file handle to query. The handle must have
2476 * %GENERIC_READ access.
2477 * @create_time: Points to a %WapiFileTime structure to receive the
2478 * number of ticks since the epoch that file was created. May be
2480 * @last_access: Points to a %WapiFileTime structure to receive the
2481 * number of ticks since the epoch when file was last accessed. May be
2483 * @last_write: Points to a %WapiFileTime structure to receive the
2484 * number of ticks since the epoch when file was last written to. May
2487 * Finds the number of ticks since the epoch that the file referenced
2488 * by @handle was created, last accessed and last modified. A tick is
2489 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2492 * Create time isn't recorded on POSIX file systems or reported by
2493 * stat(2), so that time is guessed by returning the oldest of the
2496 * Return value: %TRUE on success, %FALSE otherwise.
2498 gboolean
GetFileTime(gpointer handle
, WapiFileTime
*create_time
,
2499 WapiFileTime
*last_access
, WapiFileTime
*last_write
)
2501 WapiHandleType type
;
2503 type
= _wapi_handle_type (handle
);
2505 if (io_ops
[type
].getfiletime
== NULL
) {
2506 SetLastError (ERROR_INVALID_HANDLE
);
2510 return(io_ops
[type
].getfiletime (handle
, create_time
, last_access
,
2516 * @handle: The file handle to set. The handle must have
2517 * %GENERIC_WRITE access.
2518 * @create_time: Points to a %WapiFileTime structure that contains the
2519 * number of ticks since the epoch that the file was created. May be
2521 * @last_access: Points to a %WapiFileTime structure that contains the
2522 * number of ticks since the epoch when the file was last accessed.
2524 * @last_write: Points to a %WapiFileTime structure that contains the
2525 * number of ticks since the epoch when the file was last written to.
2528 * Sets the number of ticks since the epoch that the file referenced
2529 * by @handle was created, last accessed or last modified. A tick is
2530 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2533 * Create time isn't recorded on POSIX file systems, and is ignored.
2535 * Return value: %TRUE on success, %FALSE otherwise.
2537 gboolean
SetFileTime(gpointer handle
, const WapiFileTime
*create_time
,
2538 const WapiFileTime
*last_access
,
2539 const WapiFileTime
*last_write
)
2541 WapiHandleType type
;
2543 type
= _wapi_handle_type (handle
);
2545 if (io_ops
[type
].setfiletime
== NULL
) {
2546 SetLastError (ERROR_INVALID_HANDLE
);
2550 return(io_ops
[type
].setfiletime (handle
, create_time
, last_access
,
2554 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2555 * January 1 1601 GMT
2558 #define TICKS_PER_MILLISECOND 10000L
2559 #define TICKS_PER_SECOND 10000000L
2560 #define TICKS_PER_MINUTE 600000000L
2561 #define TICKS_PER_HOUR 36000000000LL
2562 #define TICKS_PER_DAY 864000000000LL
2564 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2566 static const guint16 mon_yday
[2][13]={
2567 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2568 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2572 * FileTimeToSystemTime:
2573 * @file_time: Points to a %WapiFileTime structure that contains the
2574 * number of ticks to convert.
2575 * @system_time: Points to a %WapiSystemTime structure to receive the
2578 * Converts a tick count into broken-out time values.
2580 * Return value: %TRUE on success, %FALSE otherwise.
2582 gboolean
FileTimeToSystemTime(const WapiFileTime
*file_time
,
2583 WapiSystemTime
*system_time
)
2585 gint64 file_ticks
, totaldays
, rem
, y
;
2588 if(system_time
==NULL
) {
2590 g_message("%s: system_time NULL", __func__
);
2593 SetLastError (ERROR_INVALID_PARAMETER
);
2597 file_ticks
=((gint64
)file_time
->dwHighDateTime
<< 32) +
2598 file_time
->dwLowDateTime
;
2600 /* Really compares if file_ticks>=0x8000000000000000
2601 * (LLONG_MAX+1) but we're working with a signed value for the
2602 * year and day calculation to work later
2606 g_message("%s: file_time too big", __func__
);
2609 SetLastError (ERROR_INVALID_PARAMETER
);
2613 totaldays
=(file_ticks
/ TICKS_PER_DAY
);
2614 rem
= file_ticks
% TICKS_PER_DAY
;
2616 g_message("%s: totaldays: %lld rem: %lld", __func__
, totaldays
, rem
);
2619 system_time
->wHour
=rem
/TICKS_PER_HOUR
;
2620 rem
%= TICKS_PER_HOUR
;
2622 g_message("%s: Hour: %d rem: %lld", __func__
, system_time
->wHour
, rem
);
2625 system_time
->wMinute
= rem
/ TICKS_PER_MINUTE
;
2626 rem
%= TICKS_PER_MINUTE
;
2628 g_message("%s: Minute: %d rem: %lld", __func__
, system_time
->wMinute
,
2632 system_time
->wSecond
= rem
/ TICKS_PER_SECOND
;
2633 rem
%= TICKS_PER_SECOND
;
2635 g_message("%s: Second: %d rem: %lld", __func__
, system_time
->wSecond
,
2639 system_time
->wMilliseconds
= rem
/ TICKS_PER_MILLISECOND
;
2641 g_message("%s: Milliseconds: %d", __func__
,
2642 system_time
->wMilliseconds
);
2645 /* January 1, 1601 was a Monday, according to Emacs calendar */
2646 system_time
->wDayOfWeek
= ((1 + totaldays
) % 7) + 1;
2648 g_message("%s: Day of week: %d", __func__
, system_time
->wDayOfWeek
);
2651 /* This algorithm to find year and month given days from epoch
2656 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2657 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2659 while(totaldays
< 0 || totaldays
>= (isleap(y
)?366:365)) {
2660 /* Guess a corrected year, assuming 365 days per year */
2661 gint64 yg
= y
+ totaldays
/ 365 - (totaldays
% 365 < 0);
2663 g_message("%s: totaldays: %lld yg: %lld y: %lld", __func__
,
2666 g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__
,
2667 LEAPS_THRU_END_OF(yg
-1), LEAPS_THRU_END_OF(y
-1));
2670 /* Adjust days and y to match the guessed year. */
2671 totaldays
-= ((yg
- y
) * 365
2672 + LEAPS_THRU_END_OF (yg
- 1)
2673 - LEAPS_THRU_END_OF (y
- 1));
2675 g_message("%s: totaldays: %lld", __func__
, totaldays
);
2679 g_message("%s: y: %lld", __func__
, y
);
2683 system_time
->wYear
= y
;
2685 g_message("%s: Year: %d", __func__
, system_time
->wYear
);
2688 ip
= mon_yday
[isleap(y
)];
2690 for(y
=11; totaldays
< ip
[y
]; --y
) {
2695 g_message("%s: totaldays: %lld", __func__
, totaldays
);
2698 system_time
->wMonth
= y
+ 1;
2700 g_message("%s: Month: %d", __func__
, system_time
->wMonth
);
2703 system_time
->wDay
= totaldays
+ 1;
2705 g_message("%s: Day: %d", __func__
, system_time
->wDay
);
2711 gpointer
FindFirstFile (const gunichar2
*pattern
, WapiFindData
*find_data
)
2713 struct _WapiHandle_find find_handle
= {0};
2715 gchar
*utf8_pattern
= NULL
, *dir_part
, *entry_part
;
2718 if (pattern
== NULL
) {
2720 g_message ("%s: pattern is NULL", __func__
);
2723 SetLastError (ERROR_PATH_NOT_FOUND
);
2724 return(INVALID_HANDLE_VALUE
);
2727 utf8_pattern
= mono_unicode_to_external (pattern
);
2728 if (utf8_pattern
== NULL
) {
2730 g_message ("%s: unicode conversion returned NULL", __func__
);
2733 SetLastError (ERROR_INVALID_NAME
);
2734 return(INVALID_HANDLE_VALUE
);
2738 g_message ("%s: looking for [%s]", __func__
, utf8_pattern
);
2741 /* Figure out which bit of the pattern is the directory */
2742 dir_part
= _wapi_dirname (utf8_pattern
);
2743 entry_part
= _wapi_basename (utf8_pattern
);
2746 /* Don't do this check for now, it breaks if directories
2747 * really do have metachars in their names (see bug 58116).
2748 * FIXME: Figure out a better solution to keep some checks...
2750 if (strchr (dir_part
, '*') || strchr (dir_part
, '?')) {
2751 SetLastError (ERROR_INVALID_NAME
);
2753 g_free (entry_part
);
2754 g_free (utf8_pattern
);
2755 return(INVALID_HANDLE_VALUE
);
2759 /* The pattern can specify a directory or a set of files.
2761 * The pattern can have wildcard characters ? and *, but only
2762 * in the section after the last directory delimiter. (Return
2763 * ERROR_INVALID_NAME if there are wildcards in earlier path
2764 * sections.) "*" has the usual 0-or-more chars meaning. "?"
2765 * means "match one character", "??" seems to mean "match one
2766 * or two characters", "???" seems to mean "match one, two or
2767 * three characters", etc. Windows will also try and match
2768 * the mangled "short name" of files, so 8 character patterns
2769 * with wildcards will show some surprising results.
2771 * All the written documentation I can find says that '?'
2772 * should only match one character, and doesn't mention '??',
2773 * '???' etc. I'm going to assume that the strict behaviour
2774 * (ie '???' means three and only three characters) is the
2775 * correct one, because that lets me use fnmatch(3) rather
2776 * than mess around with regexes.
2779 find_handle
.namelist
= NULL
;
2780 result
= _wapi_io_scandir (dir_part
, entry_part
,
2781 &find_handle
.namelist
);
2784 /* No files, which windows seems to call
2787 SetLastError (ERROR_FILE_NOT_FOUND
);
2788 g_free (utf8_pattern
);
2789 g_free (entry_part
);
2791 return (INVALID_HANDLE_VALUE
);
2796 gint errnum
= errno
;
2798 _wapi_set_last_path_error_from_errno (dir_part
, NULL
);
2800 g_message ("%s: scandir error: %s", __func__
,
2801 g_strerror (errnum
));
2803 g_free (utf8_pattern
);
2804 g_free (entry_part
);
2806 return (INVALID_HANDLE_VALUE
);
2809 g_free (utf8_pattern
);
2810 g_free (entry_part
);
2813 g_message ("%s: Got %d matches", __func__
, result
);
2816 find_handle
.dir_part
= dir_part
;
2817 find_handle
.num
= result
;
2818 find_handle
.count
= 0;
2820 handle
= _wapi_handle_new (WAPI_HANDLE_FIND
, &find_handle
);
2821 if (handle
== _WAPI_HANDLE_INVALID
) {
2822 g_warning ("%s: error creating find handle", __func__
);
2824 g_free (entry_part
);
2825 g_free (utf8_pattern
);
2826 SetLastError (ERROR_GEN_FAILURE
);
2828 return(INVALID_HANDLE_VALUE
);
2831 if (handle
!= INVALID_HANDLE_VALUE
&&
2832 !FindNextFile (handle
, find_data
)) {
2834 SetLastError (ERROR_NO_MORE_FILES
);
2835 handle
= INVALID_HANDLE_VALUE
;
2841 gboolean
FindNextFile (gpointer handle
, WapiFindData
*find_data
)
2843 struct _WapiHandle_find
*find_handle
;
2845 struct stat buf
, linkbuf
;
2848 gchar
*utf8_filename
, *utf8_basename
;
2849 gunichar2
*utf16_basename
;
2853 gboolean ret
= FALSE
;
2855 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FIND
,
2856 (gpointer
*)&find_handle
);
2858 g_warning ("%s: error looking up find handle %p", __func__
,
2860 SetLastError (ERROR_INVALID_HANDLE
);
2864 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle
,
2866 thr_ret
= _wapi_handle_lock_handle (handle
);
2867 g_assert (thr_ret
== 0);
2870 if (find_handle
->count
>= find_handle
->num
) {
2871 SetLastError (ERROR_NO_MORE_FILES
);
2875 /* stat next match */
2877 filename
= g_build_filename (find_handle
->dir_part
, find_handle
->namelist
[find_handle
->count
++], NULL
);
2879 result
= _wapi_stat (filename
, &buf
);
2880 if (result
== -1 && errno
== ENOENT
) {
2881 /* Might be a dangling symlink */
2882 result
= _wapi_lstat (filename
, &buf
);
2887 g_message ("%s: stat failed: %s", __func__
, filename
);
2894 result
= _wapi_lstat (filename
, &linkbuf
);
2897 g_message ("%s: lstat failed: %s", __func__
, filename
);
2904 utf8_filename
= mono_utf8_from_external (filename
);
2905 if (utf8_filename
== NULL
) {
2906 /* We couldn't turn this filename into utf8 (eg the
2907 * encoding of the name wasn't convertible), so just
2910 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__
, filename
);
2918 g_message ("%s: Found [%s]", __func__
, utf8_filename
);
2921 /* fill data block */
2923 if (buf
.st_mtime
< buf
.st_ctime
)
2924 create_time
= buf
.st_mtime
;
2926 create_time
= buf
.st_ctime
;
2928 find_data
->dwFileAttributes
= _wapi_stat_to_file_attributes (utf8_filename
, &buf
, &linkbuf
);
2930 _wapi_time_t_to_filetime (create_time
, &find_data
->ftCreationTime
);
2931 _wapi_time_t_to_filetime (buf
.st_atime
, &find_data
->ftLastAccessTime
);
2932 _wapi_time_t_to_filetime (buf
.st_mtime
, &find_data
->ftLastWriteTime
);
2934 if (find_data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
2935 find_data
->nFileSizeHigh
= 0;
2936 find_data
->nFileSizeLow
= 0;
2938 find_data
->nFileSizeHigh
= buf
.st_size
>> 32;
2939 find_data
->nFileSizeLow
= buf
.st_size
& 0xFFFFFFFF;
2942 find_data
->dwReserved0
= 0;
2943 find_data
->dwReserved1
= 0;
2945 utf8_basename
= _wapi_basename (utf8_filename
);
2946 utf16_basename
= g_utf8_to_utf16 (utf8_basename
, -1, NULL
, &bytes
,
2948 if(utf16_basename
==NULL
) {
2949 g_free (utf8_basename
);
2950 g_free (utf8_filename
);
2955 /* utf16 is 2 * utf8 */
2958 memset (find_data
->cFileName
, '\0', (MAX_PATH
*2));
2960 /* Truncating a utf16 string like this might leave the last
2963 memcpy (find_data
->cFileName
, utf16_basename
,
2964 bytes
<(MAX_PATH
*2)-2?bytes
:(MAX_PATH
*2)-2);
2966 find_data
->cAlternateFileName
[0] = 0; /* not used */
2968 g_free (utf8_basename
);
2969 g_free (utf8_filename
);
2970 g_free (utf16_basename
);
2973 thr_ret
= _wapi_handle_unlock_handle (handle
);
2974 g_assert (thr_ret
== 0);
2975 pthread_cleanup_pop (0);
2982 * @wapi_handle: the find handle to close.
2984 * Closes find handle @wapi_handle
2986 * Return value: %TRUE on success, %FALSE otherwise.
2988 gboolean
FindClose (gpointer handle
)
2990 struct _WapiHandle_find
*find_handle
;
2994 if (handle
== NULL
) {
2995 SetLastError (ERROR_INVALID_HANDLE
);
2999 ok
=_wapi_lookup_handle (handle
, WAPI_HANDLE_FIND
,
3000 (gpointer
*)&find_handle
);
3002 g_warning ("%s: error looking up find handle %p", __func__
,
3004 SetLastError (ERROR_INVALID_HANDLE
);
3008 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle
,
3010 thr_ret
= _wapi_handle_lock_handle (handle
);
3011 g_assert (thr_ret
== 0);
3013 g_strfreev (find_handle
->namelist
);
3014 g_free (find_handle
->dir_part
);
3016 thr_ret
= _wapi_handle_unlock_handle (handle
);
3017 g_assert (thr_ret
== 0);
3018 pthread_cleanup_pop (0);
3020 _wapi_handle_unref (handle
);
3027 * @name: a pointer to a NULL-terminated unicode string, that names
3028 * the directory to be created.
3029 * @security: ignored for now
3031 * Creates directory @name
3033 * Return value: %TRUE on success, %FALSE otherwise.
3035 gboolean
CreateDirectory (const gunichar2
*name
,
3036 WapiSecurityAttributes
*security
)
3043 g_message("%s: name is NULL", __func__
);
3046 SetLastError (ERROR_INVALID_NAME
);
3050 utf8_name
= mono_unicode_to_external (name
);
3051 if (utf8_name
== NULL
) {
3053 g_message ("%s: unicode conversion returned NULL", __func__
);
3056 SetLastError (ERROR_INVALID_NAME
);
3060 result
= _wapi_mkdir (utf8_name
, 0777);
3067 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3074 * @name: a pointer to a NULL-terminated unicode string, that names
3075 * the directory to be removed.
3077 * Removes directory @name
3079 * Return value: %TRUE on success, %FALSE otherwise.
3081 gboolean
RemoveDirectory (const gunichar2
*name
)
3088 g_message("%s: name is NULL", __func__
);
3091 SetLastError (ERROR_INVALID_NAME
);
3095 utf8_name
= mono_unicode_to_external (name
);
3096 if (utf8_name
== NULL
) {
3098 g_message ("%s: unicode conversion returned NULL", __func__
);
3101 SetLastError (ERROR_INVALID_NAME
);
3105 result
= _wapi_rmdir (utf8_name
);
3107 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3118 * GetFileAttributes:
3119 * @name: a pointer to a NULL-terminated unicode filename.
3121 * Gets the attributes for @name;
3123 * Return value: %INVALID_FILE_ATTRIBUTES on failure
3125 guint32
GetFileAttributes (const gunichar2
*name
)
3128 struct stat buf
, linkbuf
;
3134 g_message("%s: name is NULL", __func__
);
3137 SetLastError (ERROR_INVALID_NAME
);
3141 utf8_name
= mono_unicode_to_external (name
);
3142 if (utf8_name
== NULL
) {
3144 g_message ("%s: unicode conversion returned NULL", __func__
);
3147 SetLastError (ERROR_INVALID_PARAMETER
);
3148 return (INVALID_FILE_ATTRIBUTES
);
3151 result
= _wapi_stat (utf8_name
, &buf
);
3152 if (result
== -1 && errno
== ENOENT
) {
3153 /* Might be a dangling symlink... */
3154 result
= _wapi_lstat (utf8_name
, &buf
);
3158 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3160 return (INVALID_FILE_ATTRIBUTES
);
3163 result
= _wapi_lstat (utf8_name
, &linkbuf
);
3165 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3167 return (INVALID_FILE_ATTRIBUTES
);
3170 ret
= _wapi_stat_to_file_attributes (utf8_name
, &buf
, &linkbuf
);
3178 * GetFileAttributesEx:
3179 * @name: a pointer to a NULL-terminated unicode filename.
3180 * @level: must be GetFileExInfoStandard
3181 * @info: pointer to a WapiFileAttributesData structure
3183 * Gets attributes, size and filetimes for @name;
3185 * Return value: %TRUE on success, %FALSE on failure
3187 gboolean
GetFileAttributesEx (const gunichar2
*name
, WapiGetFileExInfoLevels level
, gpointer info
)
3190 WapiFileAttributesData
*data
;
3192 struct stat buf
, linkbuf
;
3196 if (level
!= GetFileExInfoStandard
) {
3198 g_message ("%s: info level %d not supported.", __func__
,
3202 SetLastError (ERROR_INVALID_PARAMETER
);
3208 g_message("%s: name is NULL", __func__
);
3211 SetLastError (ERROR_INVALID_NAME
);
3215 utf8_name
= mono_unicode_to_external (name
);
3216 if (utf8_name
== NULL
) {
3218 g_message ("%s: unicode conversion returned NULL", __func__
);
3221 SetLastError (ERROR_INVALID_PARAMETER
);
3225 result
= _wapi_stat (utf8_name
, &buf
);
3226 if (result
== -1 && errno
== ENOENT
) {
3227 /* Might be a dangling symlink... */
3228 result
= _wapi_lstat (utf8_name
, &buf
);
3232 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3237 result
= _wapi_lstat (utf8_name
, &linkbuf
);
3239 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3244 /* fill data block */
3246 data
= (WapiFileAttributesData
*)info
;
3248 if (buf
.st_mtime
< buf
.st_ctime
)
3249 create_time
= buf
.st_mtime
;
3251 create_time
= buf
.st_ctime
;
3253 data
->dwFileAttributes
= _wapi_stat_to_file_attributes (utf8_name
,
3259 _wapi_time_t_to_filetime (create_time
, &data
->ftCreationTime
);
3260 _wapi_time_t_to_filetime (buf
.st_atime
, &data
->ftLastAccessTime
);
3261 _wapi_time_t_to_filetime (buf
.st_mtime
, &data
->ftLastWriteTime
);
3263 if (data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
3264 data
->nFileSizeHigh
= 0;
3265 data
->nFileSizeLow
= 0;
3268 data
->nFileSizeHigh
= buf
.st_size
>> 32;
3269 data
->nFileSizeLow
= buf
.st_size
& 0xFFFFFFFF;
3277 * @name: name of file
3278 * @attrs: attributes to set
3280 * Changes the attributes on a named file.
3282 * Return value: %TRUE on success, %FALSE on failure.
3284 extern gboolean
SetFileAttributes (const gunichar2
*name
, guint32 attrs
)
3286 /* FIXME: think of something clever to do on unix */
3292 * Currently we only handle one *internal* case, with a value that is
3293 * not standard: 0x80000000, which means `set executable bit'
3298 g_message("%s: name is NULL", __func__
);
3301 SetLastError (ERROR_INVALID_NAME
);
3305 utf8_name
= mono_unicode_to_external (name
);
3306 if (utf8_name
== NULL
) {
3308 g_message ("%s: unicode conversion returned NULL", __func__
);
3311 SetLastError (ERROR_INVALID_NAME
);
3315 result
= _wapi_stat (utf8_name
, &buf
);
3317 _wapi_set_last_path_error_from_errno (NULL
, utf8_name
);
3322 /* Contrary to the documentation, ms allows NORMAL to be
3323 * specified along with other attributes, so dont bother to
3324 * catch that case here.
3326 if (attrs
& FILE_ATTRIBUTE_READONLY
) {
3327 result
= _wapi_chmod (utf8_name
, buf
.st_mode
& ~(S_IWRITE
| S_IWOTH
| S_IWGRP
));
3329 result
= _wapi_chmod (utf8_name
, buf
.st_mode
| S_IWRITE
);
3332 /* Ignore the other attributes for now */
3334 if (attrs
& 0x80000000){
3335 mode_t exec_mask
= 0;
3337 if ((buf
.st_mode
& S_IRUSR
) != 0)
3338 exec_mask
|= S_IXUSR
;
3340 if ((buf
.st_mode
& S_IRGRP
) != 0)
3341 exec_mask
|= S_IXGRP
;
3343 if ((buf
.st_mode
& S_IROTH
) != 0)
3344 exec_mask
|= S_IXOTH
;
3346 result
= chmod (utf8_name
, buf
.st_mode
| exec_mask
);
3348 /* Don't bother to reset executable (might need to change this
3358 * GetCurrentDirectory
3359 * @length: size of the buffer
3360 * @buffer: pointer to buffer that recieves path
3362 * Retrieves the current directory for the current process.
3364 * Return value: number of characters in buffer on success, zero on failure
3366 extern guint32
GetCurrentDirectory (guint32 length
, gunichar2
*buffer
)
3368 gunichar2
*utf16_path
;
3372 if (getcwd ((char*)buffer
, length
) == NULL
) {
3373 if (errno
== ERANGE
) { /*buffer length is not big enough */
3374 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*/
3377 utf16_path
= mono_unicode_from_external (path
, &bytes
);
3378 g_free (utf16_path
);
3382 _wapi_set_last_error_from_errno ();
3386 utf16_path
= mono_unicode_from_external ((gchar
*)buffer
, &bytes
);
3387 count
= (bytes
/2)+1;
3388 g_assert (count
<= length
); /*getcwd must have failed before with ERANGE*/
3390 /* Add the terminator */
3391 memset (buffer
, '\0', bytes
+2);
3392 memcpy (buffer
, utf16_path
, bytes
);
3394 g_free (utf16_path
);
3400 * SetCurrentDirectory
3401 * @path: path to new directory
3403 * Changes the directory path for the current process.
3405 * Return value: %TRUE on success, %FALSE on failure.
3407 extern gboolean
SetCurrentDirectory (const gunichar2
*path
)
3413 SetLastError (ERROR_INVALID_PARAMETER
);
3417 utf8_path
= mono_unicode_to_external (path
);
3418 if (_wapi_chdir (utf8_path
) != 0) {
3419 _wapi_set_last_error_from_errno ();
3429 gboolean
CreatePipe (gpointer
*readpipe
, gpointer
*writepipe
,
3430 WapiSecurityAttributes
*security G_GNUC_UNUSED
, guint32 size
)
3432 struct _WapiHandle_file pipe_read_handle
= {0};
3433 struct _WapiHandle_file pipe_write_handle
= {0};
3434 gpointer read_handle
;
3435 gpointer write_handle
;
3439 mono_once (&io_ops_once
, io_ops_init
);
3442 g_message ("%s: Creating pipe", __func__
);
3448 g_message ("%s: Error creating pipe: %s", __func__
,
3452 _wapi_set_last_error_from_errno ();
3456 if (filedes
[0] >= _wapi_fd_reserve
||
3457 filedes
[1] >= _wapi_fd_reserve
) {
3459 g_message ("%s: File descriptor is too big", __func__
);
3462 SetLastError (ERROR_TOO_MANY_OPEN_FILES
);
3470 /* filedes[0] is open for reading, filedes[1] for writing */
3472 pipe_read_handle
.fileaccess
= GENERIC_READ
;
3473 read_handle
= _wapi_handle_new_fd (WAPI_HANDLE_PIPE
, filedes
[0],
3475 if (read_handle
== _WAPI_HANDLE_INVALID
) {
3476 g_warning ("%s: error creating pipe read handle", __func__
);
3479 SetLastError (ERROR_GEN_FAILURE
);
3484 pipe_write_handle
.fileaccess
= GENERIC_WRITE
;
3485 write_handle
= _wapi_handle_new_fd (WAPI_HANDLE_PIPE
, filedes
[1],
3486 &pipe_write_handle
);
3487 if (write_handle
== _WAPI_HANDLE_INVALID
) {
3488 g_warning ("%s: error creating pipe write handle", __func__
);
3489 _wapi_handle_unref (read_handle
);
3493 SetLastError (ERROR_GEN_FAILURE
);
3498 *readpipe
= read_handle
;
3499 *writepipe
= write_handle
;
3502 g_message ("%s: Returning pipe: read handle %p, write handle %p",
3503 __func__
, read_handle
, write_handle
);
3509 guint32
GetTempPath (guint32 len
, gunichar2
*buf
)
3511 gchar
*tmpdir
=g_strdup (g_get_tmp_dir ());
3512 gunichar2
*tmpdir16
=NULL
;
3517 if(tmpdir
[strlen (tmpdir
)]!='/') {
3519 tmpdir
=g_strdup_printf ("%s/", g_get_tmp_dir ());
3522 tmpdir16
=mono_unicode_from_external (tmpdir
, &bytes
);
3523 if(tmpdir16
==NULL
) {
3531 g_message ("%s: Size %d smaller than needed (%ld)",
3532 __func__
, len
, dirlen
+1);
3537 /* Add the terminator */
3538 memset (buf
, '\0', bytes
+2);
3539 memcpy (buf
, tmpdir16
, bytes
);
3545 if(tmpdir16
!=NULL
) {
3554 GetLogicalDriveStrings (guint32 len
, gunichar2
*buf
)
3557 gunichar2
*ptr
, *dir
;
3558 glong length
, total
= 0;
3562 memset (buf
, 0, sizeof (gunichar2
) * (len
+ 1));
3567 /* Sigh, mntent and friends don't work well.
3568 * It stops on the first line that doesn't begin with a '/'.
3569 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
3570 fp
= fopen ("/etc/mtab", "rt");
3572 fp
= fopen ("/etc/mnttab", "rt");
3578 while (fgets (buffer
, 512, fp
) != NULL
) {
3582 splitted
= g_strsplit (buffer
, " ", 0);
3583 if (!*splitted
|| !*(splitted
+ 1)) {
3584 g_strfreev (splitted
);
3588 dir
= g_utf8_to_utf16 (*(splitted
+ 1), -1, &length
, NULL
, NULL
);
3589 g_strfreev (splitted
);
3590 if (total
+ length
+ 1 > len
) {
3592 return len
* 2; /* guess */
3595 memcpy (ptr
+ total
, dir
, sizeof (gunichar2
) * length
);
3597 total
+= length
+ 1;
3602 /* Commented out, does not work with my mtab!!! - Gonz */
3603 #ifdef NOTENABLED /* HAVE_MNTENT_H */
3607 gunichar2
*ptr
, *dir
;
3608 glong len
, total
= 0;
3611 fp
= setmntent ("/etc/mtab", "rt");
3613 fp
= setmntent ("/etc/mnttab", "rt");
3619 while ((mnt
= getmntent (fp
)) != NULL
) {
3620 g_print ("GOT %s\n", mnt
->mnt_dir
);
3621 dir
= g_utf8_to_utf16 (mnt
->mnt_dir
, &len
, NULL
, NULL
, NULL
);
3622 if (total
+ len
+ 1 > len
) {
3623 return len
* 2; /* guess */
3626 memcpy (ptr
+ total
, dir
, sizeof (gunichar2
) * len
);
3637 #if (defined(HAVE_STATVFS) || defined(HAVE_STATFS)) && !defined(PLATFORM_ANDROID)
3638 gboolean
GetDiskFreeSpaceEx(const gunichar2
*path_name
, WapiULargeInteger
*free_bytes_avail
,
3639 WapiULargeInteger
*total_number_of_bytes
,
3640 WapiULargeInteger
*total_number_of_free_bytes
)
3643 struct statvfs fsstat
;
3644 #elif defined(HAVE_STATFS)
3645 struct statfs fsstat
;
3647 gboolean isreadonly
;
3648 gchar
*utf8_path_name
;
3651 if (path_name
== NULL
) {
3652 utf8_path_name
= g_strdup (g_get_current_dir());
3653 if (utf8_path_name
== NULL
) {
3654 SetLastError (ERROR_DIRECTORY
);
3659 utf8_path_name
= mono_unicode_to_external (path_name
);
3660 if (utf8_path_name
== NULL
) {
3662 g_message("%s: unicode conversion returned NULL", __func__
);
3665 SetLastError (ERROR_INVALID_NAME
);
3672 ret
= statvfs (utf8_path_name
, &fsstat
);
3673 isreadonly
= ((fsstat
.f_flag
& ST_RDONLY
) == ST_RDONLY
);
3674 #elif defined(HAVE_STATFS)
3675 ret
= statfs (utf8_path_name
, &fsstat
);
3676 isreadonly
= ((fsstat
.f_flags
& MNT_RDONLY
) == MNT_RDONLY
);
3678 } while(ret
== -1 && errno
== EINTR
);
3680 g_free(utf8_path_name
);
3683 _wapi_set_last_error_from_errno ();
3685 g_message ("%s: statvfs failed: %s", __func__
, strerror (errno
));
3690 /* total number of free bytes for non-root */
3691 if (free_bytes_avail
!= NULL
) {
3693 free_bytes_avail
->QuadPart
= 0;
3696 free_bytes_avail
->QuadPart
= fsstat
.f_bsize
* fsstat
.f_bavail
;
3700 /* total number of bytes available for non-root */
3701 if (total_number_of_bytes
!= NULL
) {
3702 total_number_of_bytes
->QuadPart
= fsstat
.f_bsize
* fsstat
.f_blocks
;
3705 /* total number of bytes available for root */
3706 if (total_number_of_free_bytes
!= NULL
) {
3708 total_number_of_free_bytes
->QuadPart
= 0;
3711 total_number_of_free_bytes
->QuadPart
= fsstat
.f_bsize
* fsstat
.f_bfree
;
3718 gboolean
GetDiskFreeSpaceEx(const gunichar2
*path_name
, WapiULargeInteger
*free_bytes_avail
,
3719 WapiULargeInteger
*total_number_of_bytes
,
3720 WapiULargeInteger
*total_number_of_free_bytes
)
3722 if (free_bytes_avail
!= NULL
) {
3723 free_bytes_avail
->QuadPart
= (guint64
) -1;
3726 if (total_number_of_bytes
!= NULL
) {
3727 total_number_of_bytes
->QuadPart
= (guint64
) -1;
3730 if (total_number_of_free_bytes
!= NULL
) {
3731 total_number_of_free_bytes
->QuadPart
= (guint64
) -1;
3740 const gchar
* fstype
;
3743 static _wapi_drive_type _wapi_drive_types
[] = {
3744 { DRIVE_RAMDISK
, "ramfs" },
3745 { DRIVE_RAMDISK
, "tmpfs" },
3746 { DRIVE_RAMDISK
, "proc" },
3747 { DRIVE_RAMDISK
, "sysfs" },
3748 { DRIVE_RAMDISK
, "debugfs" },
3749 { DRIVE_RAMDISK
, "devpts" },
3750 { DRIVE_RAMDISK
, "securityfs" },
3751 { DRIVE_CDROM
, "iso9660" },
3752 { DRIVE_FIXED
, "ext2" },
3753 { DRIVE_FIXED
, "ext3" },
3754 { DRIVE_FIXED
, "ext4" },
3755 { DRIVE_FIXED
, "sysv" },
3756 { DRIVE_FIXED
, "reiserfs" },
3757 { DRIVE_FIXED
, "ufs" },
3758 { DRIVE_FIXED
, "vfat" },
3759 { DRIVE_FIXED
, "msdos" },
3760 { DRIVE_FIXED
, "udf" },
3761 { DRIVE_FIXED
, "hfs" },
3762 { DRIVE_FIXED
, "hpfs" },
3763 { DRIVE_FIXED
, "qnx4" },
3764 { DRIVE_FIXED
, "ntfs" },
3765 { DRIVE_FIXED
, "ntfs-3g" },
3766 { DRIVE_REMOTE
, "smbfs" },
3767 { DRIVE_REMOTE
, "fuse" },
3768 { DRIVE_REMOTE
, "nfs" },
3769 { DRIVE_REMOTE
, "nfs4" },
3770 { DRIVE_REMOTE
, "cifs" },
3771 { DRIVE_REMOTE
, "ncpfs" },
3772 { DRIVE_REMOTE
, "coda" },
3773 { DRIVE_REMOTE
, "afs" },
3774 { DRIVE_UNKNOWN
, NULL
}
3777 static guint32
_wapi_get_drive_type(const gchar
* fstype
)
3779 _wapi_drive_type
*current
;
3781 current
= &_wapi_drive_types
[0];
3782 while (current
->drive_type
!= DRIVE_UNKNOWN
) {
3783 if (strcmp (current
->fstype
, fstype
) == 0)
3789 return current
->drive_type
;
3792 guint32
GetDriveType(const gunichar2
*root_path_name
)
3797 gchar
*utf8_root_path_name
;
3800 if (root_path_name
== NULL
) {
3801 utf8_root_path_name
= g_strdup (g_get_current_dir());
3802 if (utf8_root_path_name
== NULL
) {
3803 return(DRIVE_NO_ROOT_DIR
);
3807 utf8_root_path_name
= mono_unicode_to_external (root_path_name
);
3808 if (utf8_root_path_name
== NULL
) {
3810 g_message("%s: unicode conversion returned NULL", __func__
);
3812 return(DRIVE_NO_ROOT_DIR
);
3815 /* strip trailing slash for compare below */
3816 if (g_str_has_suffix(utf8_root_path_name
, "/")) {
3817 utf8_root_path_name
[strlen(utf8_root_path_name
) - 1] = 0;
3821 fp
= fopen ("/etc/mtab", "rt");
3823 fp
= fopen ("/etc/mnttab", "rt");
3825 g_free (utf8_root_path_name
);
3826 return(DRIVE_UNKNOWN
);
3830 drive_type
= DRIVE_NO_ROOT_DIR
;
3831 while (fgets (buffer
, 512, fp
) != NULL
) {
3832 splitted
= g_strsplit (buffer
, " ", 0);
3833 if (!*splitted
|| !*(splitted
+ 1) || !*(splitted
+ 2)) {
3834 g_strfreev (splitted
);
3838 /* compare given root_path_name with the one from mtab,
3839 if length of utf8_root_path_name is zero it must be the root dir */
3840 if (strcmp (*(splitted
+ 1), utf8_root_path_name
) == 0 ||
3841 (strcmp (*(splitted
+ 1), "/") == 0 && strlen (utf8_root_path_name
) == 0)) {
3842 drive_type
= _wapi_get_drive_type (*(splitted
+ 2));
3843 g_strfreev (splitted
);
3847 g_strfreev (splitted
);
3851 g_free (utf8_root_path_name
);
3853 return (drive_type
);