2 * io.c: File, console and find handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
17 #include <sys/types.h>
23 #ifndef PLATFORM_WIN32
27 #elif defined(HAVE_SYS_AIO_H)
35 #include <mono/io-layer/wapi.h>
36 #include <mono/io-layer/wapi-private.h>
37 #include <mono/io-layer/handles-private.h>
38 #include <mono/io-layer/io-private.h>
39 #include <mono/io-layer/timefuncs-private.h>
40 #include <mono/io-layer/thread-private.h>
41 #include <mono/utils/strenc.h>
45 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length);
46 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length);
48 static void file_close_shared (gpointer handle);
49 static void file_close_private (gpointer handle);
50 static WapiFileType file_getfiletype(void);
51 static gboolean file_read(gpointer handle, gpointer buffer,
52 guint32 numbytes, guint32 *bytesread,
53 WapiOverlapped *overlapped);
54 static gboolean file_write(gpointer handle, gconstpointer buffer,
55 guint32 numbytes, guint32 *byteswritten,
56 WapiOverlapped *overlapped);
57 static gboolean file_flush(gpointer handle);
58 static guint32 file_seek(gpointer handle, gint32 movedistance,
59 gint32 *highmovedistance, WapiSeekMethod method);
60 static gboolean file_setendoffile(gpointer handle);
61 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
62 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
63 WapiFileTime *last_access,
64 WapiFileTime *last_write);
65 static gboolean file_setfiletime(gpointer handle,
66 const WapiFileTime *create_time,
67 const WapiFileTime *last_access,
68 const WapiFileTime *last_write);
70 /* File handle is only signalled for overlapped IO */
71 struct _WapiHandleOps _wapi_file_ops = {
72 file_close_shared, /* close_shared */
73 file_close_private, /* close_private */
79 static void console_close_shared (gpointer handle);
80 static void console_close_private (gpointer handle);
81 static WapiFileType console_getfiletype(void);
82 static gboolean console_read(gpointer handle, gpointer buffer,
83 guint32 numbytes, guint32 *bytesread,
84 WapiOverlapped *overlapped);
85 static gboolean console_write(gpointer handle, gconstpointer buffer,
86 guint32 numbytes, guint32 *byteswritten,
87 WapiOverlapped *overlapped);
89 /* Console is mostly the same as file, except it can block waiting for
92 struct _WapiHandleOps _wapi_console_ops = {
93 console_close_shared, /* close_shared */
94 console_close_private, /* close_private */
100 /* Find handle has no ops.
102 struct _WapiHandleOps _wapi_find_ops = {
103 NULL, /* close_shared */
104 NULL, /* close_private */
110 static void pipe_close_shared (gpointer handle);
111 static void pipe_close_private (gpointer handle);
112 static WapiFileType pipe_getfiletype (void);
113 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes,
114 guint32 *bytesread, WapiOverlapped *overlapped);
115 static gboolean pipe_write (gpointer handle, gconstpointer buffer,
116 guint32 numbytes, guint32 *byteswritten,
117 WapiOverlapped *overlapped);
121 struct _WapiHandleOps _wapi_pipe_ops = {
122 pipe_close_shared, /* close_shared */
123 pipe_close_private, /* close_private */
129 static const struct {
130 /* File, console and pipe handles */
131 WapiFileType (*getfiletype)(void);
133 /* File, console and pipe handles */
134 gboolean (*readfile)(gpointer handle, gpointer buffer,
135 guint32 numbytes, guint32 *bytesread,
136 WapiOverlapped *overlapped);
137 gboolean (*writefile)(gpointer handle, gconstpointer buffer,
138 guint32 numbytes, guint32 *byteswritten,
139 WapiOverlapped *overlapped);
140 gboolean (*flushfile)(gpointer handle);
143 guint32 (*seek)(gpointer handle, gint32 movedistance,
144 gint32 *highmovedistance, WapiSeekMethod method);
145 gboolean (*setendoffile)(gpointer handle);
146 guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
147 gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time,
148 WapiFileTime *last_access,
149 WapiFileTime *last_write);
150 gboolean (*setfiletime)(gpointer handle,
151 const WapiFileTime *create_time,
152 const WapiFileTime *last_access,
153 const WapiFileTime *last_write);
154 } io_ops[WAPI_HANDLE_COUNT]={
155 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
158 file_read, file_write,
159 file_flush, file_seek,
165 {console_getfiletype,
168 NULL, NULL, NULL, NULL, NULL, NULL},
170 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
172 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
174 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
176 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
177 /* socket (will need at least read and write) */
178 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
180 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
182 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
187 NULL, NULL, NULL, NULL, NULL, NULL},
191 static mono_once_t io_ops_once=MONO_ONCE_INIT;
193 static void io_ops_init (void)
195 /* _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
196 /* WAPI_HANDLE_CAP_WAIT); */
197 /* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
198 /* WAPI_HANDLE_CAP_WAIT); */
201 /* Some utility functions.
204 static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
208 /* FIXME: this could definitely be better */
210 if (S_ISDIR (buf->st_mode))
211 attrs |= FILE_ATTRIBUTE_DIRECTORY;
213 attrs |= FILE_ATTRIBUTE_ARCHIVE;
215 if (!(buf->st_mode & S_IWUSR))
216 attrs |= FILE_ATTRIBUTE_READONLY;
222 _wapi_set_last_error_from_errno (void)
224 SetLastError (_wapi_get_win32_file_error (errno));
229 static void file_close_shared (gpointer handle)
231 struct _WapiHandle_file *file_handle;
234 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
235 (gpointer *)&file_handle, NULL);
237 g_warning (G_GNUC_PRETTY_FUNCTION
238 ": error looking up file handle %p", handle);
239 SetLastError (ERROR_INVALID_HANDLE);
244 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p", handle);
247 if(file_handle->filename!=0) {
248 _wapi_handle_scratch_delete (file_handle->filename);
249 file_handle->filename=0;
251 if(file_handle->security_attributes!=0) {
252 _wapi_handle_scratch_delete (file_handle->security_attributes);
253 file_handle->security_attributes=0;
257 static void file_close_private (gpointer handle)
259 struct _WapiHandlePrivate_file *file_private_handle;
262 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
263 (gpointer *)&file_private_handle);
265 g_warning (G_GNUC_PRETTY_FUNCTION
266 ": error looking up file handle %p", handle);
267 SetLastError (ERROR_INVALID_HANDLE);
271 if (file_private_handle->fd_mapped.assigned) {
273 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
274 handle, file_private_handle->fd_mapped.fd);
277 /* Blank out the mapping, to make catching errors easier */
278 _wapi_handle_fd_offset_store (file_private_handle->fd_mapped.fd, NULL);
280 close(file_private_handle->fd_mapped.fd);
284 static WapiFileType file_getfiletype(void)
286 return(FILE_TYPE_DISK);
292 WapiOverlapped *overlapped;
293 WapiOverlappedCB callback;
296 #define SIGPTR(a) a.SIGVAL_PTR
299 async_notifier (union sigval sig)
301 notifier_data_t *ndata = SIGPTR (sig);
305 error = aio_return (ndata->aio);
307 error = _wapi_get_win32_file_error (error);
314 ndata->callback (error, numbytes, ndata->overlapped);
321 static gboolean file_read(gpointer handle, gpointer buffer,
322 guint32 numbytes, guint32 *bytesread,
323 WapiOverlapped *overlapped)
325 struct _WapiHandle_file *file_handle;
326 struct _WapiHandlePrivate_file *file_private_handle;
330 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
331 (gpointer *)&file_handle,
332 (gpointer *)&file_private_handle);
334 g_warning (G_GNUC_PRETTY_FUNCTION
335 ": error looking up file handle %p", handle);
336 SetLastError (ERROR_INVALID_HANDLE);
340 if (file_private_handle->fd_mapped.assigned == FALSE) {
341 SetLastError (ERROR_INVALID_HANDLE);
345 if(bytesread!=NULL) {
349 if(!(file_handle->fileaccess&GENERIC_READ) &&
350 !(file_handle->fileaccess&GENERIC_ALL)) {
352 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
355 SetLastError (ERROR_ACCESS_DENIED);
359 if (file_private_handle->async == FALSE) {
361 ret=read(file_private_handle->fd_mapped.fd, buffer,
364 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
370 g_message(G_GNUC_PRETTY_FUNCTION
371 ": read of handle %p fd %d error: %s", handle,
372 file_private_handle->fd_mapped.fd,
375 SetLastError (_wapi_get_win32_file_error (err));
379 if(bytesread!=NULL) {
387 SetLastError (ERROR_NOT_SUPPORTED);
390 if (overlapped == NULL || file_private_handle->callback == NULL) {
391 SetLastError (ERROR_INVALID_PARAMETER);
396 int fd = file_private_handle->fd_mapped.fd;
399 notifier_data_t *ndata;
401 ndata = g_new0 (notifier_data_t, 1);
402 aio = g_new0 (struct aiocb, 1);
403 ndata->overlapped = overlapped;
405 ndata->callback = file_private_handle->callback;
407 aio->aio_fildes = fd;
408 aio->aio_lio_opcode = LIO_READ;
409 aio->aio_nbytes = numbytes;
410 aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
411 aio->aio_buf = buffer;
412 aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
413 aio->aio_sigevent.sigev_notify_function = async_notifier;
414 SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
416 result = aio_read (aio);
418 _wapi_set_last_error_from_errno ();
422 result = aio_error (aio);
424 g_print ("aio_error (read) returned %d for %d\n", result, fd);
427 numbytes = aio_return (aio);
429 g_print ("numbytes %d for %d\n", numbytes, fd);
433 _wapi_set_last_error_from_errno ();
438 *bytesread = numbytes;
445 static gboolean file_write(gpointer handle, gconstpointer buffer,
446 guint32 numbytes, guint32 *byteswritten,
447 WapiOverlapped *overlapped G_GNUC_UNUSED)
449 struct _WapiHandle_file *file_handle;
450 struct _WapiHandlePrivate_file *file_private_handle;
454 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
455 (gpointer *)&file_handle,
456 (gpointer *)&file_private_handle);
458 g_warning (G_GNUC_PRETTY_FUNCTION
459 ": error looking up file handle %p", handle);
460 SetLastError (ERROR_INVALID_HANDLE);
464 if (file_private_handle->fd_mapped.assigned == FALSE) {
465 SetLastError (ERROR_INVALID_HANDLE);
469 if(byteswritten!=NULL) {
473 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
474 !(file_handle->fileaccess&GENERIC_ALL)) {
476 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
479 SetLastError (ERROR_ACCESS_DENIED);
483 if (file_private_handle->async == FALSE) {
486 /* Need to lock the region we're about to write to,
487 * because we only do advisory locking on POSIX
490 current_pos = lseek (file_private_handle->fd_mapped.fd,
492 if (current_pos == -1) {
494 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d lseek failed: %s", handle, file_private_handle->fd_mapped.fd, strerror (errno));
496 _wapi_set_last_error_from_errno ();
500 if (_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
501 current_pos, numbytes) == FALSE) {
502 /* The error has already been set */
507 ret=write(file_private_handle->fd_mapped.fd, buffer,
510 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
512 _wapi_unlock_file_region (file_private_handle->fd_mapped.fd,
513 current_pos, numbytes);
517 g_message(G_GNUC_PRETTY_FUNCTION
518 ": write of handle %p fd %d error: %s", handle,
519 file_private_handle->fd_mapped.fd,
523 _wapi_set_last_error_from_errno ();
526 if(byteswritten!=NULL) {
533 SetLastError (ERROR_NOT_SUPPORTED);
536 if (overlapped == NULL || file_private_handle->callback == NULL) {
537 SetLastError (ERROR_INVALID_PARAMETER);
542 int fd = file_private_handle->fd_mapped.fd;
545 notifier_data_t *ndata;
547 ndata = g_new0 (notifier_data_t, 1);
548 aio = g_new0 (struct aiocb, 1);
549 ndata->overlapped = overlapped;
551 ndata->callback = file_private_handle->callback;
553 aio->aio_fildes = fd;
554 aio->aio_lio_opcode = LIO_WRITE;
555 aio->aio_nbytes = numbytes;
556 aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
557 aio->aio_buf = (gpointer) buffer;
558 aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
559 aio->aio_sigevent.sigev_notify_function = async_notifier;
560 SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
562 result = aio_write (aio);
564 _wapi_set_last_error_from_errno ();
568 result = aio_error (aio);
570 g_print ("aio_error (write) returned %d for %d\n", result, fd);
573 numbytes = aio_return (aio);
575 g_print ("numbytes %d for %d\n", numbytes, fd);
579 _wapi_set_last_error_from_errno ();
584 *byteswritten = numbytes;
591 static gboolean file_flush(gpointer handle)
593 struct _WapiHandle_file *file_handle;
594 struct _WapiHandlePrivate_file *file_private_handle;
598 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
599 (gpointer *)&file_handle,
600 (gpointer *)&file_private_handle);
602 g_warning (G_GNUC_PRETTY_FUNCTION
603 ": error looking up file handle %p", handle);
604 SetLastError (ERROR_INVALID_HANDLE);
608 if (file_private_handle->fd_mapped.assigned == FALSE) {
609 SetLastError (ERROR_INVALID_HANDLE);
613 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
614 !(file_handle->fileaccess&GENERIC_ALL)) {
616 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
619 SetLastError (ERROR_ACCESS_DENIED);
623 ret=fsync(file_private_handle->fd_mapped.fd);
626 g_message(G_GNUC_PRETTY_FUNCTION
627 ": fsync of handle %p fd %d error: %s", handle,
628 file_private_handle->fd_mapped.fd, strerror(errno));
631 _wapi_set_last_error_from_errno ();
638 static guint32 file_seek(gpointer handle, gint32 movedistance,
639 gint32 *highmovedistance, WapiSeekMethod method)
641 struct _WapiHandle_file *file_handle;
642 struct _WapiHandlePrivate_file *file_private_handle;
644 off_t offset, newpos;
648 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
649 (gpointer *)&file_handle,
650 (gpointer *)&file_private_handle);
652 g_warning (G_GNUC_PRETTY_FUNCTION
653 ": error looking up file handle %p", handle);
654 SetLastError (ERROR_INVALID_HANDLE);
655 return(INVALID_SET_FILE_POINTER);
658 if (file_private_handle->fd_mapped.assigned == FALSE) {
659 SetLastError (ERROR_INVALID_HANDLE);
663 if(!(file_handle->fileaccess&GENERIC_READ) &&
664 !(file_handle->fileaccess&GENERIC_WRITE) &&
665 !(file_handle->fileaccess&GENERIC_ALL)) {
667 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
670 SetLastError (ERROR_ACCESS_DENIED);
671 return(INVALID_SET_FILE_POINTER);
686 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
690 SetLastError (ERROR_INVALID_PARAMETER);
691 return(INVALID_SET_FILE_POINTER);
694 #ifdef HAVE_LARGE_FILE_SUPPORT
695 if(highmovedistance==NULL) {
698 g_message(G_GNUC_PRETTY_FUNCTION
699 ": setting offset to %lld (low %d)", offset,
703 offset=((gint64) *highmovedistance << 32) | (unsigned long)movedistance;
706 g_message(G_GNUC_PRETTY_FUNCTION ": setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
714 #ifdef HAVE_LARGE_FILE_SUPPORT
715 g_message(G_GNUC_PRETTY_FUNCTION
716 ": moving handle %p fd %d by %lld bytes from %d", handle,
717 file_private_handle->fd_mapped.fd, offset, whence);
719 g_message(G_GNUC_PRETTY_FUNCTION
720 ": moving handle %p fd %d by %ld bytes from %d", handle,
721 file_private_handle->fd_mapped.fd, offset, whence);
725 newpos=lseek(file_private_handle->fd_mapped.fd, offset, whence);
728 g_message(G_GNUC_PRETTY_FUNCTION
729 ": lseek on handle %p fd %d returned error %s",
730 handle, file_private_handle->fd_mapped.fd,
734 _wapi_set_last_error_from_errno ();
735 return(INVALID_SET_FILE_POINTER);
739 #ifdef HAVE_LARGE_FILE_SUPPORT
740 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
742 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
746 #ifdef HAVE_LARGE_FILE_SUPPORT
747 ret=newpos & 0xFFFFFFFF;
748 if(highmovedistance!=NULL) {
749 *highmovedistance=newpos>>32;
753 if(highmovedistance!=NULL) {
754 /* Accurate, but potentially dodgy :-) */
760 g_message(G_GNUC_PRETTY_FUNCTION
761 ": move of handle %p fd %d returning %d/%d", handle,
762 file_private_handle->fd_mapped.fd, ret,
763 highmovedistance==NULL?0:*highmovedistance);
769 static gboolean file_setendoffile(gpointer handle)
771 struct _WapiHandle_file *file_handle;
772 struct _WapiHandlePrivate_file *file_private_handle;
778 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
779 (gpointer *)&file_handle,
780 (gpointer *)&file_private_handle);
782 g_warning (G_GNUC_PRETTY_FUNCTION
783 ": error looking up file handle %p", handle);
784 SetLastError (ERROR_INVALID_HANDLE);
788 if (file_private_handle->fd_mapped.assigned == FALSE) {
789 SetLastError (ERROR_INVALID_HANDLE);
793 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
794 !(file_handle->fileaccess&GENERIC_ALL)) {
796 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
799 SetLastError (ERROR_ACCESS_DENIED);
803 /* Find the current file position, and the file length. If
804 * the file position is greater than the length, write to
805 * extend the file with a hole. If the file position is less
806 * than the length, truncate the file.
809 ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
812 g_message(G_GNUC_PRETTY_FUNCTION
813 ": handle %p fd %d fstat failed: %s", handle,
814 file_private_handle->fd_mapped.fd, strerror(errno));
817 _wapi_set_last_error_from_errno ();
820 size=statbuf.st_size;
822 pos=lseek(file_private_handle->fd_mapped.fd, (off_t)0, SEEK_CUR);
825 g_message(G_GNUC_PRETTY_FUNCTION
826 ": handle %p fd %d lseek failed: %s", handle,
827 file_private_handle->fd_mapped.fd, strerror(errno));
830 _wapi_set_last_error_from_errno ();
837 ret=write(file_private_handle->fd_mapped.fd, "", 1);
839 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
843 g_message(G_GNUC_PRETTY_FUNCTION
844 ": handle %p fd %d extend write failed: %s",
845 handle, file_private_handle->fd_mapped.fd,
849 _wapi_set_last_error_from_errno ();
854 /* always truncate, because the extend write() adds an extra
855 * byte to the end of the file
858 ret=ftruncate(file_private_handle->fd_mapped.fd, pos);
860 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
864 g_message(G_GNUC_PRETTY_FUNCTION
865 ": handle %p fd %d ftruncate failed: %s", handle,
866 file_private_handle->fd_mapped.fd, strerror(errno));
869 _wapi_set_last_error_from_errno ();
876 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
878 struct _WapiHandle_file *file_handle;
879 struct _WapiHandlePrivate_file *file_private_handle;
885 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
886 (gpointer *)&file_handle,
887 (gpointer *)&file_private_handle);
889 g_warning (G_GNUC_PRETTY_FUNCTION
890 ": error looking up file handle %p", handle);
891 SetLastError (ERROR_INVALID_HANDLE);
892 return(INVALID_FILE_SIZE);
895 if (file_private_handle->fd_mapped.assigned == FALSE) {
896 SetLastError (ERROR_INVALID_HANDLE);
900 if(!(file_handle->fileaccess&GENERIC_READ) &&
901 !(file_handle->fileaccess&GENERIC_WRITE) &&
902 !(file_handle->fileaccess&GENERIC_ALL)) {
904 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
907 SetLastError (ERROR_ACCESS_DENIED);
908 return(INVALID_FILE_SIZE);
911 ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
914 g_message(G_GNUC_PRETTY_FUNCTION
915 ": handle %p fd %d fstat failed: %s", handle,
916 file_private_handle->fd_mapped.fd, strerror(errno));
919 _wapi_set_last_error_from_errno ();
920 return(INVALID_FILE_SIZE);
923 #ifdef HAVE_LARGE_FILE_SUPPORT
924 size=statbuf.st_size & 0xFFFFFFFF;
926 *highsize=statbuf.st_size>>32;
930 /* Accurate, but potentially dodgy :-) */
933 size=statbuf.st_size;
937 g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
944 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
945 WapiFileTime *last_access,
946 WapiFileTime *last_write)
948 struct _WapiHandle_file *file_handle;
949 struct _WapiHandlePrivate_file *file_private_handle;
952 guint64 create_ticks, access_ticks, write_ticks;
955 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
956 (gpointer *)&file_handle,
957 (gpointer *)&file_private_handle);
959 g_warning (G_GNUC_PRETTY_FUNCTION
960 ": error looking up file handle %p", handle);
961 SetLastError (ERROR_INVALID_HANDLE);
965 if (file_private_handle->fd_mapped.assigned == FALSE) {
966 SetLastError (ERROR_INVALID_HANDLE);
970 if(!(file_handle->fileaccess&GENERIC_READ) &&
971 !(file_handle->fileaccess&GENERIC_ALL)) {
973 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
976 SetLastError (ERROR_ACCESS_DENIED);
980 ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
983 g_message(G_GNUC_PRETTY_FUNCTION
984 ": handle %p fd %d fstat failed: %s", handle,
985 file_private_handle->fd_mapped.fd, strerror(errno));
988 _wapi_set_last_error_from_errno ();
993 g_message(G_GNUC_PRETTY_FUNCTION
994 ": atime: %ld ctime: %ld mtime: %ld",
995 statbuf.st_atime, statbuf.st_ctime,
999 /* Try and guess a meaningful create time by using the older
1002 /* The magic constant comes from msdn documentation
1003 * "Converting a time_t Value to a File Time"
1005 if(statbuf.st_atime < statbuf.st_ctime) {
1006 create_ticks=((guint64)statbuf.st_atime*10000000)
1007 + 116444736000000000ULL;
1009 create_ticks=((guint64)statbuf.st_ctime*10000000)
1010 + 116444736000000000ULL;
1013 access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
1014 write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
1017 g_message(G_GNUC_PRETTY_FUNCTION
1018 ": aticks: %llu cticks: %llu wticks: %llu",
1019 access_ticks, create_ticks, write_ticks);
1022 if(create_time!=NULL) {
1023 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
1024 create_time->dwHighDateTime = create_ticks >> 32;
1027 if(last_access!=NULL) {
1028 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
1029 last_access->dwHighDateTime = access_ticks >> 32;
1032 if(last_write!=NULL) {
1033 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
1034 last_write->dwHighDateTime = write_ticks >> 32;
1040 static gboolean file_setfiletime(gpointer handle,
1041 const WapiFileTime *create_time G_GNUC_UNUSED,
1042 const WapiFileTime *last_access,
1043 const WapiFileTime *last_write)
1045 struct _WapiHandle_file *file_handle;
1046 struct _WapiHandlePrivate_file *file_private_handle;
1049 struct utimbuf utbuf;
1050 struct stat statbuf;
1051 guint64 access_ticks, write_ticks;
1054 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1055 (gpointer *)&file_handle,
1056 (gpointer *)&file_private_handle);
1058 g_warning (G_GNUC_PRETTY_FUNCTION
1059 ": error looking up file handle %p", handle);
1060 SetLastError (ERROR_INVALID_HANDLE);
1064 if (file_private_handle->fd_mapped.assigned == FALSE) {
1065 SetLastError (ERROR_INVALID_HANDLE);
1069 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
1070 !(file_handle->fileaccess&GENERIC_ALL)) {
1072 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
1075 SetLastError (ERROR_ACCESS_DENIED);
1079 if(file_handle->filename==0) {
1081 g_message(G_GNUC_PRETTY_FUNCTION
1082 ": handle %p fd %d unknown filename", handle,
1083 file_private_handle->fd_mapped.fd);
1086 SetLastError (ERROR_INVALID_HANDLE);
1090 /* Get the current times, so we can put the same times back in
1091 * the event that one of the FileTime structs is NULL
1093 ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
1096 g_message(G_GNUC_PRETTY_FUNCTION
1097 ": handle %p fd %d fstat failed: %s", handle,
1098 file_private_handle->fd_mapped.fd, strerror(errno));
1101 SetLastError (ERROR_INVALID_PARAMETER);
1105 if(last_access!=NULL) {
1106 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
1107 last_access->dwLowDateTime;
1108 /* This is (time_t)0. We can actually go to INT_MIN,
1109 * but this will do for now.
1111 if (access_ticks < 116444736000000000ULL) {
1113 g_message (G_GNUC_PRETTY_FUNCTION
1114 ": attempt to set access time too early");
1116 SetLastError (ERROR_INVALID_PARAMETER);
1120 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1122 utbuf.actime=statbuf.st_atime;
1125 if(last_write!=NULL) {
1126 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
1127 last_write->dwLowDateTime;
1128 /* This is (time_t)0. We can actually go to INT_MIN,
1129 * but this will do for now.
1131 if (write_ticks < 116444736000000000ULL) {
1133 g_message (G_GNUC_PRETTY_FUNCTION
1134 ": attempt to set write time too early");
1136 SetLastError (ERROR_INVALID_PARAMETER);
1140 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1142 utbuf.modtime=statbuf.st_mtime;
1146 g_message(G_GNUC_PRETTY_FUNCTION
1147 ": setting handle %p access %ld write %ld", handle,
1148 utbuf.actime, utbuf.modtime);
1151 name=_wapi_handle_scratch_lookup (file_handle->filename);
1153 ret=utime(name, &utbuf);
1156 g_message(G_GNUC_PRETTY_FUNCTION
1157 ": handle %p [%s] fd %d utime failed: %s", handle,
1158 name, file_private_handle->fd_mapped.fd,
1163 SetLastError (ERROR_INVALID_PARAMETER);
1172 static void console_close_shared (gpointer handle)
1174 struct _WapiHandle_file *console_handle;
1177 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1178 (gpointer *)&console_handle, NULL);
1180 g_warning (G_GNUC_PRETTY_FUNCTION
1181 ": error looking up console handle %p", handle);
1182 SetLastError (ERROR_INVALID_HANDLE);
1187 g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
1190 if(console_handle->filename!=0) {
1191 _wapi_handle_scratch_delete (console_handle->filename);
1192 console_handle->filename=0;
1194 if(console_handle->security_attributes!=0) {
1195 _wapi_handle_scratch_delete (console_handle->security_attributes);
1196 console_handle->security_attributes=0;
1200 static void console_close_private (gpointer handle)
1202 struct _WapiHandlePrivate_file *console_private_handle;
1205 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
1206 (gpointer *)&console_private_handle);
1208 g_warning (G_GNUC_PRETTY_FUNCTION
1209 ": error looking up console handle %p", handle);
1210 SetLastError (ERROR_INVALID_HANDLE);
1214 if (console_private_handle->fd_mapped.assigned == TRUE) {
1216 g_message(G_GNUC_PRETTY_FUNCTION
1217 ": closing console handle %p with fd %d", handle,
1218 console_private_handle->fd_mapped.fd);
1221 /* Blank out the mapping, to make catching errors easier */
1222 _wapi_handle_fd_offset_store (console_private_handle->fd_mapped.fd, NULL);
1224 close(console_private_handle->fd_mapped.fd);
1228 static WapiFileType console_getfiletype(void)
1230 return(FILE_TYPE_CHAR);
1233 static gboolean console_read(gpointer handle, gpointer buffer,
1234 guint32 numbytes, guint32 *bytesread,
1235 WapiOverlapped *overlapped G_GNUC_UNUSED)
1237 struct _WapiHandle_file *console_handle;
1238 struct _WapiHandlePrivate_file *console_private_handle;
1242 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1243 (gpointer *)&console_handle,
1244 (gpointer *)&console_private_handle);
1246 g_warning (G_GNUC_PRETTY_FUNCTION
1247 ": error looking up console handle %p", handle);
1248 SetLastError (ERROR_INVALID_HANDLE);
1252 if (console_private_handle->fd_mapped.assigned == FALSE) {
1253 SetLastError (ERROR_INVALID_HANDLE);
1257 if(bytesread!=NULL) {
1261 if(!(console_handle->fileaccess&GENERIC_READ) &&
1262 !(console_handle->fileaccess&GENERIC_ALL)) {
1264 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, console_private_handle->fd_mapped.fd, console_handle->fileaccess);
1267 SetLastError (ERROR_ACCESS_DENIED);
1272 ret=read(console_private_handle->fd_mapped.fd, buffer,
1275 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1279 g_message(G_GNUC_PRETTY_FUNCTION
1280 ": read of handle %p fd %d error: %s", handle,
1281 console_private_handle->fd_mapped.fd,
1285 _wapi_set_last_error_from_errno ();
1289 if(bytesread!=NULL) {
1296 static gboolean console_write(gpointer handle, gconstpointer buffer,
1297 guint32 numbytes, guint32 *byteswritten,
1298 WapiOverlapped *overlapped G_GNUC_UNUSED)
1300 struct _WapiHandle_file *console_handle;
1301 struct _WapiHandlePrivate_file *console_private_handle;
1305 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1306 (gpointer *)&console_handle,
1307 (gpointer *)&console_private_handle);
1309 g_warning (G_GNUC_PRETTY_FUNCTION
1310 ": error looking up console handle %p", handle);
1311 SetLastError (ERROR_INVALID_HANDLE);
1315 if (console_private_handle->fd_mapped.assigned == FALSE) {
1316 SetLastError (ERROR_INVALID_HANDLE);
1320 if(byteswritten!=NULL) {
1324 if(!(console_handle->fileaccess&GENERIC_WRITE) &&
1325 !(console_handle->fileaccess&GENERIC_ALL)) {
1327 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd_mapped.fd, console_handle->fileaccess);
1330 SetLastError (ERROR_ACCESS_DENIED);
1335 ret=write(console_private_handle->fd_mapped.fd, buffer,
1338 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1342 g_message(G_GNUC_PRETTY_FUNCTION
1343 ": write of handle %p fd %d error: %s", handle,
1344 console_private_handle->fd_mapped.fd,
1348 _wapi_set_last_error_from_errno ();
1351 if(byteswritten!=NULL) {
1358 static void pipe_close_shared (gpointer handle)
1360 struct _WapiHandle_file *pipe_handle;
1363 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1364 (gpointer *)&pipe_handle, NULL);
1366 g_warning (G_GNUC_PRETTY_FUNCTION
1367 ": error looking up pipe handle %p", handle);
1368 SetLastError (ERROR_INVALID_HANDLE);
1373 g_message(G_GNUC_PRETTY_FUNCTION ": closing pipe handle %p", handle);
1376 if(pipe_handle->filename!=0) {
1377 _wapi_handle_scratch_delete (pipe_handle->filename);
1378 pipe_handle->filename=0;
1380 if(pipe_handle->security_attributes!=0) {
1381 _wapi_handle_scratch_delete (pipe_handle->security_attributes);
1382 pipe_handle->security_attributes=0;
1386 static void pipe_close_private (gpointer handle)
1388 struct _WapiHandlePrivate_file *pipe_private_handle;
1391 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
1392 (gpointer *)&pipe_private_handle);
1394 g_warning (G_GNUC_PRETTY_FUNCTION
1395 ": error looking up pipe handle %p", handle);
1396 SetLastError (ERROR_INVALID_HANDLE);
1400 if (pipe_private_handle->fd_mapped.assigned == TRUE) {
1402 g_message(G_GNUC_PRETTY_FUNCTION
1403 ": closing pipe handle %p with fd %d", handle,
1404 pipe_private_handle->fd_mapped.fd);
1407 /* Blank out the mapping, to make catching errors easier */
1408 _wapi_handle_fd_offset_store (pipe_private_handle->fd_mapped.fd, NULL);
1410 close(pipe_private_handle->fd_mapped.fd);
1414 static WapiFileType pipe_getfiletype(void)
1416 return(FILE_TYPE_PIPE);
1419 static gboolean pipe_read (gpointer handle, gpointer buffer,
1420 guint32 numbytes, guint32 *bytesread,
1421 WapiOverlapped *overlapped G_GNUC_UNUSED)
1423 struct _WapiHandle_file *pipe_handle;
1424 struct _WapiHandlePrivate_file *pipe_private_handle;
1428 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1429 (gpointer *)&pipe_handle,
1430 (gpointer *)&pipe_private_handle);
1432 g_warning (G_GNUC_PRETTY_FUNCTION
1433 ": error looking up pipe handle %p", handle);
1434 SetLastError (ERROR_INVALID_HANDLE);
1438 if (pipe_private_handle->fd_mapped.assigned == FALSE) {
1439 SetLastError (ERROR_INVALID_HANDLE);
1443 if(bytesread!=NULL) {
1447 if(!(pipe_handle->fileaccess&GENERIC_READ) &&
1448 !(pipe_handle->fileaccess&GENERIC_ALL)) {
1450 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, pipe_private_handle->fd_mapped.fd, pipe_handle->fileaccess);
1453 SetLastError (ERROR_ACCESS_DENIED);
1458 g_message (G_GNUC_PRETTY_FUNCTION
1459 ": reading up to %d bytes from pipe %p (fd %d)", numbytes,
1460 handle, pipe_private_handle->fd_mapped.fd);
1464 ret=read(pipe_private_handle->fd_mapped.fd, buffer, numbytes);
1466 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1470 g_message(G_GNUC_PRETTY_FUNCTION
1471 ": read of handle %p fd %d error: %s", handle,
1472 pipe_private_handle->fd_mapped.fd, strerror(errno));
1475 _wapi_set_last_error_from_errno ();
1480 g_message (G_GNUC_PRETTY_FUNCTION ": read %d bytes from pipe", ret);
1483 if(bytesread!=NULL) {
1490 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1491 guint32 numbytes, guint32 *byteswritten,
1492 WapiOverlapped *overlapped G_GNUC_UNUSED)
1494 struct _WapiHandle_file *pipe_handle;
1495 struct _WapiHandlePrivate_file *pipe_private_handle;
1499 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1500 (gpointer *)&pipe_handle,
1501 (gpointer *)&pipe_private_handle);
1503 g_warning (G_GNUC_PRETTY_FUNCTION
1504 ": error looking up pipe handle %p", handle);
1505 SetLastError (ERROR_INVALID_HANDLE);
1509 if (pipe_private_handle->fd_mapped.assigned == FALSE) {
1510 SetLastError (ERROR_INVALID_HANDLE);
1514 if(byteswritten!=NULL) {
1518 if(!(pipe_handle->fileaccess&GENERIC_WRITE) &&
1519 !(pipe_handle->fileaccess&GENERIC_ALL)) {
1521 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, pipe_private_handle->fd_mapped.fd, pipe_handle->fileaccess);
1524 SetLastError (ERROR_ACCESS_DENIED);
1529 g_message (G_GNUC_PRETTY_FUNCTION
1530 ": writing up to %d bytes to pipe %p (fd %d)", numbytes,
1531 handle, pipe_private_handle->fd_mapped.fd);
1535 ret=write(pipe_private_handle->fd_mapped.fd, buffer, numbytes);
1537 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1541 g_message(G_GNUC_PRETTY_FUNCTION
1542 ": write of handle %p fd %d error: %s", handle,
1543 pipe_private_handle->fd_mapped.fd, strerror(errno));
1546 _wapi_set_last_error_from_errno ();
1549 if(byteswritten!=NULL) {
1556 static int convert_flags(guint32 fileaccess, guint32 createmode)
1560 switch(fileaccess) {
1567 case GENERIC_READ|GENERIC_WRITE:
1572 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
1578 switch(createmode) {
1580 flags|=O_CREAT|O_EXCL;
1583 flags|=O_CREAT|O_TRUNC;
1590 case TRUNCATE_EXISTING:
1595 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
1604 static guint32 convert_from_flags(int flags)
1606 guint32 fileaccess=0;
1609 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1612 if((flags & O_ACCMODE) == O_RDONLY) {
1613 fileaccess=GENERIC_READ;
1614 } else if ((flags & O_ACCMODE) == O_WRONLY) {
1615 fileaccess=GENERIC_WRITE;
1616 } else if ((flags & O_ACCMODE) == O_RDWR) {
1617 fileaccess=GENERIC_READ|GENERIC_WRITE;
1620 g_message(G_GNUC_PRETTY_FUNCTION
1621 ": Can't figure out flags 0x%x", flags);
1625 /* Maybe sort out create mode too */
1630 static mode_t convert_perms(guint32 sharemode)
1634 if(sharemode&FILE_SHARE_READ) {
1637 if(sharemode&FILE_SHARE_WRITE) {
1647 * @name: a pointer to a NULL-terminated unicode string, that names
1648 * the file or other object to create.
1649 * @fileaccess: specifies the file access mode
1650 * @sharemode: whether the file should be shared. This parameter is
1651 * currently ignored.
1652 * @security: Ignored for now.
1653 * @createmode: specifies whether to create a new file, whether to
1654 * overwrite an existing file, whether to truncate the file, etc.
1655 * @attrs: specifies file attributes and flags. On win32 attributes
1656 * are characteristics of the file, not the handle, and are ignored
1657 * when an existing file is opened. Flags give the library hints on
1658 * how to process a file to optimise performance.
1659 * @template: the handle of an open %GENERIC_READ file that specifies
1660 * attributes to apply to a newly created file, ignoring @attrs.
1661 * Normally this parameter is NULL. This parameter is ignored when an
1662 * existing file is opened.
1664 * Creates a new file handle. This only applies to normal files:
1665 * pipes are handled by CreatePipe(), and console handles are created
1666 * with GetStdHandle().
1668 * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1670 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1671 guint32 sharemode, WapiSecurityAttributes *security,
1672 guint32 createmode, guint32 attrs,
1673 gpointer template G_GNUC_UNUSED)
1675 struct _WapiHandle_file *file_handle;
1676 struct _WapiHandlePrivate_file *file_private_handle;
1679 int flags=convert_flags(fileaccess, createmode);
1680 /*mode_t perms=convert_perms(sharemode);*/
1681 /* we don't use sharemode, because that relates to sharing of the file
1682 * when the file is open and is already handled by other code, perms instead
1683 * are the on-disk permissions and this is a sane default.
1689 gpointer cf_ret = INVALID_HANDLE_VALUE;
1690 struct stat statbuf;
1691 gboolean file_already_shared;
1692 guint32 file_existing_share, file_existing_access;
1694 mono_once (&io_ops_once, io_ops_init);
1698 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1701 SetLastError (ERROR_INVALID_NAME);
1702 return(INVALID_HANDLE_VALUE);
1705 filename=mono_unicode_to_external (name);
1706 if(filename==NULL) {
1708 g_message(G_GNUC_PRETTY_FUNCTION
1709 ": unicode conversion returned NULL");
1712 SetLastError (ERROR_INVALID_NAME);
1713 return(INVALID_HANDLE_VALUE);
1717 g_message (G_GNUC_PRETTY_FUNCTION ": Opening %s with share 0x%x and access 0x%x", filename, sharemode, fileaccess);
1720 fd = open(filename, flags, perms);
1722 /* If we were trying to open a directory with write permissions
1723 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1724 * EISDIR. However, this is a bit bogus because calls to
1725 * manipulate the directory (e.g. SetFileTime) will still work on
1726 * the directory because they use other API calls
1727 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1728 * to open the directory again without write permission.
1730 if (fd == -1 && errno == EISDIR)
1732 /* Try again but don't try to make it writable */
1733 fd = open(filename, flags & ~(O_RDWR|O_WRONLY), perms);
1738 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
1739 filename, strerror(errno));
1741 _wapi_set_last_error_from_errno ();
1744 return(INVALID_HANDLE_VALUE);
1747 if (fd >= _wapi_fd_offset_table_size) {
1749 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
1752 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1757 return(INVALID_HANDLE_VALUE);
1760 ret = fstat (fd, &statbuf);
1763 g_message (G_GNUC_PRETTY_FUNCTION ": fstat error of file %s: %s", filename, strerror (errno));
1765 _wapi_set_last_error_from_errno ();
1769 return(INVALID_HANDLE_VALUE);
1772 file_already_shared = _wapi_handle_get_or_set_share (statbuf.st_dev, statbuf.st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access);
1774 if (file_already_shared) {
1775 if (file_existing_share == 0) {
1776 /* Quick and easy, no possibility to share */
1778 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing = NONE", fileaccess);
1780 SetLastError (ERROR_SHARING_VIOLATION);
1784 return(INVALID_HANDLE_VALUE);
1787 if (((file_existing_share == FILE_SHARE_READ) &&
1788 (fileaccess != GENERIC_READ)) ||
1789 ((file_existing_share == FILE_SHARE_WRITE) &&
1790 (fileaccess != GENERIC_WRITE))) {
1791 /* New access mode doesn't match up */
1793 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", fileaccess, file_existing_share);
1795 SetLastError (ERROR_SHARING_VIOLATION);
1799 return(INVALID_HANDLE_VALUE);
1802 if (((file_existing_access & GENERIC_READ) &&
1803 !(sharemode & FILE_SHARE_READ)) ||
1804 ((file_existing_access & GENERIC_WRITE) &&
1805 !(sharemode & FILE_SHARE_WRITE))) {
1806 /* New share mode doesn't match up */
1808 g_message (G_GNUC_PRETTY_FUNCTION ": Access mode prevents open: requested share: 0x%x, file has access: 0x%x", sharemode, file_existing_access);
1810 SetLastError (ERROR_SHARING_VIOLATION);
1814 return(INVALID_HANDLE_VALUE);
1818 g_message (G_GNUC_PRETTY_FUNCTION ": New file!");
1822 handle=_wapi_handle_new (WAPI_HANDLE_FILE);
1823 if(handle==_WAPI_HANDLE_INVALID) {
1824 g_warning (G_GNUC_PRETTY_FUNCTION
1825 ": error creating file handle");
1829 SetLastError (ERROR_GEN_FAILURE);
1830 return(INVALID_HANDLE_VALUE);
1833 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
1835 thr_ret = _wapi_handle_lock_handle (handle);
1836 g_assert (thr_ret == 0);
1838 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1839 (gpointer *)&file_handle,
1840 (gpointer *)&file_private_handle);
1842 g_warning (G_GNUC_PRETTY_FUNCTION
1843 ": error looking up file handle %p", handle);
1844 SetLastError (ERROR_INVALID_HANDLE);
1849 _wapi_handle_fd_offset_store (fd, handle);
1850 cf_ret = GINT_TO_POINTER (fd);
1852 file_private_handle->fd_mapped.fd=fd;
1853 file_private_handle->fd_mapped.assigned=TRUE;
1854 file_private_handle->async = ((attrs & FILE_FLAG_OVERLAPPED) != 0);
1855 file_handle->filename=_wapi_handle_scratch_store (filename,
1857 if(security!=NULL) {
1858 file_handle->security_attributes=_wapi_handle_scratch_store (
1859 security, sizeof(WapiSecurityAttributes));
1862 file_handle->fileaccess=fileaccess;
1863 file_handle->sharemode=sharemode;
1864 file_handle->attrs=attrs;
1865 file_handle->device = statbuf.st_dev;
1866 file_handle->inode = statbuf.st_ino;
1869 g_message(G_GNUC_PRETTY_FUNCTION
1870 ": returning handle %p with fd %d", handle,
1871 file_private_handle->fd_mapped.fd);
1875 thr_ret = _wapi_handle_unlock_handle (handle);
1876 g_assert (thr_ret == 0);
1877 pthread_cleanup_pop (0);
1886 * @name: a pointer to a NULL-terminated unicode string, that names
1887 * the file to be deleted.
1889 * Deletes file @name.
1891 * Return value: %TRUE on success, %FALSE otherwise.
1893 gboolean DeleteFile(const gunichar2 *name)
1900 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1903 SetLastError (ERROR_INVALID_NAME);
1907 filename=mono_unicode_to_external(name);
1908 if(filename==NULL) {
1910 g_message(G_GNUC_PRETTY_FUNCTION
1911 ": unicode conversion returned NULL");
1914 SetLastError (ERROR_INVALID_NAME);
1918 ret=unlink(filename);
1926 _wapi_set_last_error_from_errno ();
1932 * @name: a pointer to a NULL-terminated unicode string, that names
1933 * the file to be moved.
1934 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1935 * new name for the file.
1937 * Renames file @name to @dest_name.
1938 * MoveFile sets ERROR_ALREADY_EXISTS if the destination exists, except
1939 * when it is the same file as the source. In that case it silently succeeds.
1941 * Return value: %TRUE on success, %FALSE otherwise.
1943 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1945 gchar *utf8_name, *utf8_dest_name;
1947 struct stat stat_src, stat_dest;
1951 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1954 SetLastError (ERROR_INVALID_NAME);
1958 utf8_name = mono_unicode_to_external (name);
1959 if (utf8_name == NULL) {
1961 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1964 SetLastError (ERROR_INVALID_NAME);
1968 if(dest_name==NULL) {
1970 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1974 SetLastError (ERROR_INVALID_NAME);
1978 utf8_dest_name = mono_unicode_to_external (dest_name);
1979 if (utf8_dest_name == NULL) {
1981 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1985 SetLastError (ERROR_INVALID_NAME);
1990 * In C# land we check for the existence of src, but not for dest.
1991 * We check it here and return the failure if dest exists and is not
1992 * the same file as src.
1994 if (!stat (utf8_dest_name, &stat_dest) && !stat (utf8_name, &stat_src)) {
1995 if (stat_dest.st_dev != stat_src.st_dev || stat_dest.st_ino != stat_src.st_ino) {
1996 SetLastError (ERROR_ALREADY_EXISTS);
2001 result = rename (utf8_name, utf8_dest_name);
2003 g_free (utf8_dest_name);
2005 if (result != 0 && errno == EXDEV) {
2006 /* Try a copy to the new location, and delete the source */
2007 if (CopyFile (name, dest_name, TRUE)==FALSE) {
2008 /* CopyFile will set the error */
2012 return(DeleteFile (name));
2021 SetLastError (ERROR_ALREADY_EXISTS);
2025 _wapi_set_last_error_from_errno ();
2034 * @name: a pointer to a NULL-terminated unicode string, that names
2035 * the file to be copied.
2036 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
2037 * new name for the file.
2038 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
2040 * Copies file @name to @dest_name
2042 * Return value: %TRUE on success, %FALSE otherwise.
2044 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
2045 gboolean fail_if_exists)
2047 gchar *utf8_src, *utf8_dest;
2048 int src_fd, dest_fd;
2056 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2059 SetLastError (ERROR_INVALID_NAME);
2063 utf8_src = mono_unicode_to_external (name);
2064 if (utf8_src == NULL) {
2066 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of source returned NULL");
2069 SetLastError (ERROR_INVALID_PARAMETER);
2073 if(dest_name==NULL) {
2075 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2079 SetLastError (ERROR_INVALID_NAME);
2083 utf8_dest = mono_unicode_to_external (dest_name);
2084 if (utf8_dest == NULL) {
2086 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of dest returned NULL");
2089 SetLastError (ERROR_INVALID_PARAMETER);
2096 src_fd = open (utf8_src, O_RDONLY);
2098 _wapi_set_last_error_from_errno ();
2106 if (fstat (src_fd, &st) < 0) {
2107 _wapi_set_last_error_from_errno ();
2116 if (fail_if_exists) {
2117 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
2119 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2121 /* O_TRUNC might cause a fail if the file
2124 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
2129 _wapi_set_last_error_from_errno ();
2138 buf_size = st.st_blksize;
2139 buf = (char *) alloca (buf_size);
2142 remain = read (src_fd, buf, buf_size);
2145 if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
2149 _wapi_set_last_error_from_errno ();
2163 while (remain > 0) {
2164 if ((n = write (dest_fd, buf, remain)) < 0) {
2165 if (errno == EINTR && !_wapi_thread_cur_apc_pending())
2168 _wapi_set_last_error_from_errno ();
2170 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
2193 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
2194 static gpointer stdin_handle=INVALID_HANDLE_VALUE;
2195 static gpointer stdout_handle=INVALID_HANDLE_VALUE;
2196 static gpointer stderr_handle=INVALID_HANDLE_VALUE;
2198 static gpointer stdhandle_create (int fd, const guchar *name)
2200 struct _WapiHandle_file *file_handle;
2201 struct _WapiHandlePrivate_file *file_private_handle;
2203 gpointer handle, ret = INVALID_HANDLE_VALUE;
2208 g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
2212 /* Check if fd is valid */
2214 flags=fcntl(fd, F_GETFL);
2216 while (flags==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
2219 /* Invalid fd. Not really much point checking for EBADF
2223 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
2224 fd, strerror(errno));
2227 _wapi_set_last_error_from_errno ();
2228 return(INVALID_HANDLE_VALUE);
2231 handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
2232 if(handle==_WAPI_HANDLE_INVALID) {
2233 g_warning (G_GNUC_PRETTY_FUNCTION
2234 ": error creating file handle");
2235 SetLastError (ERROR_GEN_FAILURE);
2236 return(INVALID_HANDLE_VALUE);
2239 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2241 thr_ret = _wapi_handle_lock_handle (handle);
2242 g_assert (thr_ret == 0);
2244 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2245 (gpointer *)&file_handle,
2246 (gpointer *)&file_private_handle);
2248 g_warning (G_GNUC_PRETTY_FUNCTION
2249 ": error looking up console handle %p", handle);
2250 SetLastError (ERROR_INVALID_HANDLE);
2254 /* We know this is fd 0, 1 or 2 */
2255 _wapi_handle_fd_offset_store (fd, handle);
2256 ret = GINT_TO_POINTER (fd);
2258 file_private_handle->fd_mapped.fd=fd;
2259 file_private_handle->fd_mapped.assigned=TRUE;
2260 file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
2261 /* some default security attributes might be needed */
2262 file_handle->security_attributes=0;
2263 file_handle->fileaccess=convert_from_flags(flags);
2264 file_handle->sharemode=0;
2265 file_handle->attrs=0;
2268 g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
2269 handle, file_private_handle->fd_mapped.fd);
2273 thr_ret = _wapi_handle_unlock_handle (handle);
2274 g_assert (thr_ret == 0);
2275 pthread_cleanup_pop (0);
2280 static void stdhandle_init (void)
2282 stdin_handle=stdhandle_create (0, "<stdin>");
2283 stdout_handle=stdhandle_create (1, "<stdout>");
2284 stderr_handle=stdhandle_create (2, "<stderr>");
2289 * @stdhandle: specifies the file descriptor
2291 * Returns a handle for stdin, stdout, or stderr. Always returns the
2292 * same handle for the same @stdhandle.
2294 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2297 gpointer GetStdHandle(WapiStdHandle stdhandle)
2301 mono_once (&io_ops_once, io_ops_init);
2302 mono_once (&stdhandle_once, stdhandle_init);
2305 case STD_INPUT_HANDLE:
2306 handle=stdin_handle;
2309 case STD_OUTPUT_HANDLE:
2310 handle=stdout_handle;
2313 case STD_ERROR_HANDLE:
2314 handle=stderr_handle;
2319 g_message(G_GNUC_PRETTY_FUNCTION
2320 ": unknown standard handle type");
2323 SetLastError (ERROR_INVALID_PARAMETER);
2324 return(INVALID_HANDLE_VALUE);
2327 if (handle == INVALID_HANDLE_VALUE) {
2328 SetLastError (ERROR_NO_MORE_FILES);
2329 return(INVALID_HANDLE_VALUE);
2332 /* Add a reference to this handle */
2333 _wapi_handle_ref (_wapi_handle_fd_offset_to_handle (handle));
2340 * @handle: The file handle to read from. The handle must have
2341 * %GENERIC_READ access.
2342 * @buffer: The buffer to store read data in
2343 * @numbytes: The maximum number of bytes to read
2344 * @bytesread: The actual number of bytes read is stored here. This
2345 * value can be zero if the handle is positioned at the end of the
2347 * @overlapped: points to a required %WapiOverlapped structure if
2348 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2351 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2352 * function reads up to @numbytes bytes from the file from the current
2353 * file position, and stores them in @buffer. If there are not enough
2354 * bytes left in the file, just the amount available will be read.
2355 * The actual number of bytes read is stored in @bytesread.
2357 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2358 * file position is ignored and the read position is taken from data
2359 * in the @overlapped structure.
2361 * Return value: %TRUE if the read succeeds (even if no bytes were
2362 * read due to an attempt to read past the end of the file), %FALSE on
2365 gboolean ReadFile(gpointer fd_handle, gpointer buffer, guint32 numbytes,
2366 guint32 *bytesread, WapiOverlapped *overlapped)
2368 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2369 WapiHandleType type;
2371 if (handle == NULL) {
2372 SetLastError (ERROR_INVALID_HANDLE);
2376 type = _wapi_handle_type (handle);
2378 if(io_ops[type].readfile==NULL) {
2379 SetLastError (ERROR_INVALID_HANDLE);
2383 return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2389 * @handle: The file handle to write to. The handle must have
2390 * %GENERIC_WRITE access.
2391 * @buffer: The buffer to read data from.
2392 * @numbytes: The maximum number of bytes to write.
2393 * @byteswritten: The actual number of bytes written is stored here.
2394 * If the handle is positioned at the file end, the length of the file
2395 * is extended. This parameter may be %NULL.
2396 * @overlapped: points to a required %WapiOverlapped structure if
2397 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2400 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2401 * function writes up to @numbytes bytes from @buffer to the file at
2402 * the current file position. If @handle is positioned at the end of
2403 * the file, the file is extended. The actual number of bytes written
2404 * is stored in @byteswritten.
2406 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2407 * file position is ignored and the write position is taken from data
2408 * in the @overlapped structure.
2410 * Return value: %TRUE if the write succeeds, %FALSE on error.
2412 gboolean WriteFile(gpointer fd_handle, gconstpointer buffer, guint32 numbytes,
2413 guint32 *byteswritten, WapiOverlapped *overlapped)
2415 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2416 WapiHandleType type;
2418 if (handle == NULL) {
2419 SetLastError (ERROR_INVALID_HANDLE);
2423 type = _wapi_handle_type (handle);
2425 if(io_ops[type].writefile==NULL) {
2426 SetLastError (ERROR_INVALID_HANDLE);
2430 return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2436 * @handle: Handle to open file. The handle must have
2437 * %GENERIC_WRITE access.
2439 * Flushes buffers of the file and causes all unwritten data to
2442 * Return value: %TRUE on success, %FALSE otherwise.
2444 gboolean FlushFileBuffers(gpointer fd_handle)
2446 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2447 WapiHandleType type;
2449 if (handle == NULL) {
2450 SetLastError (ERROR_INVALID_HANDLE);
2454 type = _wapi_handle_type (handle);
2456 if(io_ops[type].flushfile==NULL) {
2457 SetLastError (ERROR_INVALID_HANDLE);
2461 return(io_ops[type].flushfile (handle));
2466 * @handle: The file handle to set. The handle must have
2467 * %GENERIC_WRITE access.
2469 * Moves the end-of-file position to the current position of the file
2470 * pointer. This function is used to truncate or extend a file.
2472 * Return value: %TRUE on success, %FALSE otherwise.
2474 gboolean SetEndOfFile(gpointer fd_handle)
2476 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2477 WapiHandleType type;
2479 if (handle == NULL) {
2480 SetLastError (ERROR_INVALID_HANDLE);
2484 type = _wapi_handle_type (handle);
2486 if(io_ops[type].setendoffile==NULL) {
2487 SetLastError (ERROR_INVALID_HANDLE);
2491 return(io_ops[type].setendoffile (handle));
2496 * @handle: The file handle to set. The handle must have
2497 * %GENERIC_READ or %GENERIC_WRITE access.
2498 * @movedistance: Low 32 bits of a signed value that specifies the
2499 * number of bytes to move the file pointer.
2500 * @highmovedistance: Pointer to the high 32 bits of a signed value
2501 * that specifies the number of bytes to move the file pointer, or
2503 * @method: The starting point for the file pointer move.
2505 * Sets the file pointer of an open file.
2507 * The distance to move the file pointer is calculated from
2508 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2509 * @movedistance is the 32-bit signed value; otherwise, @movedistance
2510 * is the low 32 bits and @highmovedistance a pointer to the high 32
2511 * bits of a 64 bit signed value. A positive distance moves the file
2512 * pointer forward from the position specified by @method; a negative
2513 * distance moves the file pointer backward.
2515 * If the library is compiled without large file support,
2516 * @highmovedistance is ignored and its value is set to zero on a
2517 * successful return.
2519 * Return value: On success, the low 32 bits of the new file pointer.
2520 * If @highmovedistance is not %NULL, the high 32 bits of the new file
2521 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
2523 guint32 SetFilePointer(gpointer fd_handle, gint32 movedistance,
2524 gint32 *highmovedistance, WapiSeekMethod method)
2526 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2527 WapiHandleType type;
2529 if (handle == NULL) {
2530 SetLastError (ERROR_INVALID_HANDLE);
2531 return(INVALID_SET_FILE_POINTER);
2534 type = _wapi_handle_type (handle);
2536 if(io_ops[type].seek==NULL) {
2537 SetLastError (ERROR_INVALID_HANDLE);
2538 return(INVALID_SET_FILE_POINTER);
2541 return(io_ops[type].seek (handle, movedistance, highmovedistance,
2547 * @handle: The file handle to test.
2549 * Finds the type of file @handle.
2551 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2552 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
2553 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2554 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2556 WapiFileType GetFileType(gpointer fd_handle)
2558 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2559 WapiHandleType type;
2561 if (handle == NULL) {
2562 SetLastError (ERROR_INVALID_HANDLE);
2563 return(FILE_TYPE_UNKNOWN);
2566 type = _wapi_handle_type (handle);
2568 if(io_ops[type].getfiletype==NULL) {
2569 SetLastError (ERROR_INVALID_HANDLE);
2570 return(FILE_TYPE_UNKNOWN);
2573 return(io_ops[type].getfiletype ());
2578 * @handle: The file handle to query. The handle must have
2579 * %GENERIC_READ or %GENERIC_WRITE access.
2580 * @highsize: If non-%NULL, the high 32 bits of the file size are
2583 * Retrieves the size of the file @handle.
2585 * If the library is compiled without large file support, @highsize
2586 * has its value set to zero on a successful return.
2588 * Return value: On success, the low 32 bits of the file size. If
2589 * @highsize is non-%NULL then the high 32 bits of the file size are
2590 * stored here. On failure %INVALID_FILE_SIZE is returned.
2592 guint32 GetFileSize(gpointer fd_handle, guint32 *highsize)
2594 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2595 WapiHandleType type;
2597 if (handle == NULL) {
2598 SetLastError (ERROR_INVALID_HANDLE);
2599 return(INVALID_FILE_SIZE);
2602 type = _wapi_handle_type (handle);
2604 if(io_ops[type].getfilesize==NULL) {
2605 SetLastError (ERROR_INVALID_HANDLE);
2606 return(INVALID_FILE_SIZE);
2609 return(io_ops[type].getfilesize (handle, highsize));
2614 * @handle: The file handle to query. The handle must have
2615 * %GENERIC_READ access.
2616 * @create_time: Points to a %WapiFileTime structure to receive the
2617 * number of ticks since the epoch that file was created. May be
2619 * @last_access: Points to a %WapiFileTime structure to receive the
2620 * number of ticks since the epoch when file was last accessed. May be
2622 * @last_write: Points to a %WapiFileTime structure to receive the
2623 * number of ticks since the epoch when file was last written to. May
2626 * Finds the number of ticks since the epoch that the file referenced
2627 * by @handle was created, last accessed and last modified. A tick is
2628 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2631 * Create time isn't recorded on POSIX file systems or reported by
2632 * stat(2), so that time is guessed by returning the oldest of the
2635 * Return value: %TRUE on success, %FALSE otherwise.
2637 gboolean GetFileTime(gpointer fd_handle, WapiFileTime *create_time,
2638 WapiFileTime *last_access, WapiFileTime *last_write)
2640 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2641 WapiHandleType type;
2643 if (handle == NULL) {
2644 SetLastError (ERROR_INVALID_HANDLE);
2648 type = _wapi_handle_type (handle);
2650 if(io_ops[type].getfiletime==NULL) {
2651 SetLastError (ERROR_INVALID_HANDLE);
2655 return(io_ops[type].getfiletime (handle, create_time, last_access,
2661 * @handle: The file handle to set. The handle must have
2662 * %GENERIC_WRITE access.
2663 * @create_time: Points to a %WapiFileTime structure that contains the
2664 * number of ticks since the epoch that the file was created. May be
2666 * @last_access: Points to a %WapiFileTime structure that contains the
2667 * number of ticks since the epoch when the file was last accessed.
2669 * @last_write: Points to a %WapiFileTime structure that contains the
2670 * number of ticks since the epoch when the file was last written to.
2673 * Sets the number of ticks since the epoch that the file referenced
2674 * by @handle was created, last accessed or last modified. A tick is
2675 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2678 * Create time isn't recorded on POSIX file systems, and is ignored.
2680 * Return value: %TRUE on success, %FALSE otherwise.
2682 gboolean SetFileTime(gpointer fd_handle, const WapiFileTime *create_time,
2683 const WapiFileTime *last_access,
2684 const WapiFileTime *last_write)
2686 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2687 WapiHandleType type;
2689 if (handle == NULL) {
2690 SetLastError (ERROR_INVALID_HANDLE);
2694 type = _wapi_handle_type (handle);
2696 if(io_ops[type].setfiletime==NULL) {
2697 SetLastError (ERROR_INVALID_HANDLE);
2701 return(io_ops[type].setfiletime (handle, create_time, last_access,
2705 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2706 * January 1 1601 GMT
2709 #define TICKS_PER_MILLISECOND 10000L
2710 #define TICKS_PER_SECOND 10000000L
2711 #define TICKS_PER_MINUTE 600000000L
2712 #define TICKS_PER_HOUR 36000000000LL
2713 #define TICKS_PER_DAY 864000000000LL
2715 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2717 static const guint16 mon_yday[2][13]={
2718 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2719 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2723 * FileTimeToSystemTime:
2724 * @file_time: Points to a %WapiFileTime structure that contains the
2725 * number of ticks to convert.
2726 * @system_time: Points to a %WapiSystemTime structure to receive the
2729 * Converts a tick count into broken-out time values.
2731 * Return value: %TRUE on success, %FALSE otherwise.
2733 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2734 WapiSystemTime *system_time)
2736 gint64 file_ticks, totaldays, rem, y;
2739 if(system_time==NULL) {
2741 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2744 SetLastError (ERROR_INVALID_PARAMETER);
2748 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2749 file_time->dwLowDateTime;
2751 /* Really compares if file_ticks>=0x8000000000000000
2752 * (LLONG_MAX+1) but we're working with a signed value for the
2753 * year and day calculation to work later
2757 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2760 SetLastError (ERROR_INVALID_PARAMETER);
2764 totaldays=(file_ticks / TICKS_PER_DAY);
2765 rem = file_ticks % TICKS_PER_DAY;
2767 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2771 system_time->wHour=rem/TICKS_PER_HOUR;
2772 rem %= TICKS_PER_HOUR;
2774 g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2775 system_time->wHour, rem);
2778 system_time->wMinute = rem / TICKS_PER_MINUTE;
2779 rem %= TICKS_PER_MINUTE;
2781 g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2782 system_time->wMinute, rem);
2785 system_time->wSecond = rem / TICKS_PER_SECOND;
2786 rem %= TICKS_PER_SECOND;
2788 g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2789 system_time->wSecond, rem);
2792 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2794 g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2795 system_time->wMilliseconds);
2798 /* January 1, 1601 was a Monday, according to Emacs calendar */
2799 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2801 g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2802 system_time->wDayOfWeek);
2805 /* This algorithm to find year and month given days from epoch
2810 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2811 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2813 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2814 /* Guess a corrected year, assuming 365 days per year */
2815 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2817 g_message(G_GNUC_PRETTY_FUNCTION
2818 ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2820 g_message(G_GNUC_PRETTY_FUNCTION
2821 ": LEAPS(yg): %lld LEAPS(y): %lld",
2822 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2825 /* Adjust days and y to match the guessed year. */
2826 totaldays -= ((yg - y) * 365
2827 + LEAPS_THRU_END_OF (yg - 1)
2828 - LEAPS_THRU_END_OF (y - 1));
2830 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2835 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2839 system_time->wYear = y;
2841 g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2844 ip = mon_yday[isleap(y)];
2846 for(y=11; totaldays < ip[y]; --y) {
2851 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2854 system_time->wMonth = y + 1;
2856 g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2859 system_time->wDay = totaldays + 1;
2861 g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2868 file_compare (gconstpointer a, gconstpointer b)
2870 gchar *astr = *(gchar **) a;
2871 gchar *bstr = *(gchar **) b;
2873 return strcmp (astr, bstr);
2877 get_errno_from_g_file_error (gint error)
2881 case G_FILE_ERROR_ACCES:
2886 case G_FILE_ERROR_NAMETOOLONG:
2887 error = ENAMETOOLONG;
2891 case G_FILE_ERROR_NOENT:
2896 case G_FILE_ERROR_NOTDIR:
2901 case G_FILE_ERROR_NXIO:
2906 case G_FILE_ERROR_NODEV:
2911 case G_FILE_ERROR_ROFS:
2916 case G_FILE_ERROR_TXTBSY:
2921 case G_FILE_ERROR_FAULT:
2926 case G_FILE_ERROR_LOOP:
2931 case G_FILE_ERROR_NOSPC:
2936 case G_FILE_ERROR_NOMEM:
2941 case G_FILE_ERROR_MFILE:
2946 case G_FILE_ERROR_NFILE:
2951 case G_FILE_ERROR_BADF:
2956 case G_FILE_ERROR_INVAL:
2961 case G_FILE_ERROR_PIPE:
2966 case G_FILE_ERROR_AGAIN:
2971 case G_FILE_ERROR_INTR:
2976 case G_FILE_ERROR_IO:
2981 case G_FILE_ERROR_PERM:
2985 case G_FILE_ERROR_FAILED:
2986 error = ERROR_INVALID_PARAMETER;
2993 /* scandir using glib */
2995 mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
2997 GError *error = NULL;
3002 GPatternSpec *patspec;
3004 dir = g_dir_open (dirname, 0, &error);
3006 /* g_dir_open returns ENOENT on directories on which we don't
3007 * have read/x permission */
3008 gint errnum = get_errno_from_g_file_error (error->code);
3009 g_error_free (error);
3010 if (errnum == ENOENT && g_file_test (dirname, G_FILE_TEST_IS_DIR))
3017 patspec = g_pattern_spec_new (pattern);
3018 names = g_ptr_array_new ();
3019 while ((name = g_dir_read_name (dir)) != NULL) {
3020 if (g_pattern_match_string (patspec, name))
3021 g_ptr_array_add (names, g_strdup (name));
3024 g_pattern_spec_free (patspec);
3026 result = names->len;
3028 g_ptr_array_sort (names, file_compare);
3029 g_ptr_array_set_size (names, result + 1);
3031 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
3033 g_ptr_array_free (names, TRUE);
3039 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
3041 struct _WapiHandlePrivate_find *find_handle;
3042 gpointer handle, find_ret = INVALID_HANDLE_VALUE;
3044 gchar *utf8_pattern = NULL, *dir_part, *entry_part;
3047 gboolean unref = FALSE;
3049 if (pattern == NULL) {
3051 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
3054 SetLastError (ERROR_PATH_NOT_FOUND);
3055 return(INVALID_HANDLE_VALUE);
3058 utf8_pattern = mono_unicode_to_external (pattern);
3059 if (utf8_pattern == NULL) {
3061 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3064 SetLastError (ERROR_INVALID_NAME);
3065 return(INVALID_HANDLE_VALUE);
3069 g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
3073 /* Figure out which bit of the pattern is the directory */
3074 dir_part=g_path_get_dirname (utf8_pattern);
3075 entry_part=g_path_get_basename (utf8_pattern);
3078 /* Don't do this check for now, it breaks if directories
3079 * really do have metachars in their names (see bug 58116).
3080 * FIXME: Figure out a better solution to keep some checks...
3082 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3083 SetLastError (ERROR_INVALID_NAME);
3085 g_free (entry_part);
3086 g_free (utf8_pattern);
3087 return(INVALID_HANDLE_VALUE);
3091 handle=_wapi_handle_new (WAPI_HANDLE_FIND);
3092 if(handle==_WAPI_HANDLE_INVALID) {
3093 g_warning (G_GNUC_PRETTY_FUNCTION ": error creating find handle");
3095 g_free (entry_part);
3096 g_free (utf8_pattern);
3097 SetLastError (ERROR_GEN_FAILURE);
3099 return(INVALID_HANDLE_VALUE);
3102 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3104 thr_ret = _wapi_handle_lock_handle (handle);
3105 g_assert (thr_ret == 0);
3107 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3108 (gpointer *)&find_handle);
3110 g_warning (G_GNUC_PRETTY_FUNCTION
3111 ": error looking up find handle %p", handle);
3114 g_free (entry_part);
3116 g_free (utf8_pattern);
3117 utf8_pattern = NULL;
3118 SetLastError (ERROR_INVALID_HANDLE);
3122 /* The pattern can specify a directory or a set of files.
3124 * The pattern can have wildcard characters ? and *, but only
3125 * in the section after the last directory delimiter. (Return
3126 * ERROR_INVALID_NAME if there are wildcards in earlier path
3127 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3128 * means "match one character", "??" seems to mean "match one
3129 * or two characters", "???" seems to mean "match one, two or
3130 * three characters", etc. Windows will also try and match
3131 * the mangled "short name" of files, so 8 character patterns
3132 * with wildcards will show some surprising results.
3134 * All the written documentation I can find says that '?'
3135 * should only match one character, and doesn't mention '??',
3136 * '???' etc. I'm going to assume that the strict behaviour
3137 * (ie '???' means three and only three characters) is the
3138 * correct one, because that lets me use fnmatch(3) rather
3139 * than mess around with regexes.
3142 find_handle->namelist = NULL;
3143 result = mono_io_scandir (dir_part, entry_part, &find_handle->namelist);
3147 gint errnum = errno;
3149 _wapi_set_last_error_from_errno ();
3151 g_message (G_GNUC_PRETTY_FUNCTION ": scandir error: %s", g_strerror (errnum));
3153 g_free (utf8_pattern);
3154 g_free (entry_part);
3160 g_free (utf8_pattern);
3161 g_free (entry_part);
3164 g_message (G_GNUC_PRETTY_FUNCTION ": Got %d matches", result);
3167 find_handle->dir_part = dir_part;
3168 find_handle->num = result;
3169 find_handle->count = 0;
3174 thr_ret = _wapi_handle_unlock_handle (handle);
3175 g_assert (thr_ret == 0);
3176 pthread_cleanup_pop (0);
3178 /* FindNextFile has to be called after unlocking the handle,
3179 * because it wants to lock the handle itself
3181 if (find_ret != INVALID_HANDLE_VALUE &&
3182 !FindNextFile (handle, find_data)) {
3184 SetLastError (ERROR_NO_MORE_FILES);
3185 find_ret = INVALID_HANDLE_VALUE;
3188 /* Must not call _wapi_handle_unref() with the handle already
3192 _wapi_handle_unref (handle);
3198 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
3200 struct _WapiHandlePrivate_find *find_handle;
3204 gchar *utf8_filename, *utf8_basename;
3205 gunichar2 *utf16_basename;
3209 gboolean ret = FALSE;
3211 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3212 (gpointer *)&find_handle);
3214 g_warning (G_GNUC_PRETTY_FUNCTION
3215 ": error looking up find handle %p", handle);
3216 SetLastError (ERROR_INVALID_HANDLE);
3220 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3222 thr_ret = _wapi_handle_lock_handle (handle);
3223 g_assert (thr_ret == 0);
3226 if (find_handle->count >= find_handle->num) {
3227 SetLastError (ERROR_NO_MORE_FILES);
3231 /* stat next match */
3233 filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3234 if (lstat (filename, &buf) != 0) {
3236 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
3243 /* Check for dangling symlinks, and ignore them (principle of
3244 * least surprise, avoiding confusion where we report the file
3245 * exists, but when someone tries to open it we would report
3248 if(S_ISLNK (buf.st_mode)) {
3249 if(stat (filename, &buf) != 0) {
3255 utf8_filename=mono_utf8_from_external (filename);
3256 if(utf8_filename==NULL) {
3257 /* We couldn't turn this filename into utf8 (eg the
3258 * encoding of the name wasn't convertible), so just
3267 g_message (G_GNUC_PRETTY_FUNCTION ": Found [%s]", utf8_filename);
3270 /* fill data block */
3272 if (buf.st_mtime < buf.st_ctime)
3273 create_time = buf.st_mtime;
3275 create_time = buf.st_ctime;
3277 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3279 _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
3280 _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3281 _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3283 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3284 find_data->nFileSizeHigh = 0;
3285 find_data->nFileSizeLow = 0;
3288 find_data->nFileSizeHigh = buf.st_size >> 32;
3289 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3292 find_data->dwReserved0 = 0;
3293 find_data->dwReserved1 = 0;
3295 utf8_basename = g_path_get_basename (utf8_filename);
3296 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3298 if(utf16_basename==NULL) {
3299 g_free (utf8_basename);
3300 g_free (utf8_filename);
3305 /* utf16 is 2 * utf8 */
3308 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3310 /* Truncating a utf16 string like this might leave the last
3313 memcpy (find_data->cFileName, utf16_basename,
3314 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3316 find_data->cAlternateFileName [0] = 0; /* not used */
3318 g_free (utf8_basename);
3319 g_free (utf8_filename);
3320 g_free (utf16_basename);
3323 thr_ret = _wapi_handle_unlock_handle (handle);
3324 g_assert (thr_ret == 0);
3325 pthread_cleanup_pop (0);
3332 * @wapi_handle: the find handle to close.
3334 * Closes find handle @wapi_handle
3336 * Return value: %TRUE on success, %FALSE otherwise.
3338 gboolean FindClose (gpointer handle)
3340 struct _WapiHandlePrivate_find *find_handle;
3344 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3345 (gpointer *)&find_handle);
3347 g_warning (G_GNUC_PRETTY_FUNCTION
3348 ": error looking up find handle %p", handle);
3349 SetLastError (ERROR_INVALID_HANDLE);
3353 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3355 thr_ret = _wapi_handle_lock_handle (handle);
3356 g_assert (thr_ret == 0);
3358 g_strfreev (find_handle->namelist);
3359 g_free (find_handle->dir_part);
3361 thr_ret = _wapi_handle_unlock_handle (handle);
3362 g_assert (thr_ret == 0);
3363 pthread_cleanup_pop (0);
3365 _wapi_handle_unref (handle);
3372 * @name: a pointer to a NULL-terminated unicode string, that names
3373 * the directory to be created.
3374 * @security: ignored for now
3376 * Creates directory @name
3378 * Return value: %TRUE on success, %FALSE otherwise.
3380 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
3389 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3392 SetLastError (ERROR_INVALID_NAME);
3396 utf8_name = mono_unicode_to_external (name);
3397 if (utf8_name == NULL) {
3399 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3402 SetLastError (ERROR_INVALID_NAME);
3406 result = mkdir (utf8_name, 0777);
3413 if (errno == EEXIST) {
3414 result = stat (utf8_name, &buf);
3416 _wapi_set_last_error_from_errno ();
3422 attrs = _wapi_stat_to_file_attributes (&buf);
3423 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
3427 _wapi_set_last_error_from_errno ();
3431 _wapi_set_last_error_from_errno ();
3438 * @name: a pointer to a NULL-terminated unicode string, that names
3439 * the directory to be removed.
3441 * Removes directory @name
3443 * Return value: %TRUE on success, %FALSE otherwise.
3445 gboolean RemoveDirectory (const gunichar2 *name)
3452 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3455 SetLastError (ERROR_INVALID_NAME);
3459 utf8_name = mono_unicode_to_external (name);
3460 if (utf8_name == NULL) {
3462 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3465 SetLastError (ERROR_INVALID_NAME);
3469 result = rmdir (utf8_name);
3475 _wapi_set_last_error_from_errno ();
3480 * GetFileAttributes:
3481 * @name: a pointer to a NULL-terminated unicode filename.
3483 * Gets the attributes for @name;
3485 * Return value: %INVALID_FILE_ATTRIBUTES on failure
3487 guint32 GetFileAttributes (const gunichar2 *name)
3495 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3498 SetLastError (ERROR_INVALID_NAME);
3502 utf8_name = mono_unicode_to_external (name);
3503 if (utf8_name == NULL) {
3505 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3508 SetLastError (ERROR_INVALID_PARAMETER);
3509 return (INVALID_FILE_ATTRIBUTES);
3512 result = stat (utf8_name, &buf);
3515 _wapi_set_last_error_from_errno ();
3517 return (INVALID_FILE_ATTRIBUTES);
3521 return _wapi_stat_to_file_attributes (&buf);
3525 * GetFileAttributesEx:
3526 * @name: a pointer to a NULL-terminated unicode filename.
3527 * @level: must be GetFileExInfoStandard
3528 * @info: pointer to a WapiFileAttributesData structure
3530 * Gets attributes, size and filetimes for @name;
3532 * Return value: %TRUE on success, %FALSE on failure
3534 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3537 WapiFileAttributesData *data;
3543 if (level != GetFileExInfoStandard) {
3545 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
3548 SetLastError (ERROR_INVALID_PARAMETER);
3554 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3557 SetLastError (ERROR_INVALID_NAME);
3561 utf8_name = mono_unicode_to_external (name);
3562 if (utf8_name == NULL) {
3564 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3567 SetLastError (ERROR_INVALID_PARAMETER);
3571 result = stat (utf8_name, &buf);
3575 SetLastError (ERROR_FILE_NOT_FOUND);
3579 /* fill data block */
3581 data = (WapiFileAttributesData *)info;
3583 if (buf.st_mtime < buf.st_ctime)
3584 create_time = buf.st_mtime;
3586 create_time = buf.st_ctime;
3588 data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3590 _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3591 _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3592 _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3594 if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3595 data->nFileSizeHigh = 0;
3596 data->nFileSizeLow = 0;
3599 data->nFileSizeHigh = buf.st_size >> 32;
3600 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3608 * @name: name of file
3609 * @attrs: attributes to set
3611 * Changes the attributes on a named file.
3613 * Return value: %TRUE on success, %FALSE on failure.
3615 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3617 /* FIXME: think of something clever to do on unix */
3623 * Currently we only handle one *internal* case, with a value that is
3624 * not standard: 0x80000000, which means `set executable bit'
3629 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3632 SetLastError (ERROR_INVALID_NAME);
3636 utf8_name = mono_unicode_to_external (name);
3637 if (utf8_name == NULL) {
3639 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3642 SetLastError (ERROR_INVALID_NAME);
3646 result = stat (utf8_name, &buf);
3649 SetLastError (ERROR_FILE_NOT_FOUND);
3653 /* Contrary to the documentation, ms allows NORMAL to be
3654 * specified along with other attributes, so dont bother to
3655 * catch that case here.
3657 if (attrs & FILE_ATTRIBUTE_READONLY) {
3658 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3660 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
3663 /* Ignore the other attributes for now */
3665 if (attrs & 0x80000000){
3666 mode_t exec_mask = 0;
3668 if ((buf.st_mode & S_IRUSR) != 0)
3669 exec_mask |= S_IXUSR;
3671 if ((buf.st_mode & S_IRGRP) != 0)
3672 exec_mask |= S_IXGRP;
3674 if ((buf.st_mode & S_IROTH) != 0)
3675 exec_mask |= S_IXOTH;
3677 result = chmod (utf8_name, buf.st_mode | exec_mask);
3679 /* Don't bother to reset executable (might need to change this
3689 * GetCurrentDirectory
3690 * @length: size of the buffer
3691 * @buffer: pointer to buffer that recieves path
3693 * Retrieves the current directory for the current process.
3695 * Return value: number of characters in buffer on success, zero on failure
3697 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3700 gunichar2 *utf16_path;
3704 path = g_get_current_dir ();
3708 utf16_path=mono_unicode_from_external (path, &bytes);
3710 /* if buffer too small, return number of characters required.
3711 * this is plain dumb.
3714 count = (bytes/2)+1;
3715 if (count > length) {
3717 g_free (utf16_path);
3722 /* Add the terminator */
3723 memset (buffer, '\0', bytes+2);
3724 memcpy (buffer, utf16_path, bytes);
3726 g_free (utf16_path);
3733 * SetCurrentDirectory
3734 * @path: path to new directory
3736 * Changes the directory path for the current process.
3738 * Return value: %TRUE on success, %FALSE on failure.
3740 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3745 utf8_path = mono_unicode_to_external (path);
3746 if (chdir (utf8_path) != 0) {
3747 _wapi_set_last_error_from_errno ();
3757 /* When we're confident there are no more bugs in the fd->handle
3758 * mapping, this can be replaced as a no-op: GPOINTER_TO_INT(fd_handle) == fd
3760 int _wapi_file_handle_to_fd (gpointer fd_handle)
3762 struct _WapiHandlePrivate_file *file_private_handle;
3764 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3767 g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
3770 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
3771 (gpointer *)&file_private_handle);
3773 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
3774 (gpointer *)&file_private_handle);
3776 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
3777 (gpointer *)&file_private_handle);
3780 g_message (G_GNUC_PRETTY_FUNCTION
3783 SetLastError (ERROR_INVALID_HANDLE);
3789 if (file_private_handle->fd_mapped.assigned == FALSE) {
3790 SetLastError (ERROR_INVALID_HANDLE);
3795 g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
3796 file_private_handle->fd_mapped.fd);
3799 g_assert (file_private_handle->fd_mapped.fd == GPOINTER_TO_INT (fd_handle));
3801 return(file_private_handle->fd_mapped.fd);
3804 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3805 WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3807 struct _WapiHandle_file *pipe_read_handle;
3808 struct _WapiHandle_file *pipe_write_handle;
3809 struct _WapiHandlePrivate_file *pipe_read_private_handle;
3810 struct _WapiHandlePrivate_file *pipe_write_private_handle;
3811 gpointer read_handle;
3812 gpointer write_handle;
3817 gboolean unref_read = FALSE, unref_write = FALSE;
3818 gboolean cp_ret = FALSE;
3820 mono_once (&io_ops_once, io_ops_init);
3823 g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
3829 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
3833 _wapi_set_last_error_from_errno ();
3837 if (filedes[0] >= _wapi_fd_offset_table_size ||
3838 filedes[1] >= _wapi_fd_offset_table_size) {
3840 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
3843 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3851 /* filedes[0] is open for reading, filedes[1] for writing */
3853 read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3854 if(read_handle==_WAPI_HANDLE_INVALID) {
3855 g_warning (G_GNUC_PRETTY_FUNCTION
3856 ": error creating pipe read handle");
3859 SetLastError (ERROR_GEN_FAILURE);
3864 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3866 thr_ret = _wapi_handle_lock_handle (read_handle);
3867 g_assert (thr_ret == 0);
3869 ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
3870 (gpointer *)&pipe_read_handle,
3871 (gpointer *)&pipe_read_private_handle);
3873 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3876 SetLastError (ERROR_INVALID_HANDLE);
3880 write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3881 if(write_handle==_WAPI_HANDLE_INVALID) {
3882 g_warning (G_GNUC_PRETTY_FUNCTION
3883 ": error creating pipe write handle");
3888 SetLastError (ERROR_GEN_FAILURE);
3893 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3895 thr_ret = _wapi_handle_lock_handle (write_handle);
3896 g_assert (thr_ret == 0);
3898 ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
3899 (gpointer *)&pipe_write_handle,
3900 (gpointer *)&pipe_write_private_handle);
3902 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3908 SetLastError (ERROR_INVALID_HANDLE);
3913 pipe_read_private_handle->fd_mapped.fd=filedes[0];
3914 pipe_read_private_handle->fd_mapped.assigned=TRUE;
3915 pipe_read_handle->fileaccess=GENERIC_READ;
3917 _wapi_handle_fd_offset_store (filedes[0], read_handle);
3918 *readpipe=GINT_TO_POINTER (filedes[0]);
3920 pipe_write_private_handle->fd_mapped.fd=filedes[1];
3921 pipe_write_private_handle->fd_mapped.assigned=TRUE;
3922 pipe_write_handle->fileaccess=GENERIC_WRITE;
3924 _wapi_handle_fd_offset_store (filedes[1], write_handle);
3925 *writepipe=GINT_TO_POINTER (filedes[1]);
3928 g_message (G_GNUC_PRETTY_FUNCTION ": Returning pipe: read handle %p (fd %d), write handle %p (fd %d)", read_handle, filedes[0], write_handle, filedes[1]);
3932 thr_ret =_wapi_handle_unlock_handle (write_handle);
3933 g_assert (thr_ret == 0);
3934 pthread_cleanup_pop (0);
3937 _wapi_handle_unref (write_handle);
3941 thr_ret =_wapi_handle_unlock_handle (read_handle);
3942 g_assert (thr_ret == 0);
3943 pthread_cleanup_pop (0);
3945 /* Must not call _wapi_handle_unref() with the handle already
3949 _wapi_handle_unref (read_handle);
3955 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3957 gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3958 gunichar2 *tmpdir16=NULL;
3963 if(tmpdir[strlen (tmpdir)]!='/') {
3965 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3968 tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3969 if(tmpdir16==NULL) {
3977 g_message (G_GNUC_PRETTY_FUNCTION
3978 ": Size %d smaller than needed (%ld)", len,
3984 /* Add the terminator */
3985 memset (buf, '\0', bytes+2);
3986 memcpy (buf, tmpdir16, bytes);
3992 if(tmpdir16!=NULL) {
4001 _wapi_io_add_callback (gpointer fd_handle,
4002 WapiOverlappedCB callback,
4003 guint64 flags G_GNUC_UNUSED)
4005 struct _WapiHandle_file *file_handle;
4006 struct _WapiHandlePrivate_file *file_private_handle;
4009 gboolean ret = FALSE;
4010 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4012 if (handle == NULL) {
4013 SetLastError (ERROR_INVALID_HANDLE);
4017 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4018 (gpointer *) &file_handle,
4019 (gpointer *) &file_private_handle);
4022 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
4023 (gpointer *) &file_handle,
4024 (gpointer *) &file_private_handle);
4028 if (ok == FALSE || file_private_handle->async == FALSE) {
4029 SetLastError (ERROR_INVALID_HANDLE);
4033 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
4035 thr_ret = _wapi_handle_lock_handle (handle);
4036 g_assert (thr_ret == 0);
4038 if (file_private_handle->callback != NULL) {
4039 SetLastError (ERROR_INVALID_PARAMETER);
4044 file_private_handle->callback = callback;
4047 thr_ret = _wapi_handle_unlock_handle (handle);
4048 g_assert (thr_ret == 0);
4049 pthread_cleanup_pop (0);
4055 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
4058 gunichar2 *ptr, *dir;
4059 glong length, total = 0;
4063 memset (buf, 0, sizeof (gunichar2) * (len + 1));
4068 /* Sigh, mntent and friends don't work well.
4069 * It stops on the first line that doesn't begin with a '/'.
4070 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4071 fp = fopen ("/etc/mtab", "rt");
4073 fp = fopen ("/etc/mnttab", "rt");
4079 while (fgets (buffer, 512, fp) != NULL) {
4083 splitted = g_strsplit (buffer, " ", 0);
4084 if (!*splitted || !*(splitted + 1))
4087 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
4088 g_strfreev (splitted);
4089 if (total + length + 1 > len) {
4090 return len * 2; /* guess */
4093 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4095 total += length + 1;
4100 /* Commented out, does not work with my mtab!!! - Gonz */
4101 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4105 gunichar2 *ptr, *dir;
4106 glong len, total = 0;
4109 fp = setmntent ("/etc/mtab", "rt");
4111 fp = setmntent ("/etc/mnttab", "rt");
4117 while ((mnt = getmntent (fp)) != NULL) {
4118 g_print ("GOT %s\n", mnt->mnt_dir);
4119 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4120 if (total + len + 1 > len) {
4121 return len * 2; /* guess */
4124 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4135 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
4137 struct flock lock_data;
4140 lock_data.l_type = F_WRLCK;
4141 lock_data.l_whence = SEEK_SET;
4142 lock_data.l_start = offset;
4143 lock_data.l_len = length;
4146 ret = fcntl (fd, F_SETLK, &lock_data);
4148 while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
4151 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4155 SetLastError (ERROR_LOCK_VIOLATION);
4162 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
4164 struct flock lock_data;
4167 lock_data.l_type = F_UNLCK;
4168 lock_data.l_whence = SEEK_SET;
4169 lock_data.l_start = offset;
4170 lock_data.l_len = length;
4173 ret = fcntl (fd, F_SETLK, &lock_data);
4175 while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
4178 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4182 SetLastError (ERROR_LOCK_VIOLATION);
4189 gboolean LockFile (gpointer fd_handle, guint32 offset_low, guint32 offset_high,
4190 guint32 length_low, guint32 length_high)
4192 struct _WapiHandle_file *file_handle;
4193 struct _WapiHandlePrivate_file *file_private_handle;
4195 off_t offset, length;
4196 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4198 if (handle == NULL) {
4199 SetLastError (ERROR_INVALID_HANDLE);
4203 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4204 (gpointer *)&file_handle,
4205 (gpointer *)&file_private_handle);
4207 g_warning (G_GNUC_PRETTY_FUNCTION
4208 ": error looking up file handle %p", handle);
4209 SetLastError (ERROR_INVALID_HANDLE);
4213 if (file_private_handle->fd_mapped.assigned == FALSE) {
4214 SetLastError (ERROR_INVALID_HANDLE);
4218 if (!(file_handle->fileaccess & GENERIC_READ) &&
4219 !(file_handle->fileaccess & GENERIC_WRITE) &&
4220 !(file_handle->fileaccess & GENERIC_ALL)) {
4222 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
4224 SetLastError (ERROR_ACCESS_DENIED);
4228 #ifdef HAVE_LARGE_FILE_SUPPORT
4229 offset = ((gint64)offset_high << 32) | offset_low;
4230 length = ((gint64)length_high << 32) | length_low;
4233 g_message (G_GNUC_PRETTY_FUNCTION
4234 ": Locking handle %p fd %d, offset %lld, length %lld",
4235 handle, file_private_handle->fd_mapped.fd, offset, length);
4238 offset = offset_low;
4239 length = length_low;
4242 g_message (G_GNUC_PRETTY_FUNCTION
4243 ": Locking handle %p fd %d, offset %ld, length %ld",
4244 handle, file_private_handle->fd_mapped.fd, offset, length);
4248 return(_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
4252 gboolean UnlockFile (gpointer fd_handle, guint32 offset_low,
4253 guint32 offset_high, guint32 length_low,
4254 guint32 length_high)
4256 struct _WapiHandle_file *file_handle;
4257 struct _WapiHandlePrivate_file *file_private_handle;
4259 off_t offset, length;
4260 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4262 if (handle == NULL) {
4263 SetLastError (ERROR_INVALID_HANDLE);
4267 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4268 (gpointer *)&file_handle,
4269 (gpointer *)&file_private_handle);
4271 g_warning (G_GNUC_PRETTY_FUNCTION
4272 ": error looking up file handle %p", handle);
4273 SetLastError (ERROR_INVALID_HANDLE);
4277 if (file_private_handle->fd_mapped.assigned == FALSE) {
4278 SetLastError (ERROR_INVALID_HANDLE);
4282 if (!(file_handle->fileaccess & GENERIC_READ) &&
4283 !(file_handle->fileaccess & GENERIC_WRITE) &&
4284 !(file_handle->fileaccess & GENERIC_ALL)) {
4286 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
4288 SetLastError (ERROR_ACCESS_DENIED);
4292 #ifdef HAVE_LARGE_FILE_SUPPORT
4293 offset = ((gint64)offset_high << 32) | offset_low;
4294 length = ((gint64)length_high << 32) | length_low;
4297 g_message (G_GNUC_PRETTY_FUNCTION
4298 ": Unlocking handle %p fd %d, offset %lld, length %lld",
4299 handle, file_private_handle->fd_mapped.fd, offset, length);
4302 offset = offset_low;
4303 length = length_low;
4306 g_message (G_GNUC_PRETTY_FUNCTION
4307 ": Unlocking handle %p fd %d, offset %ld, length %ld",
4308 handle, file_private_handle->fd_mapped.fd, offset, length);
4312 return(_wapi_unlock_file_region (file_private_handle->fd_mapped.fd,