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 */
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
1939 * Return value: %TRUE on success, %FALSE otherwise.
1941 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1943 gchar *utf8_name, *utf8_dest_name;
1948 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1951 SetLastError (ERROR_INVALID_NAME);
1955 utf8_name = mono_unicode_to_external (name);
1956 if (utf8_name == NULL) {
1958 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1961 SetLastError (ERROR_INVALID_NAME);
1965 if(dest_name==NULL) {
1967 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1971 SetLastError (ERROR_INVALID_NAME);
1975 utf8_dest_name = mono_unicode_to_external (dest_name);
1976 if (utf8_dest_name == NULL) {
1978 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1982 SetLastError (ERROR_INVALID_NAME);
1986 result = rename (utf8_name, utf8_dest_name);
1988 g_free (utf8_dest_name);
1990 if (result != 0 && errno == EXDEV) {
1991 /* Try a copy to the new location, and delete the source */
1992 if (CopyFile (name, dest_name, TRUE)==FALSE) {
1993 /* CopyFile will set the error */
1997 return(DeleteFile (name));
2006 SetLastError (ERROR_ALREADY_EXISTS);
2010 _wapi_set_last_error_from_errno ();
2019 * @name: a pointer to a NULL-terminated unicode string, that names
2020 * the file to be copied.
2021 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
2022 * new name for the file.
2023 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
2025 * Copies file @name to @dest_name
2027 * Return value: %TRUE on success, %FALSE otherwise.
2029 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
2030 gboolean fail_if_exists)
2032 gchar *utf8_src, *utf8_dest;
2033 int src_fd, dest_fd;
2041 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2044 SetLastError (ERROR_INVALID_NAME);
2048 utf8_src = mono_unicode_to_external (name);
2049 if (utf8_src == NULL) {
2051 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of source returned NULL");
2054 SetLastError (ERROR_INVALID_PARAMETER);
2058 if(dest_name==NULL) {
2060 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2064 SetLastError (ERROR_INVALID_NAME);
2068 utf8_dest = mono_unicode_to_external (dest_name);
2069 if (utf8_dest == NULL) {
2071 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of dest returned NULL");
2074 SetLastError (ERROR_INVALID_PARAMETER);
2081 src_fd = open (utf8_src, O_RDONLY);
2083 _wapi_set_last_error_from_errno ();
2091 if (fstat (src_fd, &st) < 0) {
2092 _wapi_set_last_error_from_errno ();
2101 if (fail_if_exists) {
2102 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
2104 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2106 /* O_TRUNC might cause a fail if the file
2109 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
2114 _wapi_set_last_error_from_errno ();
2123 buf_size = st.st_blksize;
2124 buf = (char *) alloca (buf_size);
2127 remain = read (src_fd, buf, buf_size);
2130 if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
2134 _wapi_set_last_error_from_errno ();
2148 while (remain > 0) {
2149 if ((n = write (dest_fd, buf, remain)) < 0) {
2150 if (errno == EINTR && !_wapi_thread_cur_apc_pending())
2153 _wapi_set_last_error_from_errno ();
2155 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
2178 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
2179 static gpointer stdin_handle=INVALID_HANDLE_VALUE;
2180 static gpointer stdout_handle=INVALID_HANDLE_VALUE;
2181 static gpointer stderr_handle=INVALID_HANDLE_VALUE;
2183 static gpointer stdhandle_create (int fd, const guchar *name)
2185 struct _WapiHandle_file *file_handle;
2186 struct _WapiHandlePrivate_file *file_private_handle;
2188 gpointer handle, ret = INVALID_HANDLE_VALUE;
2193 g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
2197 /* Check if fd is valid */
2199 flags=fcntl(fd, F_GETFL);
2201 while (flags==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
2204 /* Invalid fd. Not really much point checking for EBADF
2208 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
2209 fd, strerror(errno));
2212 _wapi_set_last_error_from_errno ();
2213 return(INVALID_HANDLE_VALUE);
2216 handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
2217 if(handle==_WAPI_HANDLE_INVALID) {
2218 g_warning (G_GNUC_PRETTY_FUNCTION
2219 ": error creating file handle");
2220 SetLastError (ERROR_GEN_FAILURE);
2221 return(INVALID_HANDLE_VALUE);
2224 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2226 thr_ret = _wapi_handle_lock_handle (handle);
2227 g_assert (thr_ret == 0);
2229 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2230 (gpointer *)&file_handle,
2231 (gpointer *)&file_private_handle);
2233 g_warning (G_GNUC_PRETTY_FUNCTION
2234 ": error looking up console handle %p", handle);
2235 SetLastError (ERROR_INVALID_HANDLE);
2239 /* We know this is fd 0, 1 or 2 */
2240 _wapi_handle_fd_offset_store (fd, handle);
2241 ret = GINT_TO_POINTER (fd);
2243 file_private_handle->fd_mapped.fd=fd;
2244 file_private_handle->fd_mapped.assigned=TRUE;
2245 file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
2246 /* some default security attributes might be needed */
2247 file_handle->security_attributes=0;
2248 file_handle->fileaccess=convert_from_flags(flags);
2249 file_handle->sharemode=0;
2250 file_handle->attrs=0;
2253 g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
2254 handle, file_private_handle->fd_mapped.fd);
2258 thr_ret = _wapi_handle_unlock_handle (handle);
2259 g_assert (thr_ret == 0);
2260 pthread_cleanup_pop (0);
2265 static void stdhandle_init (void)
2267 stdin_handle=stdhandle_create (0, "<stdin>");
2268 stdout_handle=stdhandle_create (1, "<stdout>");
2269 stderr_handle=stdhandle_create (2, "<stderr>");
2274 * @stdhandle: specifies the file descriptor
2276 * Returns a handle for stdin, stdout, or stderr. Always returns the
2277 * same handle for the same @stdhandle.
2279 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2282 gpointer GetStdHandle(WapiStdHandle stdhandle)
2286 mono_once (&io_ops_once, io_ops_init);
2287 mono_once (&stdhandle_once, stdhandle_init);
2290 case STD_INPUT_HANDLE:
2291 handle=stdin_handle;
2294 case STD_OUTPUT_HANDLE:
2295 handle=stdout_handle;
2298 case STD_ERROR_HANDLE:
2299 handle=stderr_handle;
2304 g_message(G_GNUC_PRETTY_FUNCTION
2305 ": unknown standard handle type");
2308 SetLastError (ERROR_INVALID_PARAMETER);
2309 return(INVALID_HANDLE_VALUE);
2312 if (handle == INVALID_HANDLE_VALUE) {
2313 SetLastError (ERROR_NO_MORE_FILES);
2314 return(INVALID_HANDLE_VALUE);
2317 /* Add a reference to this handle */
2318 _wapi_handle_ref (_wapi_handle_fd_offset_to_handle (handle));
2325 * @handle: The file handle to read from. The handle must have
2326 * %GENERIC_READ access.
2327 * @buffer: The buffer to store read data in
2328 * @numbytes: The maximum number of bytes to read
2329 * @bytesread: The actual number of bytes read is stored here. This
2330 * value can be zero if the handle is positioned at the end of the
2332 * @overlapped: points to a required %WapiOverlapped structure if
2333 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2336 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2337 * function reads up to @numbytes bytes from the file from the current
2338 * file position, and stores them in @buffer. If there are not enough
2339 * bytes left in the file, just the amount available will be read.
2340 * The actual number of bytes read is stored in @bytesread.
2342 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2343 * file position is ignored and the read position is taken from data
2344 * in the @overlapped structure.
2346 * Return value: %TRUE if the read succeeds (even if no bytes were
2347 * read due to an attempt to read past the end of the file), %FALSE on
2350 gboolean ReadFile(gpointer fd_handle, gpointer buffer, guint32 numbytes,
2351 guint32 *bytesread, WapiOverlapped *overlapped)
2353 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2354 WapiHandleType type;
2356 if (handle == NULL) {
2357 SetLastError (ERROR_INVALID_HANDLE);
2361 type = _wapi_handle_type (handle);
2363 if(io_ops[type].readfile==NULL) {
2364 SetLastError (ERROR_INVALID_HANDLE);
2368 return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2374 * @handle: The file handle to write to. The handle must have
2375 * %GENERIC_WRITE access.
2376 * @buffer: The buffer to read data from.
2377 * @numbytes: The maximum number of bytes to write.
2378 * @byteswritten: The actual number of bytes written is stored here.
2379 * If the handle is positioned at the file end, the length of the file
2380 * is extended. This parameter may be %NULL.
2381 * @overlapped: points to a required %WapiOverlapped structure if
2382 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2385 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2386 * function writes up to @numbytes bytes from @buffer to the file at
2387 * the current file position. If @handle is positioned at the end of
2388 * the file, the file is extended. The actual number of bytes written
2389 * is stored in @byteswritten.
2391 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2392 * file position is ignored and the write position is taken from data
2393 * in the @overlapped structure.
2395 * Return value: %TRUE if the write succeeds, %FALSE on error.
2397 gboolean WriteFile(gpointer fd_handle, gconstpointer buffer, guint32 numbytes,
2398 guint32 *byteswritten, WapiOverlapped *overlapped)
2400 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2401 WapiHandleType type;
2403 if (handle == NULL) {
2404 SetLastError (ERROR_INVALID_HANDLE);
2408 type = _wapi_handle_type (handle);
2410 if(io_ops[type].writefile==NULL) {
2411 SetLastError (ERROR_INVALID_HANDLE);
2415 return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2421 * @handle: Handle to open file. The handle must have
2422 * %GENERIC_WRITE access.
2424 * Flushes buffers of the file and causes all unwritten data to
2427 * Return value: %TRUE on success, %FALSE otherwise.
2429 gboolean FlushFileBuffers(gpointer fd_handle)
2431 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2432 WapiHandleType type;
2434 if (handle == NULL) {
2435 SetLastError (ERROR_INVALID_HANDLE);
2439 type = _wapi_handle_type (handle);
2441 if(io_ops[type].flushfile==NULL) {
2442 SetLastError (ERROR_INVALID_HANDLE);
2446 return(io_ops[type].flushfile (handle));
2451 * @handle: The file handle to set. The handle must have
2452 * %GENERIC_WRITE access.
2454 * Moves the end-of-file position to the current position of the file
2455 * pointer. This function is used to truncate or extend a file.
2457 * Return value: %TRUE on success, %FALSE otherwise.
2459 gboolean SetEndOfFile(gpointer fd_handle)
2461 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2462 WapiHandleType type;
2464 if (handle == NULL) {
2465 SetLastError (ERROR_INVALID_HANDLE);
2469 type = _wapi_handle_type (handle);
2471 if(io_ops[type].setendoffile==NULL) {
2472 SetLastError (ERROR_INVALID_HANDLE);
2476 return(io_ops[type].setendoffile (handle));
2481 * @handle: The file handle to set. The handle must have
2482 * %GENERIC_READ or %GENERIC_WRITE access.
2483 * @movedistance: Low 32 bits of a signed value that specifies the
2484 * number of bytes to move the file pointer.
2485 * @highmovedistance: Pointer to the high 32 bits of a signed value
2486 * that specifies the number of bytes to move the file pointer, or
2488 * @method: The starting point for the file pointer move.
2490 * Sets the file pointer of an open file.
2492 * The distance to move the file pointer is calculated from
2493 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2494 * @movedistance is the 32-bit signed value; otherwise, @movedistance
2495 * is the low 32 bits and @highmovedistance a pointer to the high 32
2496 * bits of a 64 bit signed value. A positive distance moves the file
2497 * pointer forward from the position specified by @method; a negative
2498 * distance moves the file pointer backward.
2500 * If the library is compiled without large file support,
2501 * @highmovedistance is ignored and its value is set to zero on a
2502 * successful return.
2504 * Return value: On success, the low 32 bits of the new file pointer.
2505 * If @highmovedistance is not %NULL, the high 32 bits of the new file
2506 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
2508 guint32 SetFilePointer(gpointer fd_handle, gint32 movedistance,
2509 gint32 *highmovedistance, WapiSeekMethod method)
2511 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2512 WapiHandleType type;
2514 if (handle == NULL) {
2515 SetLastError (ERROR_INVALID_HANDLE);
2516 return(INVALID_SET_FILE_POINTER);
2519 type = _wapi_handle_type (handle);
2521 if(io_ops[type].seek==NULL) {
2522 SetLastError (ERROR_INVALID_HANDLE);
2523 return(INVALID_SET_FILE_POINTER);
2526 return(io_ops[type].seek (handle, movedistance, highmovedistance,
2532 * @handle: The file handle to test.
2534 * Finds the type of file @handle.
2536 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2537 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
2538 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2539 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2541 WapiFileType GetFileType(gpointer fd_handle)
2543 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2544 WapiHandleType type;
2546 if (handle == NULL) {
2547 SetLastError (ERROR_INVALID_HANDLE);
2548 return(FILE_TYPE_UNKNOWN);
2551 type = _wapi_handle_type (handle);
2553 if(io_ops[type].getfiletype==NULL) {
2554 SetLastError (ERROR_INVALID_HANDLE);
2555 return(FILE_TYPE_UNKNOWN);
2558 return(io_ops[type].getfiletype ());
2563 * @handle: The file handle to query. The handle must have
2564 * %GENERIC_READ or %GENERIC_WRITE access.
2565 * @highsize: If non-%NULL, the high 32 bits of the file size are
2568 * Retrieves the size of the file @handle.
2570 * If the library is compiled without large file support, @highsize
2571 * has its value set to zero on a successful return.
2573 * Return value: On success, the low 32 bits of the file size. If
2574 * @highsize is non-%NULL then the high 32 bits of the file size are
2575 * stored here. On failure %INVALID_FILE_SIZE is returned.
2577 guint32 GetFileSize(gpointer fd_handle, guint32 *highsize)
2579 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2580 WapiHandleType type;
2582 if (handle == NULL) {
2583 SetLastError (ERROR_INVALID_HANDLE);
2584 return(INVALID_FILE_SIZE);
2587 type = _wapi_handle_type (handle);
2589 if(io_ops[type].getfilesize==NULL) {
2590 SetLastError (ERROR_INVALID_HANDLE);
2591 return(INVALID_FILE_SIZE);
2594 return(io_ops[type].getfilesize (handle, highsize));
2599 * @handle: The file handle to query. The handle must have
2600 * %GENERIC_READ access.
2601 * @create_time: Points to a %WapiFileTime structure to receive the
2602 * number of ticks since the epoch that file was created. May be
2604 * @last_access: Points to a %WapiFileTime structure to receive the
2605 * number of ticks since the epoch when file was last accessed. May be
2607 * @last_write: Points to a %WapiFileTime structure to receive the
2608 * number of ticks since the epoch when file was last written to. May
2611 * Finds the number of ticks since the epoch that the file referenced
2612 * by @handle was created, last accessed and last modified. A tick is
2613 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2616 * Create time isn't recorded on POSIX file systems or reported by
2617 * stat(2), so that time is guessed by returning the oldest of the
2620 * Return value: %TRUE on success, %FALSE otherwise.
2622 gboolean GetFileTime(gpointer fd_handle, WapiFileTime *create_time,
2623 WapiFileTime *last_access, WapiFileTime *last_write)
2625 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2626 WapiHandleType type;
2628 if (handle == NULL) {
2629 SetLastError (ERROR_INVALID_HANDLE);
2633 type = _wapi_handle_type (handle);
2635 if(io_ops[type].getfiletime==NULL) {
2636 SetLastError (ERROR_INVALID_HANDLE);
2640 return(io_ops[type].getfiletime (handle, create_time, last_access,
2646 * @handle: The file handle to set. The handle must have
2647 * %GENERIC_WRITE access.
2648 * @create_time: Points to a %WapiFileTime structure that contains the
2649 * number of ticks since the epoch that the file was created. May be
2651 * @last_access: Points to a %WapiFileTime structure that contains the
2652 * number of ticks since the epoch when the file was last accessed.
2654 * @last_write: Points to a %WapiFileTime structure that contains the
2655 * number of ticks since the epoch when the file was last written to.
2658 * Sets the number of ticks since the epoch that the file referenced
2659 * by @handle was created, last accessed or last modified. A tick is
2660 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2663 * Create time isn't recorded on POSIX file systems, and is ignored.
2665 * Return value: %TRUE on success, %FALSE otherwise.
2667 gboolean SetFileTime(gpointer fd_handle, const WapiFileTime *create_time,
2668 const WapiFileTime *last_access,
2669 const WapiFileTime *last_write)
2671 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2672 WapiHandleType type;
2674 if (handle == NULL) {
2675 SetLastError (ERROR_INVALID_HANDLE);
2679 type = _wapi_handle_type (handle);
2681 if(io_ops[type].setfiletime==NULL) {
2682 SetLastError (ERROR_INVALID_HANDLE);
2686 return(io_ops[type].setfiletime (handle, create_time, last_access,
2690 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2691 * January 1 1601 GMT
2694 #define TICKS_PER_MILLISECOND 10000L
2695 #define TICKS_PER_SECOND 10000000L
2696 #define TICKS_PER_MINUTE 600000000L
2697 #define TICKS_PER_HOUR 36000000000LL
2698 #define TICKS_PER_DAY 864000000000LL
2700 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2702 static const guint16 mon_yday[2][13]={
2703 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2704 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2708 * FileTimeToSystemTime:
2709 * @file_time: Points to a %WapiFileTime structure that contains the
2710 * number of ticks to convert.
2711 * @system_time: Points to a %WapiSystemTime structure to receive the
2714 * Converts a tick count into broken-out time values.
2716 * Return value: %TRUE on success, %FALSE otherwise.
2718 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2719 WapiSystemTime *system_time)
2721 gint64 file_ticks, totaldays, rem, y;
2724 if(system_time==NULL) {
2726 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2729 SetLastError (ERROR_INVALID_PARAMETER);
2733 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2734 file_time->dwLowDateTime;
2736 /* Really compares if file_ticks>=0x8000000000000000
2737 * (LLONG_MAX+1) but we're working with a signed value for the
2738 * year and day calculation to work later
2742 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2745 SetLastError (ERROR_INVALID_PARAMETER);
2749 totaldays=(file_ticks / TICKS_PER_DAY);
2750 rem = file_ticks % TICKS_PER_DAY;
2752 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2756 system_time->wHour=rem/TICKS_PER_HOUR;
2757 rem %= TICKS_PER_HOUR;
2759 g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2760 system_time->wHour, rem);
2763 system_time->wMinute = rem / TICKS_PER_MINUTE;
2764 rem %= TICKS_PER_MINUTE;
2766 g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2767 system_time->wMinute, rem);
2770 system_time->wSecond = rem / TICKS_PER_SECOND;
2771 rem %= TICKS_PER_SECOND;
2773 g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2774 system_time->wSecond, rem);
2777 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2779 g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2780 system_time->wMilliseconds);
2783 /* January 1, 1601 was a Monday, according to Emacs calendar */
2784 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2786 g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2787 system_time->wDayOfWeek);
2790 /* This algorithm to find year and month given days from epoch
2795 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2796 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2798 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2799 /* Guess a corrected year, assuming 365 days per year */
2800 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2802 g_message(G_GNUC_PRETTY_FUNCTION
2803 ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2805 g_message(G_GNUC_PRETTY_FUNCTION
2806 ": LEAPS(yg): %lld LEAPS(y): %lld",
2807 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2810 /* Adjust days and y to match the guessed year. */
2811 totaldays -= ((yg - y) * 365
2812 + LEAPS_THRU_END_OF (yg - 1)
2813 - LEAPS_THRU_END_OF (y - 1));
2815 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2820 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2824 system_time->wYear = y;
2826 g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2829 ip = mon_yday[isleap(y)];
2831 for(y=11; totaldays < ip[y]; --y) {
2836 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2839 system_time->wMonth = y + 1;
2841 g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2844 system_time->wDay = totaldays + 1;
2846 g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2853 file_compare (gconstpointer a, gconstpointer b)
2855 gchar *astr = *(gchar **) a;
2856 gchar *bstr = *(gchar **) b;
2858 return strcmp (astr, bstr);
2862 get_errno_from_g_file_error (gint error)
2866 case G_FILE_ERROR_ACCES:
2871 case G_FILE_ERROR_NAMETOOLONG:
2872 error = ENAMETOOLONG;
2876 case G_FILE_ERROR_NOENT:
2881 case G_FILE_ERROR_NOTDIR:
2886 case G_FILE_ERROR_NXIO:
2891 case G_FILE_ERROR_NODEV:
2896 case G_FILE_ERROR_ROFS:
2901 case G_FILE_ERROR_TXTBSY:
2906 case G_FILE_ERROR_FAULT:
2911 case G_FILE_ERROR_LOOP:
2916 case G_FILE_ERROR_NOSPC:
2921 case G_FILE_ERROR_NOMEM:
2926 case G_FILE_ERROR_MFILE:
2931 case G_FILE_ERROR_NFILE:
2936 case G_FILE_ERROR_BADF:
2941 case G_FILE_ERROR_INVAL:
2946 case G_FILE_ERROR_PIPE:
2951 case G_FILE_ERROR_AGAIN:
2956 case G_FILE_ERROR_INTR:
2961 case G_FILE_ERROR_IO:
2966 case G_FILE_ERROR_PERM:
2970 case G_FILE_ERROR_FAILED:
2971 error = ERROR_INVALID_PARAMETER;
2978 /* scandir using glib */
2980 mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
2982 GError *error = NULL;
2987 GPatternSpec *patspec;
2989 dir = g_dir_open (dirname, 0, &error);
2991 /* g_dir_open returns ENOENT on directories on which we don't
2992 * have read/x permission */
2993 gint errnum = get_errno_from_g_file_error (error->code);
2994 g_error_free (error);
2995 if (errnum == ENOENT && g_file_test (dirname, G_FILE_TEST_IS_DIR))
3002 patspec = g_pattern_spec_new (pattern);
3003 names = g_ptr_array_new ();
3004 while ((name = g_dir_read_name (dir)) != NULL) {
3005 if (g_pattern_match_string (patspec, name))
3006 g_ptr_array_add (names, g_strdup (name));
3009 g_pattern_spec_free (patspec);
3011 result = names->len;
3013 g_ptr_array_sort (names, file_compare);
3014 g_ptr_array_set_size (names, result + 1);
3016 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
3018 g_ptr_array_free (names, TRUE);
3024 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
3026 struct _WapiHandlePrivate_find *find_handle;
3027 gpointer handle, find_ret = INVALID_HANDLE_VALUE;
3029 gchar *utf8_pattern = NULL, *dir_part, *entry_part;
3032 gboolean unref = FALSE;
3034 if (pattern == NULL) {
3036 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
3039 SetLastError (ERROR_PATH_NOT_FOUND);
3040 return(INVALID_HANDLE_VALUE);
3043 utf8_pattern = mono_unicode_to_external (pattern);
3044 if (utf8_pattern == NULL) {
3046 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3049 SetLastError (ERROR_INVALID_NAME);
3050 return(INVALID_HANDLE_VALUE);
3054 g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
3058 /* Figure out which bit of the pattern is the directory */
3059 dir_part=g_path_get_dirname (utf8_pattern);
3060 entry_part=g_path_get_basename (utf8_pattern);
3063 /* Don't do this check for now, it breaks if directories
3064 * really do have metachars in their names (see bug 58116).
3065 * FIXME: Figure out a better solution to keep some checks...
3067 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3068 SetLastError (ERROR_INVALID_NAME);
3070 g_free (entry_part);
3071 g_free (utf8_pattern);
3072 return(INVALID_HANDLE_VALUE);
3076 handle=_wapi_handle_new (WAPI_HANDLE_FIND);
3077 if(handle==_WAPI_HANDLE_INVALID) {
3078 g_warning (G_GNUC_PRETTY_FUNCTION ": error creating find handle");
3080 g_free (entry_part);
3081 g_free (utf8_pattern);
3082 SetLastError (ERROR_GEN_FAILURE);
3084 return(INVALID_HANDLE_VALUE);
3087 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3089 thr_ret = _wapi_handle_lock_handle (handle);
3090 g_assert (thr_ret == 0);
3092 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3093 (gpointer *)&find_handle);
3095 g_warning (G_GNUC_PRETTY_FUNCTION
3096 ": error looking up find handle %p", handle);
3099 g_free (entry_part);
3101 g_free (utf8_pattern);
3102 utf8_pattern = NULL;
3103 SetLastError (ERROR_INVALID_HANDLE);
3107 /* The pattern can specify a directory or a set of files.
3109 * The pattern can have wildcard characters ? and *, but only
3110 * in the section after the last directory delimiter. (Return
3111 * ERROR_INVALID_NAME if there are wildcards in earlier path
3112 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3113 * means "match one character", "??" seems to mean "match one
3114 * or two characters", "???" seems to mean "match one, two or
3115 * three characters", etc. Windows will also try and match
3116 * the mangled "short name" of files, so 8 character patterns
3117 * with wildcards will show some surprising results.
3119 * All the written documentation I can find says that '?'
3120 * should only match one character, and doesn't mention '??',
3121 * '???' etc. I'm going to assume that the strict behaviour
3122 * (ie '???' means three and only three characters) is the
3123 * correct one, because that lets me use fnmatch(3) rather
3124 * than mess around with regexes.
3127 find_handle->namelist = NULL;
3128 result = mono_io_scandir (dir_part, entry_part, &find_handle->namelist);
3132 gint errnum = errno;
3134 _wapi_set_last_error_from_errno ();
3136 g_message (G_GNUC_PRETTY_FUNCTION ": scandir error: %s", g_strerror (errnum));
3138 g_free (utf8_pattern);
3139 g_free (entry_part);
3145 g_free (utf8_pattern);
3146 g_free (entry_part);
3149 g_message (G_GNUC_PRETTY_FUNCTION ": Got %d matches", result);
3152 find_handle->dir_part = dir_part;
3153 find_handle->num = result;
3154 find_handle->count = 0;
3159 thr_ret = _wapi_handle_unlock_handle (handle);
3160 g_assert (thr_ret == 0);
3161 pthread_cleanup_pop (0);
3163 /* FindNextFile has to be called after unlocking the handle,
3164 * because it wants to lock the handle itself
3166 if (find_ret != INVALID_HANDLE_VALUE &&
3167 !FindNextFile (handle, find_data)) {
3169 SetLastError (ERROR_NO_MORE_FILES);
3170 find_ret = INVALID_HANDLE_VALUE;
3173 /* Must not call _wapi_handle_unref() with the handle already
3177 _wapi_handle_unref (handle);
3183 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
3185 struct _WapiHandlePrivate_find *find_handle;
3189 gchar *utf8_filename, *utf8_basename;
3190 gunichar2 *utf16_basename;
3194 gboolean ret = FALSE;
3196 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3197 (gpointer *)&find_handle);
3199 g_warning (G_GNUC_PRETTY_FUNCTION
3200 ": error looking up find handle %p", handle);
3201 SetLastError (ERROR_INVALID_HANDLE);
3205 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3207 thr_ret = _wapi_handle_lock_handle (handle);
3208 g_assert (thr_ret == 0);
3211 if (find_handle->count >= find_handle->num) {
3212 SetLastError (ERROR_NO_MORE_FILES);
3216 /* stat next match */
3218 filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3219 if (lstat (filename, &buf) != 0) {
3221 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
3228 /* Check for dangling symlinks, and ignore them (principle of
3229 * least surprise, avoiding confusion where we report the file
3230 * exists, but when someone tries to open it we would report
3233 if(S_ISLNK (buf.st_mode)) {
3234 if(stat (filename, &buf) != 0) {
3240 utf8_filename=mono_utf8_from_external (filename);
3241 if(utf8_filename==NULL) {
3242 /* We couldn't turn this filename into utf8 (eg the
3243 * encoding of the name wasn't convertible), so just
3252 g_message (G_GNUC_PRETTY_FUNCTION ": Found [%s]", utf8_filename);
3255 /* fill data block */
3257 if (buf.st_mtime < buf.st_ctime)
3258 create_time = buf.st_mtime;
3260 create_time = buf.st_ctime;
3262 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3264 _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
3265 _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3266 _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3268 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3269 find_data->nFileSizeHigh = 0;
3270 find_data->nFileSizeLow = 0;
3273 find_data->nFileSizeHigh = buf.st_size >> 32;
3274 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3277 find_data->dwReserved0 = 0;
3278 find_data->dwReserved1 = 0;
3280 utf8_basename = g_path_get_basename (utf8_filename);
3281 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3283 if(utf16_basename==NULL) {
3284 g_free (utf8_basename);
3285 g_free (utf8_filename);
3290 /* utf16 is 2 * utf8 */
3293 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3295 /* Truncating a utf16 string like this might leave the last
3298 memcpy (find_data->cFileName, utf16_basename,
3299 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3301 find_data->cAlternateFileName [0] = 0; /* not used */
3303 g_free (utf8_basename);
3304 g_free (utf8_filename);
3305 g_free (utf16_basename);
3308 thr_ret = _wapi_handle_unlock_handle (handle);
3309 g_assert (thr_ret == 0);
3310 pthread_cleanup_pop (0);
3317 * @wapi_handle: the find handle to close.
3319 * Closes find handle @wapi_handle
3321 * Return value: %TRUE on success, %FALSE otherwise.
3323 gboolean FindClose (gpointer handle)
3325 struct _WapiHandlePrivate_find *find_handle;
3329 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3330 (gpointer *)&find_handle);
3332 g_warning (G_GNUC_PRETTY_FUNCTION
3333 ": error looking up find handle %p", handle);
3334 SetLastError (ERROR_INVALID_HANDLE);
3338 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3340 thr_ret = _wapi_handle_lock_handle (handle);
3341 g_assert (thr_ret == 0);
3343 g_strfreev (find_handle->namelist);
3344 g_free (find_handle->dir_part);
3346 thr_ret = _wapi_handle_unlock_handle (handle);
3347 g_assert (thr_ret == 0);
3348 pthread_cleanup_pop (0);
3350 _wapi_handle_unref (handle);
3357 * @name: a pointer to a NULL-terminated unicode string, that names
3358 * the directory to be created.
3359 * @security: ignored for now
3361 * Creates directory @name
3363 * Return value: %TRUE on success, %FALSE otherwise.
3365 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
3374 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3377 SetLastError (ERROR_INVALID_NAME);
3381 utf8_name = mono_unicode_to_external (name);
3382 if (utf8_name == NULL) {
3384 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3387 SetLastError (ERROR_INVALID_NAME);
3391 result = mkdir (utf8_name, 0777);
3398 if (errno == EEXIST) {
3399 result = stat (utf8_name, &buf);
3401 _wapi_set_last_error_from_errno ();
3407 attrs = _wapi_stat_to_file_attributes (&buf);
3408 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
3412 _wapi_set_last_error_from_errno ();
3416 _wapi_set_last_error_from_errno ();
3423 * @name: a pointer to a NULL-terminated unicode string, that names
3424 * the directory to be removed.
3426 * Removes directory @name
3428 * Return value: %TRUE on success, %FALSE otherwise.
3430 gboolean RemoveDirectory (const gunichar2 *name)
3437 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3440 SetLastError (ERROR_INVALID_NAME);
3444 utf8_name = mono_unicode_to_external (name);
3445 if (utf8_name == NULL) {
3447 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3450 SetLastError (ERROR_INVALID_NAME);
3454 result = rmdir (utf8_name);
3460 _wapi_set_last_error_from_errno ();
3465 * GetFileAttributes:
3466 * @name: a pointer to a NULL-terminated unicode filename.
3468 * Gets the attributes for @name;
3470 * Return value: %INVALID_FILE_ATTRIBUTES on failure
3472 guint32 GetFileAttributes (const gunichar2 *name)
3480 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3483 SetLastError (ERROR_INVALID_NAME);
3487 utf8_name = mono_unicode_to_external (name);
3488 if (utf8_name == NULL) {
3490 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3493 SetLastError (ERROR_INVALID_PARAMETER);
3494 return (INVALID_FILE_ATTRIBUTES);
3497 result = stat (utf8_name, &buf);
3500 _wapi_set_last_error_from_errno ();
3502 return (INVALID_FILE_ATTRIBUTES);
3506 return _wapi_stat_to_file_attributes (&buf);
3510 * GetFileAttributesEx:
3511 * @name: a pointer to a NULL-terminated unicode filename.
3512 * @level: must be GetFileExInfoStandard
3513 * @info: pointer to a WapiFileAttributesData structure
3515 * Gets attributes, size and filetimes for @name;
3517 * Return value: %TRUE on success, %FALSE on failure
3519 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3522 WapiFileAttributesData *data;
3528 if (level != GetFileExInfoStandard) {
3530 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
3533 SetLastError (ERROR_INVALID_PARAMETER);
3539 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3542 SetLastError (ERROR_INVALID_NAME);
3546 utf8_name = mono_unicode_to_external (name);
3547 if (utf8_name == NULL) {
3549 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3552 SetLastError (ERROR_INVALID_PARAMETER);
3556 result = stat (utf8_name, &buf);
3560 SetLastError (ERROR_FILE_NOT_FOUND);
3564 /* fill data block */
3566 data = (WapiFileAttributesData *)info;
3568 if (buf.st_mtime < buf.st_ctime)
3569 create_time = buf.st_mtime;
3571 create_time = buf.st_ctime;
3573 data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3575 _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3576 _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3577 _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3579 if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3580 data->nFileSizeHigh = 0;
3581 data->nFileSizeLow = 0;
3584 data->nFileSizeHigh = buf.st_size >> 32;
3585 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3593 * @name: name of file
3594 * @attrs: attributes to set
3596 * Changes the attributes on a named file.
3598 * Return value: %TRUE on success, %FALSE on failure.
3600 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3602 /* FIXME: think of something clever to do on unix */
3608 * Currently we only handle one *internal* case, with a value that is
3609 * not standard: 0x80000000, which means `set executable bit'
3614 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3617 SetLastError (ERROR_INVALID_NAME);
3621 utf8_name = mono_unicode_to_external (name);
3622 if (utf8_name == NULL) {
3624 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3627 SetLastError (ERROR_INVALID_NAME);
3631 result = stat (utf8_name, &buf);
3634 SetLastError (ERROR_FILE_NOT_FOUND);
3638 /* Contrary to the documentation, ms allows NORMAL to be
3639 * specified along with other attributes, so dont bother to
3640 * catch that case here.
3642 if (attrs & FILE_ATTRIBUTE_READONLY) {
3643 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3645 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
3648 /* Ignore the other attributes for now */
3650 if (attrs & 0x80000000){
3651 mode_t exec_mask = 0;
3653 if ((buf.st_mode & S_IRUSR) != 0)
3654 exec_mask |= S_IXUSR;
3656 if ((buf.st_mode & S_IRGRP) != 0)
3657 exec_mask |= S_IXGRP;
3659 if ((buf.st_mode & S_IROTH) != 0)
3660 exec_mask |= S_IXOTH;
3662 result = chmod (utf8_name, buf.st_mode | exec_mask);
3664 /* Don't bother to reset executable (might need to change this
3674 * GetCurrentDirectory
3675 * @length: size of the buffer
3676 * @buffer: pointer to buffer that recieves path
3678 * Retrieves the current directory for the current process.
3680 * Return value: number of characters in buffer on success, zero on failure
3682 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3685 gunichar2 *utf16_path;
3689 path = g_get_current_dir ();
3693 utf16_path=mono_unicode_from_external (path, &bytes);
3695 /* if buffer too small, return number of characters required.
3696 * this is plain dumb.
3699 count = (bytes/2)+1;
3700 if (count > length) {
3702 g_free (utf16_path);
3707 /* Add the terminator */
3708 memset (buffer, '\0', bytes+2);
3709 memcpy (buffer, utf16_path, bytes);
3711 g_free (utf16_path);
3718 * SetCurrentDirectory
3719 * @path: path to new directory
3721 * Changes the directory path for the current process.
3723 * Return value: %TRUE on success, %FALSE on failure.
3725 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3730 utf8_path = mono_unicode_to_external (path);
3731 if (chdir (utf8_path) != 0) {
3732 _wapi_set_last_error_from_errno ();
3742 /* When we're confident there are no more bugs in the fd->handle
3743 * mapping, this can be replaced as a no-op: GPOINTER_TO_INT(fd_handle) == fd
3745 int _wapi_file_handle_to_fd (gpointer fd_handle)
3747 struct _WapiHandlePrivate_file *file_private_handle;
3749 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3752 g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
3755 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
3756 (gpointer *)&file_private_handle);
3758 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
3759 (gpointer *)&file_private_handle);
3761 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
3762 (gpointer *)&file_private_handle);
3765 g_message (G_GNUC_PRETTY_FUNCTION
3768 SetLastError (ERROR_INVALID_HANDLE);
3774 if (file_private_handle->fd_mapped.assigned == FALSE) {
3775 SetLastError (ERROR_INVALID_HANDLE);
3780 g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
3781 file_private_handle->fd_mapped.fd);
3784 g_assert (file_private_handle->fd_mapped.fd == GPOINTER_TO_INT (fd_handle));
3786 return(file_private_handle->fd_mapped.fd);
3789 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3790 WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3792 struct _WapiHandle_file *pipe_read_handle;
3793 struct _WapiHandle_file *pipe_write_handle;
3794 struct _WapiHandlePrivate_file *pipe_read_private_handle;
3795 struct _WapiHandlePrivate_file *pipe_write_private_handle;
3796 gpointer read_handle;
3797 gpointer write_handle;
3802 gboolean unref_read = FALSE, unref_write = FALSE;
3803 gboolean cp_ret = FALSE;
3805 mono_once (&io_ops_once, io_ops_init);
3808 g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
3814 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
3818 _wapi_set_last_error_from_errno ();
3822 if (filedes[0] >= _wapi_fd_offset_table_size ||
3823 filedes[1] >= _wapi_fd_offset_table_size) {
3825 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
3828 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3836 /* filedes[0] is open for reading, filedes[1] for writing */
3838 read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3839 if(read_handle==_WAPI_HANDLE_INVALID) {
3840 g_warning (G_GNUC_PRETTY_FUNCTION
3841 ": error creating pipe read handle");
3844 SetLastError (ERROR_GEN_FAILURE);
3849 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3851 thr_ret = _wapi_handle_lock_handle (read_handle);
3852 g_assert (thr_ret == 0);
3854 ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
3855 (gpointer *)&pipe_read_handle,
3856 (gpointer *)&pipe_read_private_handle);
3858 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3861 SetLastError (ERROR_INVALID_HANDLE);
3865 write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3866 if(write_handle==_WAPI_HANDLE_INVALID) {
3867 g_warning (G_GNUC_PRETTY_FUNCTION
3868 ": error creating pipe write handle");
3873 SetLastError (ERROR_GEN_FAILURE);
3878 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3880 thr_ret = _wapi_handle_lock_handle (write_handle);
3881 g_assert (thr_ret == 0);
3883 ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
3884 (gpointer *)&pipe_write_handle,
3885 (gpointer *)&pipe_write_private_handle);
3887 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3893 SetLastError (ERROR_INVALID_HANDLE);
3898 pipe_read_private_handle->fd_mapped.fd=filedes[0];
3899 pipe_read_private_handle->fd_mapped.assigned=TRUE;
3900 pipe_read_handle->fileaccess=GENERIC_READ;
3902 _wapi_handle_fd_offset_store (filedes[0], read_handle);
3903 *readpipe=GINT_TO_POINTER (filedes[0]);
3905 pipe_write_private_handle->fd_mapped.fd=filedes[1];
3906 pipe_write_private_handle->fd_mapped.assigned=TRUE;
3907 pipe_write_handle->fileaccess=GENERIC_WRITE;
3909 _wapi_handle_fd_offset_store (filedes[1], write_handle);
3910 *writepipe=GINT_TO_POINTER (filedes[1]);
3913 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]);
3917 thr_ret =_wapi_handle_unlock_handle (write_handle);
3918 g_assert (thr_ret == 0);
3919 pthread_cleanup_pop (0);
3922 _wapi_handle_unref (write_handle);
3926 thr_ret =_wapi_handle_unlock_handle (read_handle);
3927 g_assert (thr_ret == 0);
3928 pthread_cleanup_pop (0);
3930 /* Must not call _wapi_handle_unref() with the handle already
3934 _wapi_handle_unref (read_handle);
3940 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3942 gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3943 gunichar2 *tmpdir16=NULL;
3948 if(tmpdir[strlen (tmpdir)]!='/') {
3950 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3953 tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3954 if(tmpdir16==NULL) {
3962 g_message (G_GNUC_PRETTY_FUNCTION
3963 ": Size %d smaller than needed (%ld)", len,
3969 /* Add the terminator */
3970 memset (buf, '\0', bytes+2);
3971 memcpy (buf, tmpdir16, bytes);
3977 if(tmpdir16!=NULL) {
3986 _wapi_io_add_callback (gpointer fd_handle,
3987 WapiOverlappedCB callback,
3988 guint64 flags G_GNUC_UNUSED)
3990 struct _WapiHandle_file *file_handle;
3991 struct _WapiHandlePrivate_file *file_private_handle;
3994 gboolean ret = FALSE;
3995 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3997 if (handle == NULL) {
3998 SetLastError (ERROR_INVALID_HANDLE);
4002 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4003 (gpointer *) &file_handle,
4004 (gpointer *) &file_private_handle);
4007 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
4008 (gpointer *) &file_handle,
4009 (gpointer *) &file_private_handle);
4013 if (ok == FALSE || file_private_handle->async == FALSE) {
4014 SetLastError (ERROR_INVALID_HANDLE);
4018 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
4020 thr_ret = _wapi_handle_lock_handle (handle);
4021 g_assert (thr_ret == 0);
4023 if (file_private_handle->callback != NULL) {
4024 SetLastError (ERROR_INVALID_PARAMETER);
4029 file_private_handle->callback = callback;
4032 thr_ret = _wapi_handle_unlock_handle (handle);
4033 g_assert (thr_ret == 0);
4034 pthread_cleanup_pop (0);
4040 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
4043 gunichar2 *ptr, *dir;
4044 glong length, total = 0;
4048 memset (buf, 0, sizeof (gunichar2) * (len + 1));
4053 /* Sigh, mntent and friends don't work well.
4054 * It stops on the first line that doesn't begin with a '/'.
4055 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4056 fp = fopen ("/etc/mtab", "rt");
4058 fp = fopen ("/etc/mnttab", "rt");
4064 while (fgets (buffer, 512, fp) != NULL) {
4068 splitted = g_strsplit (buffer, " ", 0);
4069 if (!*splitted || !*(splitted + 1))
4072 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
4073 g_strfreev (splitted);
4074 if (total + length + 1 > len) {
4075 return len * 2; /* guess */
4078 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4080 total += length + 1;
4085 /* Commented out, does not work with my mtab!!! - Gonz */
4086 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4090 gunichar2 *ptr, *dir;
4091 glong len, total = 0;
4094 fp = setmntent ("/etc/mtab", "rt");
4096 fp = setmntent ("/etc/mnttab", "rt");
4102 while ((mnt = getmntent (fp)) != NULL) {
4103 g_print ("GOT %s\n", mnt->mnt_dir);
4104 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4105 if (total + len + 1 > len) {
4106 return len * 2; /* guess */
4109 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4120 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
4122 struct flock lock_data;
4125 lock_data.l_type = F_WRLCK;
4126 lock_data.l_whence = SEEK_SET;
4127 lock_data.l_start = offset;
4128 lock_data.l_len = length;
4131 ret = fcntl (fd, F_SETLK, &lock_data);
4133 while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
4136 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4140 SetLastError (ERROR_LOCK_VIOLATION);
4147 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
4149 struct flock lock_data;
4152 lock_data.l_type = F_UNLCK;
4153 lock_data.l_whence = SEEK_SET;
4154 lock_data.l_start = offset;
4155 lock_data.l_len = length;
4158 ret = fcntl (fd, F_SETLK, &lock_data);
4160 while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
4163 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4167 SetLastError (ERROR_LOCK_VIOLATION);
4174 gboolean LockFile (gpointer fd_handle, guint32 offset_low, guint32 offset_high,
4175 guint32 length_low, guint32 length_high)
4177 struct _WapiHandle_file *file_handle;
4178 struct _WapiHandlePrivate_file *file_private_handle;
4180 off_t offset, length;
4181 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4183 if (handle == NULL) {
4184 SetLastError (ERROR_INVALID_HANDLE);
4188 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4189 (gpointer *)&file_handle,
4190 (gpointer *)&file_private_handle);
4192 g_warning (G_GNUC_PRETTY_FUNCTION
4193 ": error looking up file handle %p", handle);
4194 SetLastError (ERROR_INVALID_HANDLE);
4198 if (file_private_handle->fd_mapped.assigned == FALSE) {
4199 SetLastError (ERROR_INVALID_HANDLE);
4203 if (!(file_handle->fileaccess & GENERIC_READ) &&
4204 !(file_handle->fileaccess & GENERIC_WRITE) &&
4205 !(file_handle->fileaccess & GENERIC_ALL)) {
4207 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);
4209 SetLastError (ERROR_ACCESS_DENIED);
4213 #ifdef HAVE_LARGE_FILE_SUPPORT
4214 offset = ((gint64)offset_high << 32) | offset_low;
4215 length = ((gint64)length_high << 32) | length_low;
4218 g_message (G_GNUC_PRETTY_FUNCTION
4219 ": Locking handle %p fd %d, offset %lld, length %lld",
4220 handle, file_private_handle->fd_mapped.fd, offset, length);
4223 offset = offset_low;
4224 length = length_low;
4227 g_message (G_GNUC_PRETTY_FUNCTION
4228 ": Locking handle %p fd %d, offset %ld, length %ld",
4229 handle, file_private_handle->fd_mapped.fd, offset, length);
4233 return(_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
4237 gboolean UnlockFile (gpointer fd_handle, guint32 offset_low,
4238 guint32 offset_high, guint32 length_low,
4239 guint32 length_high)
4241 struct _WapiHandle_file *file_handle;
4242 struct _WapiHandlePrivate_file *file_private_handle;
4244 off_t offset, length;
4245 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4247 if (handle == NULL) {
4248 SetLastError (ERROR_INVALID_HANDLE);
4252 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4253 (gpointer *)&file_handle,
4254 (gpointer *)&file_private_handle);
4256 g_warning (G_GNUC_PRETTY_FUNCTION
4257 ": error looking up file handle %p", handle);
4258 SetLastError (ERROR_INVALID_HANDLE);
4262 if (file_private_handle->fd_mapped.assigned == FALSE) {
4263 SetLastError (ERROR_INVALID_HANDLE);
4267 if (!(file_handle->fileaccess & GENERIC_READ) &&
4268 !(file_handle->fileaccess & GENERIC_WRITE) &&
4269 !(file_handle->fileaccess & GENERIC_ALL)) {
4271 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);
4273 SetLastError (ERROR_ACCESS_DENIED);
4277 #ifdef HAVE_LARGE_FILE_SUPPORT
4278 offset = ((gint64)offset_high << 32) | offset_low;
4279 length = ((gint64)length_high << 32) | length_low;
4282 g_message (G_GNUC_PRETTY_FUNCTION
4283 ": Unlocking handle %p fd %d, offset %lld, length %lld",
4284 handle, file_private_handle->fd_mapped.fd, offset, length);
4287 offset = offset_low;
4288 length = length_low;
4291 g_message (G_GNUC_PRETTY_FUNCTION
4292 ": Unlocking handle %p fd %d, offset %ld, length %ld",
4293 handle, file_private_handle->fd_mapped.fd, offset, length);
4297 return(_wapi_unlock_file_region (file_private_handle->fd_mapped.fd,