2 * io.c: File, console and find handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
17 #include <sys/types.h>
23 #ifndef PLATFORM_WIN32
27 #elif defined(HAVE_SYS_AIO_H)
35 #include <mono/io-layer/wapi.h>
36 #include <mono/io-layer/wapi-private.h>
37 #include <mono/io-layer/handles-private.h>
38 #include <mono/io-layer/io-private.h>
39 #include <mono/io-layer/timefuncs-private.h>
40 #include <mono/io-layer/thread-private.h>
41 #include <mono/utils/strenc.h>
45 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length);
46 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length);
48 static void file_close_shared (gpointer handle);
49 static void file_close_private (gpointer handle);
50 static WapiFileType file_getfiletype(void);
51 static gboolean file_read(gpointer handle, gpointer buffer,
52 guint32 numbytes, guint32 *bytesread,
53 WapiOverlapped *overlapped);
54 static gboolean file_write(gpointer handle, gconstpointer buffer,
55 guint32 numbytes, guint32 *byteswritten,
56 WapiOverlapped *overlapped);
57 static gboolean file_flush(gpointer handle);
58 static guint32 file_seek(gpointer handle, gint32 movedistance,
59 gint32 *highmovedistance, WapiSeekMethod method);
60 static gboolean file_setendoffile(gpointer handle);
61 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
62 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
63 WapiFileTime *last_access,
64 WapiFileTime *last_write);
65 static gboolean file_setfiletime(gpointer handle,
66 const WapiFileTime *create_time,
67 const WapiFileTime *last_access,
68 const WapiFileTime *last_write);
70 /* File handle is only signalled for overlapped IO */
71 struct _WapiHandleOps _wapi_file_ops = {
72 file_close_shared, /* close_shared */
73 file_close_private, /* close_private */
79 static void console_close_shared (gpointer handle);
80 static void console_close_private (gpointer handle);
81 static WapiFileType console_getfiletype(void);
82 static gboolean console_read(gpointer handle, gpointer buffer,
83 guint32 numbytes, guint32 *bytesread,
84 WapiOverlapped *overlapped);
85 static gboolean console_write(gpointer handle, gconstpointer buffer,
86 guint32 numbytes, guint32 *byteswritten,
87 WapiOverlapped *overlapped);
89 /* Console is mostly the same as file, except it can block waiting for
92 struct _WapiHandleOps _wapi_console_ops = {
93 console_close_shared, /* close_shared */
94 console_close_private, /* close_private */
100 /* Find handle has no ops.
102 struct _WapiHandleOps _wapi_find_ops = {
103 NULL, /* close_shared */
104 NULL, /* close_private */
110 static void pipe_close_shared (gpointer handle);
111 static void pipe_close_private (gpointer handle);
112 static WapiFileType pipe_getfiletype (void);
113 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes,
114 guint32 *bytesread, WapiOverlapped *overlapped);
115 static gboolean pipe_write (gpointer handle, gconstpointer buffer,
116 guint32 numbytes, guint32 *byteswritten,
117 WapiOverlapped *overlapped);
121 struct _WapiHandleOps _wapi_pipe_ops = {
122 pipe_close_shared, /* close_shared */
123 pipe_close_private, /* close_private */
129 static const struct {
130 /* File, console and pipe handles */
131 WapiFileType (*getfiletype)(void);
133 /* File, console and pipe handles */
134 gboolean (*readfile)(gpointer handle, gpointer buffer,
135 guint32 numbytes, guint32 *bytesread,
136 WapiOverlapped *overlapped);
137 gboolean (*writefile)(gpointer handle, gconstpointer buffer,
138 guint32 numbytes, guint32 *byteswritten,
139 WapiOverlapped *overlapped);
140 gboolean (*flushfile)(gpointer handle);
143 guint32 (*seek)(gpointer handle, gint32 movedistance,
144 gint32 *highmovedistance, WapiSeekMethod method);
145 gboolean (*setendoffile)(gpointer handle);
146 guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
147 gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time,
148 WapiFileTime *last_access,
149 WapiFileTime *last_write);
150 gboolean (*setfiletime)(gpointer handle,
151 const WapiFileTime *create_time,
152 const WapiFileTime *last_access,
153 const WapiFileTime *last_write);
154 } io_ops[WAPI_HANDLE_COUNT]={
155 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
158 file_read, file_write,
159 file_flush, file_seek,
165 {console_getfiletype,
168 NULL, NULL, NULL, NULL, NULL, NULL},
170 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
172 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
174 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
176 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
177 /* socket (will need at least read and write) */
178 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
180 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
182 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
187 NULL, NULL, NULL, NULL, NULL, NULL},
191 static mono_once_t io_ops_once=MONO_ONCE_INIT;
193 static void io_ops_init (void)
195 /* _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
196 /* WAPI_HANDLE_CAP_WAIT); */
197 /* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
198 /* WAPI_HANDLE_CAP_WAIT); */
201 /* Some utility functions.
204 static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
208 /* FIXME: this could definitely be better */
210 if (S_ISDIR (buf->st_mode))
211 attrs |= FILE_ATTRIBUTE_DIRECTORY;
213 attrs |= FILE_ATTRIBUTE_ARCHIVE;
215 if (!(buf->st_mode & S_IWUSR))
216 attrs |= FILE_ATTRIBUTE_READONLY;
222 _wapi_set_last_error_from_errno (void)
224 SetLastError (_wapi_get_win32_file_error (errno));
229 static void file_close_shared (gpointer handle)
231 struct _WapiHandle_file *file_handle;
234 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
235 (gpointer *)&file_handle, NULL);
237 g_warning (G_GNUC_PRETTY_FUNCTION
238 ": error looking up file handle %p", handle);
239 SetLastError (ERROR_INVALID_HANDLE);
244 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p", handle);
247 if(file_handle->filename!=0) {
248 _wapi_handle_scratch_delete (file_handle->filename);
249 file_handle->filename=0;
251 if(file_handle->security_attributes!=0) {
252 _wapi_handle_scratch_delete (file_handle->security_attributes);
253 file_handle->security_attributes=0;
257 static void file_close_private (gpointer handle)
259 struct _WapiHandlePrivate_file *file_private_handle;
262 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
263 (gpointer *)&file_private_handle);
265 g_warning (G_GNUC_PRETTY_FUNCTION
266 ": error looking up file handle %p", handle);
267 SetLastError (ERROR_INVALID_HANDLE);
271 if (file_private_handle->fd_mapped.assigned) {
273 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
274 handle, file_private_handle->fd_mapped.fd);
277 /* Blank out the mapping, to make catching errors easier */
278 _wapi_handle_fd_offset_store (file_private_handle->fd_mapped.fd, NULL);
280 close(file_private_handle->fd_mapped.fd);
284 static WapiFileType file_getfiletype(void)
286 return(FILE_TYPE_DISK);
292 WapiOverlapped *overlapped;
293 WapiOverlappedCB callback;
296 #define SIGPTR(a) a.SIGVAL_PTR
299 async_notifier (union sigval sig)
301 notifier_data_t *ndata = SIGPTR (sig);
305 error = aio_return (ndata->aio);
307 error = _wapi_get_win32_file_error (error);
314 ndata->callback (error, numbytes, ndata->overlapped);
321 static gboolean file_read(gpointer handle, gpointer buffer,
322 guint32 numbytes, guint32 *bytesread,
323 WapiOverlapped *overlapped)
325 struct _WapiHandle_file *file_handle;
326 struct _WapiHandlePrivate_file *file_private_handle;
330 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
331 (gpointer *)&file_handle,
332 (gpointer *)&file_private_handle);
334 g_warning (G_GNUC_PRETTY_FUNCTION
335 ": error looking up file handle %p", handle);
336 SetLastError (ERROR_INVALID_HANDLE);
340 if (file_private_handle->fd_mapped.assigned == FALSE) {
341 SetLastError (ERROR_INVALID_HANDLE);
345 if(bytesread!=NULL) {
349 if(!(file_handle->fileaccess&GENERIC_READ) &&
350 !(file_handle->fileaccess&GENERIC_ALL)) {
352 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
355 SetLastError (ERROR_ACCESS_DENIED);
359 if (file_private_handle->async == FALSE) {
361 ret=read(file_private_handle->fd_mapped.fd, buffer,
364 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
370 g_message(G_GNUC_PRETTY_FUNCTION
371 ": read of handle %p fd %d error: %s", handle,
372 file_private_handle->fd_mapped.fd,
375 SetLastError (_wapi_get_win32_file_error (err));
379 if(bytesread!=NULL) {
387 SetLastError (ERROR_NOT_SUPPORTED);
390 if (overlapped == NULL || file_private_handle->callback == NULL) {
391 SetLastError (ERROR_INVALID_PARAMETER);
396 int fd = file_private_handle->fd_mapped.fd;
399 notifier_data_t *ndata;
401 ndata = g_new0 (notifier_data_t, 1);
402 aio = g_new0 (struct aiocb, 1);
403 ndata->overlapped = overlapped;
405 ndata->callback = file_private_handle->callback;
407 aio->aio_fildes = fd;
408 aio->aio_lio_opcode = LIO_READ;
409 aio->aio_nbytes = numbytes;
410 aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
411 aio->aio_buf = buffer;
412 aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
413 aio->aio_sigevent.sigev_notify_function = async_notifier;
414 SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
416 result = aio_read (aio);
418 _wapi_set_last_error_from_errno ();
422 result = aio_error (aio);
424 g_print ("aio_error (read) returned %d for %d\n", result, fd);
427 numbytes = aio_return (aio);
429 g_print ("numbytes %d for %d\n", numbytes, fd);
433 _wapi_set_last_error_from_errno ();
438 *bytesread = numbytes;
445 static gboolean file_write(gpointer handle, gconstpointer buffer,
446 guint32 numbytes, guint32 *byteswritten,
447 WapiOverlapped *overlapped G_GNUC_UNUSED)
449 struct _WapiHandle_file *file_handle;
450 struct _WapiHandlePrivate_file *file_private_handle;
454 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
455 (gpointer *)&file_handle,
456 (gpointer *)&file_private_handle);
458 g_warning (G_GNUC_PRETTY_FUNCTION
459 ": error looking up file handle %p", handle);
460 SetLastError (ERROR_INVALID_HANDLE);
464 if (file_private_handle->fd_mapped.assigned == FALSE) {
465 SetLastError (ERROR_INVALID_HANDLE);
469 if(byteswritten!=NULL) {
473 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
474 !(file_handle->fileaccess&GENERIC_ALL)) {
476 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
479 SetLastError (ERROR_ACCESS_DENIED);
483 if (file_private_handle->async == FALSE) {
486 /* Need to lock the region we're about to write to,
487 * because we only do advisory locking on POSIX
490 current_pos = lseek (file_private_handle->fd_mapped.fd,
492 if (current_pos == -1) {
494 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d lseek failed: %s", handle, file_private_handle->fd_mapped.fd, strerror (errno));
496 _wapi_set_last_error_from_errno ();
500 if (_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
501 current_pos, numbytes) == FALSE) {
502 /* The error has already been set */
507 ret=write(file_private_handle->fd_mapped.fd, buffer,
510 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
512 _wapi_unlock_file_region (file_private_handle->fd_mapped.fd,
513 current_pos, numbytes);
516 if (errno == EINTR) {
519 _wapi_set_last_error_from_errno ();
521 g_message(G_GNUC_PRETTY_FUNCTION
522 ": write of handle %p fd %d error: %s", handle,
523 file_private_handle->fd_mapped.fd,
530 if(byteswritten!=NULL) {
537 SetLastError (ERROR_NOT_SUPPORTED);
540 if (overlapped == NULL || file_private_handle->callback == NULL) {
541 SetLastError (ERROR_INVALID_PARAMETER);
546 int fd = file_private_handle->fd_mapped.fd;
549 notifier_data_t *ndata;
551 ndata = g_new0 (notifier_data_t, 1);
552 aio = g_new0 (struct aiocb, 1);
553 ndata->overlapped = overlapped;
555 ndata->callback = file_private_handle->callback;
557 aio->aio_fildes = fd;
558 aio->aio_lio_opcode = LIO_WRITE;
559 aio->aio_nbytes = numbytes;
560 aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
561 aio->aio_buf = (gpointer) buffer;
562 aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
563 aio->aio_sigevent.sigev_notify_function = async_notifier;
564 SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
566 result = aio_write (aio);
568 _wapi_set_last_error_from_errno ();
572 result = aio_error (aio);
574 g_print ("aio_error (write) returned %d for %d\n", result, fd);
577 numbytes = aio_return (aio);
579 g_print ("numbytes %d for %d\n", numbytes, fd);
583 _wapi_set_last_error_from_errno ();
588 *byteswritten = numbytes;
595 static gboolean file_flush(gpointer handle)
597 struct _WapiHandle_file *file_handle;
598 struct _WapiHandlePrivate_file *file_private_handle;
602 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
603 (gpointer *)&file_handle,
604 (gpointer *)&file_private_handle);
606 g_warning (G_GNUC_PRETTY_FUNCTION
607 ": error looking up file handle %p", handle);
608 SetLastError (ERROR_INVALID_HANDLE);
612 if (file_private_handle->fd_mapped.assigned == FALSE) {
613 SetLastError (ERROR_INVALID_HANDLE);
617 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
618 !(file_handle->fileaccess&GENERIC_ALL)) {
620 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);
623 SetLastError (ERROR_ACCESS_DENIED);
627 ret=fsync(file_private_handle->fd_mapped.fd);
630 g_message(G_GNUC_PRETTY_FUNCTION
631 ": fsync of handle %p fd %d error: %s", handle,
632 file_private_handle->fd_mapped.fd, strerror(errno));
635 _wapi_set_last_error_from_errno ();
642 static guint32 file_seek(gpointer handle, gint32 movedistance,
643 gint32 *highmovedistance, WapiSeekMethod method)
645 struct _WapiHandle_file *file_handle;
646 struct _WapiHandlePrivate_file *file_private_handle;
648 off_t offset, newpos;
652 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
653 (gpointer *)&file_handle,
654 (gpointer *)&file_private_handle);
656 g_warning (G_GNUC_PRETTY_FUNCTION
657 ": error looking up file handle %p", handle);
658 SetLastError (ERROR_INVALID_HANDLE);
659 return(INVALID_SET_FILE_POINTER);
662 if (file_private_handle->fd_mapped.assigned == FALSE) {
663 SetLastError (ERROR_INVALID_HANDLE);
667 if(!(file_handle->fileaccess&GENERIC_READ) &&
668 !(file_handle->fileaccess&GENERIC_WRITE) &&
669 !(file_handle->fileaccess&GENERIC_ALL)) {
671 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);
674 SetLastError (ERROR_ACCESS_DENIED);
675 return(INVALID_SET_FILE_POINTER);
690 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
694 SetLastError (ERROR_INVALID_PARAMETER);
695 return(INVALID_SET_FILE_POINTER);
698 #ifdef HAVE_LARGE_FILE_SUPPORT
699 if(highmovedistance==NULL) {
702 g_message(G_GNUC_PRETTY_FUNCTION
703 ": setting offset to %lld (low %d)", offset,
707 offset=((gint64) *highmovedistance << 32) | (unsigned long)movedistance;
710 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);
718 #ifdef HAVE_LARGE_FILE_SUPPORT
719 g_message(G_GNUC_PRETTY_FUNCTION
720 ": moving handle %p fd %d by %lld bytes from %d", handle,
721 file_private_handle->fd_mapped.fd, offset, whence);
723 g_message(G_GNUC_PRETTY_FUNCTION
724 ": moving handle %p fd %d by %ld bytes from %d", handle,
725 file_private_handle->fd_mapped.fd, offset, whence);
729 newpos=lseek(file_private_handle->fd_mapped.fd, offset, whence);
732 g_message(G_GNUC_PRETTY_FUNCTION
733 ": lseek on handle %p fd %d returned error %s",
734 handle, file_private_handle->fd_mapped.fd,
738 _wapi_set_last_error_from_errno ();
739 return(INVALID_SET_FILE_POINTER);
743 #ifdef HAVE_LARGE_FILE_SUPPORT
744 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
746 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
750 #ifdef HAVE_LARGE_FILE_SUPPORT
751 ret=newpos & 0xFFFFFFFF;
752 if(highmovedistance!=NULL) {
753 *highmovedistance=newpos>>32;
757 if(highmovedistance!=NULL) {
758 /* Accurate, but potentially dodgy :-) */
764 g_message(G_GNUC_PRETTY_FUNCTION
765 ": move of handle %p fd %d returning %d/%d", handle,
766 file_private_handle->fd_mapped.fd, ret,
767 highmovedistance==NULL?0:*highmovedistance);
773 static gboolean file_setendoffile(gpointer handle)
775 struct _WapiHandle_file *file_handle;
776 struct _WapiHandlePrivate_file *file_private_handle;
782 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
783 (gpointer *)&file_handle,
784 (gpointer *)&file_private_handle);
786 g_warning (G_GNUC_PRETTY_FUNCTION
787 ": error looking up file handle %p", handle);
788 SetLastError (ERROR_INVALID_HANDLE);
792 if (file_private_handle->fd_mapped.assigned == FALSE) {
793 SetLastError (ERROR_INVALID_HANDLE);
797 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
798 !(file_handle->fileaccess&GENERIC_ALL)) {
800 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);
803 SetLastError (ERROR_ACCESS_DENIED);
807 /* Find the current file position, and the file length. If
808 * the file position is greater than the length, write to
809 * extend the file with a hole. If the file position is less
810 * than the length, truncate the file.
813 ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
816 g_message(G_GNUC_PRETTY_FUNCTION
817 ": handle %p fd %d fstat failed: %s", handle,
818 file_private_handle->fd_mapped.fd, strerror(errno));
821 _wapi_set_last_error_from_errno ();
824 size=statbuf.st_size;
826 pos=lseek(file_private_handle->fd_mapped.fd, (off_t)0, SEEK_CUR);
829 g_message(G_GNUC_PRETTY_FUNCTION
830 ": handle %p fd %d lseek failed: %s", handle,
831 file_private_handle->fd_mapped.fd, strerror(errno));
834 _wapi_set_last_error_from_errno ();
839 /* Extend the file. Use write() here, because some
840 * manuals say that ftruncate() behaviour is undefined
841 * when the file needs extending. The POSIX spec says
842 * that on XSI-conformant systems it extends, so if
843 * every system we care about conforms, then we can
847 ret=write(file_private_handle->fd_mapped.fd, "", 1);
849 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
853 g_message(G_GNUC_PRETTY_FUNCTION
854 ": handle %p fd %d extend write failed: %s",
855 handle, file_private_handle->fd_mapped.fd,
859 _wapi_set_last_error_from_errno ();
864 /* always truncate, because the extend write() adds an extra
865 * byte to the end of the file
868 ret=ftruncate(file_private_handle->fd_mapped.fd, pos);
870 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
874 g_message(G_GNUC_PRETTY_FUNCTION
875 ": handle %p fd %d ftruncate failed: %s", handle,
876 file_private_handle->fd_mapped.fd, strerror(errno));
879 _wapi_set_last_error_from_errno ();
886 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
888 struct _WapiHandle_file *file_handle;
889 struct _WapiHandlePrivate_file *file_private_handle;
895 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
896 (gpointer *)&file_handle,
897 (gpointer *)&file_private_handle);
899 g_warning (G_GNUC_PRETTY_FUNCTION
900 ": error looking up file handle %p", handle);
901 SetLastError (ERROR_INVALID_HANDLE);
902 return(INVALID_FILE_SIZE);
905 if (file_private_handle->fd_mapped.assigned == FALSE) {
906 SetLastError (ERROR_INVALID_HANDLE);
910 if(!(file_handle->fileaccess&GENERIC_READ) &&
911 !(file_handle->fileaccess&GENERIC_WRITE) &&
912 !(file_handle->fileaccess&GENERIC_ALL)) {
914 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);
917 SetLastError (ERROR_ACCESS_DENIED);
918 return(INVALID_FILE_SIZE);
921 ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
924 g_message(G_GNUC_PRETTY_FUNCTION
925 ": handle %p fd %d fstat failed: %s", handle,
926 file_private_handle->fd_mapped.fd, strerror(errno));
929 _wapi_set_last_error_from_errno ();
930 return(INVALID_FILE_SIZE);
933 #ifdef HAVE_LARGE_FILE_SUPPORT
934 size=statbuf.st_size & 0xFFFFFFFF;
936 *highsize=statbuf.st_size>>32;
940 /* Accurate, but potentially dodgy :-) */
943 size=statbuf.st_size;
947 g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
954 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
955 WapiFileTime *last_access,
956 WapiFileTime *last_write)
958 struct _WapiHandle_file *file_handle;
959 struct _WapiHandlePrivate_file *file_private_handle;
962 guint64 create_ticks, access_ticks, write_ticks;
965 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
966 (gpointer *)&file_handle,
967 (gpointer *)&file_private_handle);
969 g_warning (G_GNUC_PRETTY_FUNCTION
970 ": error looking up file handle %p", handle);
971 SetLastError (ERROR_INVALID_HANDLE);
975 if (file_private_handle->fd_mapped.assigned == FALSE) {
976 SetLastError (ERROR_INVALID_HANDLE);
980 if(!(file_handle->fileaccess&GENERIC_READ) &&
981 !(file_handle->fileaccess&GENERIC_ALL)) {
983 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);
986 SetLastError (ERROR_ACCESS_DENIED);
990 ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
993 g_message(G_GNUC_PRETTY_FUNCTION
994 ": handle %p fd %d fstat failed: %s", handle,
995 file_private_handle->fd_mapped.fd, strerror(errno));
998 _wapi_set_last_error_from_errno ();
1003 g_message(G_GNUC_PRETTY_FUNCTION
1004 ": atime: %ld ctime: %ld mtime: %ld",
1005 statbuf.st_atime, statbuf.st_ctime,
1009 /* Try and guess a meaningful create time by using the older
1012 /* The magic constant comes from msdn documentation
1013 * "Converting a time_t Value to a File Time"
1015 if(statbuf.st_atime < statbuf.st_ctime) {
1016 create_ticks=((guint64)statbuf.st_atime*10000000)
1017 + 116444736000000000ULL;
1019 create_ticks=((guint64)statbuf.st_ctime*10000000)
1020 + 116444736000000000ULL;
1023 access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
1024 write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
1027 g_message(G_GNUC_PRETTY_FUNCTION
1028 ": aticks: %llu cticks: %llu wticks: %llu",
1029 access_ticks, create_ticks, write_ticks);
1032 if(create_time!=NULL) {
1033 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
1034 create_time->dwHighDateTime = create_ticks >> 32;
1037 if(last_access!=NULL) {
1038 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
1039 last_access->dwHighDateTime = access_ticks >> 32;
1042 if(last_write!=NULL) {
1043 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
1044 last_write->dwHighDateTime = write_ticks >> 32;
1050 static gboolean file_setfiletime(gpointer handle,
1051 const WapiFileTime *create_time G_GNUC_UNUSED,
1052 const WapiFileTime *last_access,
1053 const WapiFileTime *last_write)
1055 struct _WapiHandle_file *file_handle;
1056 struct _WapiHandlePrivate_file *file_private_handle;
1059 struct utimbuf utbuf;
1060 struct stat statbuf;
1061 guint64 access_ticks, write_ticks;
1064 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1065 (gpointer *)&file_handle,
1066 (gpointer *)&file_private_handle);
1068 g_warning (G_GNUC_PRETTY_FUNCTION
1069 ": error looking up file handle %p", handle);
1070 SetLastError (ERROR_INVALID_HANDLE);
1074 if (file_private_handle->fd_mapped.assigned == FALSE) {
1075 SetLastError (ERROR_INVALID_HANDLE);
1079 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
1080 !(file_handle->fileaccess&GENERIC_ALL)) {
1082 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);
1085 SetLastError (ERROR_ACCESS_DENIED);
1089 if(file_handle->filename==0) {
1091 g_message(G_GNUC_PRETTY_FUNCTION
1092 ": handle %p fd %d unknown filename", handle,
1093 file_private_handle->fd_mapped.fd);
1096 SetLastError (ERROR_INVALID_HANDLE);
1100 /* Get the current times, so we can put the same times back in
1101 * the event that one of the FileTime structs is NULL
1103 ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
1106 g_message(G_GNUC_PRETTY_FUNCTION
1107 ": handle %p fd %d fstat failed: %s", handle,
1108 file_private_handle->fd_mapped.fd, strerror(errno));
1111 SetLastError (ERROR_INVALID_PARAMETER);
1115 if(last_access!=NULL) {
1116 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
1117 last_access->dwLowDateTime;
1118 /* This is (time_t)0. We can actually go to INT_MIN,
1119 * but this will do for now.
1121 if (access_ticks < 116444736000000000ULL) {
1123 g_message (G_GNUC_PRETTY_FUNCTION
1124 ": attempt to set access time too early");
1126 SetLastError (ERROR_INVALID_PARAMETER);
1130 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1132 utbuf.actime=statbuf.st_atime;
1135 if(last_write!=NULL) {
1136 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
1137 last_write->dwLowDateTime;
1138 /* This is (time_t)0. We can actually go to INT_MIN,
1139 * but this will do for now.
1141 if (write_ticks < 116444736000000000ULL) {
1143 g_message (G_GNUC_PRETTY_FUNCTION
1144 ": attempt to set write time too early");
1146 SetLastError (ERROR_INVALID_PARAMETER);
1150 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1152 utbuf.modtime=statbuf.st_mtime;
1156 g_message(G_GNUC_PRETTY_FUNCTION
1157 ": setting handle %p access %ld write %ld", handle,
1158 utbuf.actime, utbuf.modtime);
1161 name=_wapi_handle_scratch_lookup (file_handle->filename);
1163 ret=utime(name, &utbuf);
1166 g_message(G_GNUC_PRETTY_FUNCTION
1167 ": handle %p [%s] fd %d utime failed: %s", handle,
1168 name, file_private_handle->fd_mapped.fd,
1173 SetLastError (ERROR_INVALID_PARAMETER);
1182 static void console_close_shared (gpointer handle)
1184 struct _WapiHandle_file *console_handle;
1187 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1188 (gpointer *)&console_handle, NULL);
1190 g_warning (G_GNUC_PRETTY_FUNCTION
1191 ": error looking up console handle %p", handle);
1192 SetLastError (ERROR_INVALID_HANDLE);
1197 g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
1200 if(console_handle->filename!=0) {
1201 _wapi_handle_scratch_delete (console_handle->filename);
1202 console_handle->filename=0;
1204 if(console_handle->security_attributes!=0) {
1205 _wapi_handle_scratch_delete (console_handle->security_attributes);
1206 console_handle->security_attributes=0;
1210 static void console_close_private (gpointer handle)
1212 struct _WapiHandlePrivate_file *console_private_handle;
1215 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
1216 (gpointer *)&console_private_handle);
1218 g_warning (G_GNUC_PRETTY_FUNCTION
1219 ": error looking up console handle %p", handle);
1220 SetLastError (ERROR_INVALID_HANDLE);
1224 if (console_private_handle->fd_mapped.assigned == TRUE) {
1226 g_message(G_GNUC_PRETTY_FUNCTION
1227 ": closing console handle %p with fd %d", handle,
1228 console_private_handle->fd_mapped.fd);
1231 /* Blank out the mapping, to make catching errors easier */
1232 _wapi_handle_fd_offset_store (console_private_handle->fd_mapped.fd, NULL);
1234 close(console_private_handle->fd_mapped.fd);
1238 static WapiFileType console_getfiletype(void)
1240 return(FILE_TYPE_CHAR);
1243 static gboolean console_read(gpointer handle, gpointer buffer,
1244 guint32 numbytes, guint32 *bytesread,
1245 WapiOverlapped *overlapped G_GNUC_UNUSED)
1247 struct _WapiHandle_file *console_handle;
1248 struct _WapiHandlePrivate_file *console_private_handle;
1252 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1253 (gpointer *)&console_handle,
1254 (gpointer *)&console_private_handle);
1256 g_warning (G_GNUC_PRETTY_FUNCTION
1257 ": error looking up console handle %p", handle);
1258 SetLastError (ERROR_INVALID_HANDLE);
1262 if (console_private_handle->fd_mapped.assigned == FALSE) {
1263 SetLastError (ERROR_INVALID_HANDLE);
1267 if(bytesread!=NULL) {
1271 if(!(console_handle->fileaccess&GENERIC_READ) &&
1272 !(console_handle->fileaccess&GENERIC_ALL)) {
1274 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);
1277 SetLastError (ERROR_ACCESS_DENIED);
1282 ret=read(console_private_handle->fd_mapped.fd, buffer,
1285 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1289 g_message(G_GNUC_PRETTY_FUNCTION
1290 ": read of handle %p fd %d error: %s", handle,
1291 console_private_handle->fd_mapped.fd,
1295 _wapi_set_last_error_from_errno ();
1299 if(bytesread!=NULL) {
1306 static gboolean console_write(gpointer handle, gconstpointer buffer,
1307 guint32 numbytes, guint32 *byteswritten,
1308 WapiOverlapped *overlapped G_GNUC_UNUSED)
1310 struct _WapiHandle_file *console_handle;
1311 struct _WapiHandlePrivate_file *console_private_handle;
1315 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1316 (gpointer *)&console_handle,
1317 (gpointer *)&console_private_handle);
1319 g_warning (G_GNUC_PRETTY_FUNCTION
1320 ": error looking up console handle %p", handle);
1321 SetLastError (ERROR_INVALID_HANDLE);
1325 if (console_private_handle->fd_mapped.assigned == FALSE) {
1326 SetLastError (ERROR_INVALID_HANDLE);
1330 if(byteswritten!=NULL) {
1334 if(!(console_handle->fileaccess&GENERIC_WRITE) &&
1335 !(console_handle->fileaccess&GENERIC_ALL)) {
1337 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);
1340 SetLastError (ERROR_ACCESS_DENIED);
1345 ret=write(console_private_handle->fd_mapped.fd, buffer,
1348 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1351 if (errno == EINTR) {
1354 _wapi_set_last_error_from_errno ();
1356 g_message(G_GNUC_PRETTY_FUNCTION
1357 ": write of handle %p fd %d error: %s", handle,
1358 console_private_handle->fd_mapped.fd,
1365 if(byteswritten!=NULL) {
1372 static void pipe_close_shared (gpointer handle)
1374 struct _WapiHandle_file *pipe_handle;
1377 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1378 (gpointer *)&pipe_handle, NULL);
1380 g_warning (G_GNUC_PRETTY_FUNCTION
1381 ": error looking up pipe handle %p", handle);
1382 SetLastError (ERROR_INVALID_HANDLE);
1387 g_message(G_GNUC_PRETTY_FUNCTION ": closing pipe handle %p", handle);
1390 if(pipe_handle->filename!=0) {
1391 _wapi_handle_scratch_delete (pipe_handle->filename);
1392 pipe_handle->filename=0;
1394 if(pipe_handle->security_attributes!=0) {
1395 _wapi_handle_scratch_delete (pipe_handle->security_attributes);
1396 pipe_handle->security_attributes=0;
1400 static void pipe_close_private (gpointer handle)
1402 struct _WapiHandlePrivate_file *pipe_private_handle;
1405 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
1406 (gpointer *)&pipe_private_handle);
1408 g_warning (G_GNUC_PRETTY_FUNCTION
1409 ": error looking up pipe handle %p", handle);
1410 SetLastError (ERROR_INVALID_HANDLE);
1414 if (pipe_private_handle->fd_mapped.assigned == TRUE) {
1416 g_message(G_GNUC_PRETTY_FUNCTION
1417 ": closing pipe handle %p with fd %d", handle,
1418 pipe_private_handle->fd_mapped.fd);
1421 /* Blank out the mapping, to make catching errors easier */
1422 _wapi_handle_fd_offset_store (pipe_private_handle->fd_mapped.fd, NULL);
1424 close(pipe_private_handle->fd_mapped.fd);
1428 static WapiFileType pipe_getfiletype(void)
1430 return(FILE_TYPE_PIPE);
1433 static gboolean pipe_read (gpointer handle, gpointer buffer,
1434 guint32 numbytes, guint32 *bytesread,
1435 WapiOverlapped *overlapped G_GNUC_UNUSED)
1437 struct _WapiHandle_file *pipe_handle;
1438 struct _WapiHandlePrivate_file *pipe_private_handle;
1442 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1443 (gpointer *)&pipe_handle,
1444 (gpointer *)&pipe_private_handle);
1446 g_warning (G_GNUC_PRETTY_FUNCTION
1447 ": error looking up pipe handle %p", handle);
1448 SetLastError (ERROR_INVALID_HANDLE);
1452 if (pipe_private_handle->fd_mapped.assigned == FALSE) {
1453 SetLastError (ERROR_INVALID_HANDLE);
1457 if(bytesread!=NULL) {
1461 if(!(pipe_handle->fileaccess&GENERIC_READ) &&
1462 !(pipe_handle->fileaccess&GENERIC_ALL)) {
1464 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);
1467 SetLastError (ERROR_ACCESS_DENIED);
1472 g_message (G_GNUC_PRETTY_FUNCTION
1473 ": reading up to %d bytes from pipe %p (fd %d)", numbytes,
1474 handle, pipe_private_handle->fd_mapped.fd);
1478 ret=read(pipe_private_handle->fd_mapped.fd, buffer, numbytes);
1480 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1483 if (errno == EINTR) {
1486 _wapi_set_last_error_from_errno ();
1488 g_message(G_GNUC_PRETTY_FUNCTION
1489 ": read of handle %p fd %d error: %s", handle,
1490 pipe_private_handle->fd_mapped.fd, strerror(errno));
1498 g_message (G_GNUC_PRETTY_FUNCTION ": read %d bytes from pipe", ret);
1501 if(bytesread!=NULL) {
1508 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1509 guint32 numbytes, guint32 *byteswritten,
1510 WapiOverlapped *overlapped G_GNUC_UNUSED)
1512 struct _WapiHandle_file *pipe_handle;
1513 struct _WapiHandlePrivate_file *pipe_private_handle;
1517 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1518 (gpointer *)&pipe_handle,
1519 (gpointer *)&pipe_private_handle);
1521 g_warning (G_GNUC_PRETTY_FUNCTION
1522 ": error looking up pipe handle %p", handle);
1523 SetLastError (ERROR_INVALID_HANDLE);
1527 if (pipe_private_handle->fd_mapped.assigned == FALSE) {
1528 SetLastError (ERROR_INVALID_HANDLE);
1532 if(byteswritten!=NULL) {
1536 if(!(pipe_handle->fileaccess&GENERIC_WRITE) &&
1537 !(pipe_handle->fileaccess&GENERIC_ALL)) {
1539 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);
1542 SetLastError (ERROR_ACCESS_DENIED);
1547 g_message (G_GNUC_PRETTY_FUNCTION
1548 ": writing up to %d bytes to pipe %p (fd %d)", numbytes,
1549 handle, pipe_private_handle->fd_mapped.fd);
1553 ret=write(pipe_private_handle->fd_mapped.fd, buffer, numbytes);
1555 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1558 if (errno == EINTR) {
1561 _wapi_set_last_error_from_errno ();
1563 g_message(G_GNUC_PRETTY_FUNCTION
1564 ": write of handle %p fd %d error: %s", handle,
1565 pipe_private_handle->fd_mapped.fd, strerror(errno));
1571 if(byteswritten!=NULL) {
1578 static int convert_flags(guint32 fileaccess, guint32 createmode)
1582 switch(fileaccess) {
1589 case GENERIC_READ|GENERIC_WRITE:
1594 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
1600 switch(createmode) {
1602 flags|=O_CREAT|O_EXCL;
1605 flags|=O_CREAT|O_TRUNC;
1612 case TRUNCATE_EXISTING:
1617 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
1626 static guint32 convert_from_flags(int flags)
1628 guint32 fileaccess=0;
1631 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1634 if((flags & O_ACCMODE) == O_RDONLY) {
1635 fileaccess=GENERIC_READ;
1636 } else if ((flags & O_ACCMODE) == O_WRONLY) {
1637 fileaccess=GENERIC_WRITE;
1638 } else if ((flags & O_ACCMODE) == O_RDWR) {
1639 fileaccess=GENERIC_READ|GENERIC_WRITE;
1642 g_message(G_GNUC_PRETTY_FUNCTION
1643 ": Can't figure out flags 0x%x", flags);
1647 /* Maybe sort out create mode too */
1652 static mode_t convert_perms(guint32 sharemode)
1656 if(sharemode&FILE_SHARE_READ) {
1659 if(sharemode&FILE_SHARE_WRITE) {
1669 * @name: a pointer to a NULL-terminated unicode string, that names
1670 * the file or other object to create.
1671 * @fileaccess: specifies the file access mode
1672 * @sharemode: whether the file should be shared. This parameter is
1673 * currently ignored.
1674 * @security: Ignored for now.
1675 * @createmode: specifies whether to create a new file, whether to
1676 * overwrite an existing file, whether to truncate the file, etc.
1677 * @attrs: specifies file attributes and flags. On win32 attributes
1678 * are characteristics of the file, not the handle, and are ignored
1679 * when an existing file is opened. Flags give the library hints on
1680 * how to process a file to optimise performance.
1681 * @template: the handle of an open %GENERIC_READ file that specifies
1682 * attributes to apply to a newly created file, ignoring @attrs.
1683 * Normally this parameter is NULL. This parameter is ignored when an
1684 * existing file is opened.
1686 * Creates a new file handle. This only applies to normal files:
1687 * pipes are handled by CreatePipe(), and console handles are created
1688 * with GetStdHandle().
1690 * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1692 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1693 guint32 sharemode, WapiSecurityAttributes *security,
1694 guint32 createmode, guint32 attrs,
1695 gpointer template G_GNUC_UNUSED)
1697 struct _WapiHandle_file *file_handle;
1698 struct _WapiHandlePrivate_file *file_private_handle;
1701 int flags=convert_flags(fileaccess, createmode);
1702 /*mode_t perms=convert_perms(sharemode);*/
1703 /* we don't use sharemode, because that relates to sharing of the file
1704 * when the file is open and is already handled by other code, perms instead
1705 * are the on-disk permissions and this is a sane default.
1711 gpointer cf_ret = INVALID_HANDLE_VALUE;
1712 struct stat statbuf;
1713 gboolean file_already_shared;
1714 guint32 file_existing_share, file_existing_access;
1716 mono_once (&io_ops_once, io_ops_init);
1720 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1723 SetLastError (ERROR_INVALID_NAME);
1724 return(INVALID_HANDLE_VALUE);
1727 filename=mono_unicode_to_external (name);
1728 if(filename==NULL) {
1730 g_message(G_GNUC_PRETTY_FUNCTION
1731 ": unicode conversion returned NULL");
1734 SetLastError (ERROR_INVALID_NAME);
1735 return(INVALID_HANDLE_VALUE);
1739 g_message (G_GNUC_PRETTY_FUNCTION ": Opening %s with share 0x%x and access 0x%x", filename, sharemode, fileaccess);
1742 fd = open(filename, flags, perms);
1744 /* If we were trying to open a directory with write permissions
1745 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1746 * EISDIR. However, this is a bit bogus because calls to
1747 * manipulate the directory (e.g. SetFileTime) will still work on
1748 * the directory because they use other API calls
1749 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1750 * to open the directory again without write permission.
1752 if (fd == -1 && errno == EISDIR)
1754 /* Try again but don't try to make it writable */
1755 fd = open(filename, flags & ~(O_RDWR|O_WRONLY), perms);
1760 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
1761 filename, strerror(errno));
1763 _wapi_set_last_error_from_errno ();
1766 return(INVALID_HANDLE_VALUE);
1769 if (fd >= _wapi_fd_offset_table_size) {
1771 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
1774 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1779 return(INVALID_HANDLE_VALUE);
1782 ret = fstat (fd, &statbuf);
1785 g_message (G_GNUC_PRETTY_FUNCTION ": fstat error of file %s: %s", filename, strerror (errno));
1787 _wapi_set_last_error_from_errno ();
1791 return(INVALID_HANDLE_VALUE);
1794 file_already_shared = _wapi_handle_get_or_set_share (statbuf.st_dev, statbuf.st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access);
1796 if (file_already_shared) {
1797 if (file_existing_share == 0) {
1798 /* Quick and easy, no possibility to share */
1800 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing = NONE", fileaccess);
1802 SetLastError (ERROR_SHARING_VIOLATION);
1806 return(INVALID_HANDLE_VALUE);
1809 if (((file_existing_share == FILE_SHARE_READ) &&
1810 (fileaccess != GENERIC_READ)) ||
1811 ((file_existing_share == FILE_SHARE_WRITE) &&
1812 (fileaccess != GENERIC_WRITE))) {
1813 /* New access mode doesn't match up */
1815 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", fileaccess, file_existing_share);
1817 SetLastError (ERROR_SHARING_VIOLATION);
1821 return(INVALID_HANDLE_VALUE);
1824 if (((file_existing_access & GENERIC_READ) &&
1825 !(sharemode & FILE_SHARE_READ)) ||
1826 ((file_existing_access & GENERIC_WRITE) &&
1827 !(sharemode & FILE_SHARE_WRITE))) {
1828 /* New share mode doesn't match up */
1830 g_message (G_GNUC_PRETTY_FUNCTION ": Access mode prevents open: requested share: 0x%x, file has access: 0x%x", sharemode, file_existing_access);
1832 SetLastError (ERROR_SHARING_VIOLATION);
1836 return(INVALID_HANDLE_VALUE);
1840 g_message (G_GNUC_PRETTY_FUNCTION ": New file!");
1844 handle=_wapi_handle_new (WAPI_HANDLE_FILE);
1845 if(handle==_WAPI_HANDLE_INVALID) {
1846 g_warning (G_GNUC_PRETTY_FUNCTION
1847 ": error creating file handle");
1851 SetLastError (ERROR_GEN_FAILURE);
1852 return(INVALID_HANDLE_VALUE);
1855 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
1857 thr_ret = _wapi_handle_lock_handle (handle);
1858 g_assert (thr_ret == 0);
1860 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1861 (gpointer *)&file_handle,
1862 (gpointer *)&file_private_handle);
1864 g_warning (G_GNUC_PRETTY_FUNCTION
1865 ": error looking up file handle %p", handle);
1866 SetLastError (ERROR_INVALID_HANDLE);
1871 _wapi_handle_fd_offset_store (fd, handle);
1872 cf_ret = GINT_TO_POINTER (fd);
1874 file_private_handle->fd_mapped.fd=fd;
1875 file_private_handle->fd_mapped.assigned=TRUE;
1876 file_private_handle->async = ((attrs & FILE_FLAG_OVERLAPPED) != 0);
1877 file_handle->filename=_wapi_handle_scratch_store (filename,
1879 if(security!=NULL) {
1880 file_handle->security_attributes=_wapi_handle_scratch_store (
1881 security, sizeof(WapiSecurityAttributes));
1884 file_handle->fileaccess=fileaccess;
1885 file_handle->sharemode=sharemode;
1886 file_handle->attrs=attrs;
1887 file_handle->device = statbuf.st_dev;
1888 file_handle->inode = statbuf.st_ino;
1891 g_message(G_GNUC_PRETTY_FUNCTION
1892 ": returning handle %p with fd %d", handle,
1893 file_private_handle->fd_mapped.fd);
1897 thr_ret = _wapi_handle_unlock_handle (handle);
1898 g_assert (thr_ret == 0);
1899 pthread_cleanup_pop (0);
1908 * @name: a pointer to a NULL-terminated unicode string, that names
1909 * the file to be deleted.
1911 * Deletes file @name.
1913 * Return value: %TRUE on success, %FALSE otherwise.
1915 gboolean DeleteFile(const gunichar2 *name)
1922 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1925 SetLastError (ERROR_INVALID_NAME);
1929 filename=mono_unicode_to_external(name);
1930 if(filename==NULL) {
1932 g_message(G_GNUC_PRETTY_FUNCTION
1933 ": unicode conversion returned NULL");
1936 SetLastError (ERROR_INVALID_NAME);
1940 ret=unlink(filename);
1948 _wapi_set_last_error_from_errno ();
1954 * @name: a pointer to a NULL-terminated unicode string, that names
1955 * the file to be moved.
1956 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1957 * new name for the file.
1959 * Renames file @name to @dest_name.
1960 * MoveFile sets ERROR_ALREADY_EXISTS if the destination exists, except
1961 * when it is the same file as the source. In that case it silently succeeds.
1963 * Return value: %TRUE on success, %FALSE otherwise.
1965 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1967 gchar *utf8_name, *utf8_dest_name;
1969 struct stat stat_src, stat_dest;
1973 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1976 SetLastError (ERROR_INVALID_NAME);
1980 utf8_name = mono_unicode_to_external (name);
1981 if (utf8_name == NULL) {
1983 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1986 SetLastError (ERROR_INVALID_NAME);
1990 if(dest_name==NULL) {
1992 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1996 SetLastError (ERROR_INVALID_NAME);
2000 utf8_dest_name = mono_unicode_to_external (dest_name);
2001 if (utf8_dest_name == NULL) {
2003 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2007 SetLastError (ERROR_INVALID_NAME);
2012 * In C# land we check for the existence of src, but not for dest.
2013 * We check it here and return the failure if dest exists and is not
2014 * the same file as src.
2016 if (!stat (utf8_dest_name, &stat_dest) && !stat (utf8_name, &stat_src)) {
2017 if (stat_dest.st_dev != stat_src.st_dev || stat_dest.st_ino != stat_src.st_ino) {
2018 SetLastError (ERROR_ALREADY_EXISTS);
2023 result = rename (utf8_name, utf8_dest_name);
2025 g_free (utf8_dest_name);
2027 if (result != 0 && errno == EXDEV) {
2028 /* Try a copy to the new location, and delete the source */
2029 if (CopyFile (name, dest_name, TRUE)==FALSE) {
2030 /* CopyFile will set the error */
2034 return(DeleteFile (name));
2043 SetLastError (ERROR_ALREADY_EXISTS);
2047 _wapi_set_last_error_from_errno ();
2056 * @name: a pointer to a NULL-terminated unicode string, that names
2057 * the file to be copied.
2058 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
2059 * new name for the file.
2060 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
2062 * Copies file @name to @dest_name
2064 * Return value: %TRUE on success, %FALSE otherwise.
2066 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
2067 gboolean fail_if_exists)
2069 gchar *utf8_src, *utf8_dest;
2070 int src_fd, dest_fd;
2078 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2081 SetLastError (ERROR_INVALID_NAME);
2085 utf8_src = mono_unicode_to_external (name);
2086 if (utf8_src == NULL) {
2088 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of source returned NULL");
2091 SetLastError (ERROR_INVALID_PARAMETER);
2095 if(dest_name==NULL) {
2097 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2101 SetLastError (ERROR_INVALID_NAME);
2105 utf8_dest = mono_unicode_to_external (dest_name);
2106 if (utf8_dest == NULL) {
2108 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of dest returned NULL");
2111 SetLastError (ERROR_INVALID_PARAMETER);
2118 src_fd = open (utf8_src, O_RDONLY);
2120 _wapi_set_last_error_from_errno ();
2128 if (fstat (src_fd, &st) < 0) {
2129 _wapi_set_last_error_from_errno ();
2138 if (fail_if_exists) {
2139 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
2141 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2143 /* O_TRUNC might cause a fail if the file
2146 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
2151 _wapi_set_last_error_from_errno ();
2160 buf_size = st.st_blksize;
2161 buf = (char *) alloca (buf_size);
2164 remain = read (src_fd, buf, buf_size);
2167 if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
2171 _wapi_set_last_error_from_errno ();
2185 while (remain > 0) {
2186 if ((n = write (dest_fd, buf, remain)) < 0) {
2187 if (errno == EINTR && !_wapi_thread_cur_apc_pending())
2190 _wapi_set_last_error_from_errno ();
2192 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
2215 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
2216 static gpointer stdin_handle=INVALID_HANDLE_VALUE;
2217 static gpointer stdout_handle=INVALID_HANDLE_VALUE;
2218 static gpointer stderr_handle=INVALID_HANDLE_VALUE;
2220 static gpointer stdhandle_create (int fd, const guchar *name)
2222 struct _WapiHandle_file *file_handle;
2223 struct _WapiHandlePrivate_file *file_private_handle;
2225 gpointer handle, ret = INVALID_HANDLE_VALUE;
2230 g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
2234 /* Check if fd is valid */
2236 flags=fcntl(fd, F_GETFL);
2238 while (flags==-1 && errno==EINTR);
2241 /* Invalid fd. Not really much point checking for EBADF
2245 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
2246 fd, strerror(errno));
2249 _wapi_set_last_error_from_errno ();
2250 return(INVALID_HANDLE_VALUE);
2253 handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
2254 if(handle==_WAPI_HANDLE_INVALID) {
2255 g_warning (G_GNUC_PRETTY_FUNCTION
2256 ": error creating file handle");
2257 SetLastError (ERROR_GEN_FAILURE);
2258 return(INVALID_HANDLE_VALUE);
2261 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2263 thr_ret = _wapi_handle_lock_handle (handle);
2264 g_assert (thr_ret == 0);
2266 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2267 (gpointer *)&file_handle,
2268 (gpointer *)&file_private_handle);
2270 g_warning (G_GNUC_PRETTY_FUNCTION
2271 ": error looking up console handle %p", handle);
2272 SetLastError (ERROR_INVALID_HANDLE);
2276 /* We know this is fd 0, 1 or 2 */
2277 _wapi_handle_fd_offset_store (fd, handle);
2278 ret = GINT_TO_POINTER (fd);
2280 file_private_handle->fd_mapped.fd=fd;
2281 file_private_handle->fd_mapped.assigned=TRUE;
2282 file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
2283 /* some default security attributes might be needed */
2284 file_handle->security_attributes=0;
2285 file_handle->fileaccess=convert_from_flags(flags);
2286 file_handle->sharemode=0;
2287 file_handle->attrs=0;
2290 g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
2291 handle, file_private_handle->fd_mapped.fd);
2295 thr_ret = _wapi_handle_unlock_handle (handle);
2296 g_assert (thr_ret == 0);
2297 pthread_cleanup_pop (0);
2302 static void stdhandle_init (void)
2304 stdin_handle=stdhandle_create (0, "<stdin>");
2305 stdout_handle=stdhandle_create (1, "<stdout>");
2306 stderr_handle=stdhandle_create (2, "<stderr>");
2311 * @stdhandle: specifies the file descriptor
2313 * Returns a handle for stdin, stdout, or stderr. Always returns the
2314 * same handle for the same @stdhandle.
2316 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2319 gpointer GetStdHandle(WapiStdHandle stdhandle)
2323 mono_once (&io_ops_once, io_ops_init);
2324 mono_once (&stdhandle_once, stdhandle_init);
2327 case STD_INPUT_HANDLE:
2328 handle=stdin_handle;
2331 case STD_OUTPUT_HANDLE:
2332 handle=stdout_handle;
2335 case STD_ERROR_HANDLE:
2336 handle=stderr_handle;
2341 g_message(G_GNUC_PRETTY_FUNCTION
2342 ": unknown standard handle type");
2345 SetLastError (ERROR_INVALID_PARAMETER);
2346 return(INVALID_HANDLE_VALUE);
2349 if (handle == INVALID_HANDLE_VALUE) {
2350 SetLastError (ERROR_NO_MORE_FILES);
2351 return(INVALID_HANDLE_VALUE);
2354 /* Add a reference to this handle */
2355 _wapi_handle_ref (_wapi_handle_fd_offset_to_handle (handle));
2362 * @handle: The file handle to read from. The handle must have
2363 * %GENERIC_READ access.
2364 * @buffer: The buffer to store read data in
2365 * @numbytes: The maximum number of bytes to read
2366 * @bytesread: The actual number of bytes read is stored here. This
2367 * value can be zero if the handle is positioned at the end of the
2369 * @overlapped: points to a required %WapiOverlapped structure if
2370 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2373 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2374 * function reads up to @numbytes bytes from the file from the current
2375 * file position, and stores them in @buffer. If there are not enough
2376 * bytes left in the file, just the amount available will be read.
2377 * The actual number of bytes read is stored in @bytesread.
2379 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2380 * file position is ignored and the read position is taken from data
2381 * in the @overlapped structure.
2383 * Return value: %TRUE if the read succeeds (even if no bytes were
2384 * read due to an attempt to read past the end of the file), %FALSE on
2387 gboolean ReadFile(gpointer fd_handle, gpointer buffer, guint32 numbytes,
2388 guint32 *bytesread, WapiOverlapped *overlapped)
2390 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2391 WapiHandleType type;
2393 if (handle == NULL) {
2394 SetLastError (ERROR_INVALID_HANDLE);
2398 type = _wapi_handle_type (handle);
2400 if(io_ops[type].readfile==NULL) {
2401 SetLastError (ERROR_INVALID_HANDLE);
2405 return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2411 * @handle: The file handle to write to. The handle must have
2412 * %GENERIC_WRITE access.
2413 * @buffer: The buffer to read data from.
2414 * @numbytes: The maximum number of bytes to write.
2415 * @byteswritten: The actual number of bytes written is stored here.
2416 * If the handle is positioned at the file end, the length of the file
2417 * is extended. This parameter may be %NULL.
2418 * @overlapped: points to a required %WapiOverlapped structure if
2419 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2422 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2423 * function writes up to @numbytes bytes from @buffer to the file at
2424 * the current file position. If @handle is positioned at the end of
2425 * the file, the file is extended. The actual number of bytes written
2426 * is stored in @byteswritten.
2428 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2429 * file position is ignored and the write position is taken from data
2430 * in the @overlapped structure.
2432 * Return value: %TRUE if the write succeeds, %FALSE on error.
2434 gboolean WriteFile(gpointer fd_handle, gconstpointer buffer, guint32 numbytes,
2435 guint32 *byteswritten, WapiOverlapped *overlapped)
2437 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2438 WapiHandleType type;
2440 if (handle == NULL) {
2441 SetLastError (ERROR_INVALID_HANDLE);
2445 type = _wapi_handle_type (handle);
2447 if(io_ops[type].writefile==NULL) {
2448 SetLastError (ERROR_INVALID_HANDLE);
2452 return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2458 * @handle: Handle to open file. The handle must have
2459 * %GENERIC_WRITE access.
2461 * Flushes buffers of the file and causes all unwritten data to
2464 * Return value: %TRUE on success, %FALSE otherwise.
2466 gboolean FlushFileBuffers(gpointer fd_handle)
2468 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2469 WapiHandleType type;
2471 if (handle == NULL) {
2472 SetLastError (ERROR_INVALID_HANDLE);
2476 type = _wapi_handle_type (handle);
2478 if(io_ops[type].flushfile==NULL) {
2479 SetLastError (ERROR_INVALID_HANDLE);
2483 return(io_ops[type].flushfile (handle));
2488 * @handle: The file handle to set. The handle must have
2489 * %GENERIC_WRITE access.
2491 * Moves the end-of-file position to the current position of the file
2492 * pointer. This function is used to truncate or extend a file.
2494 * Return value: %TRUE on success, %FALSE otherwise.
2496 gboolean SetEndOfFile(gpointer fd_handle)
2498 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2499 WapiHandleType type;
2501 if (handle == NULL) {
2502 SetLastError (ERROR_INVALID_HANDLE);
2506 type = _wapi_handle_type (handle);
2508 if(io_ops[type].setendoffile==NULL) {
2509 SetLastError (ERROR_INVALID_HANDLE);
2513 return(io_ops[type].setendoffile (handle));
2518 * @handle: The file handle to set. The handle must have
2519 * %GENERIC_READ or %GENERIC_WRITE access.
2520 * @movedistance: Low 32 bits of a signed value that specifies the
2521 * number of bytes to move the file pointer.
2522 * @highmovedistance: Pointer to the high 32 bits of a signed value
2523 * that specifies the number of bytes to move the file pointer, or
2525 * @method: The starting point for the file pointer move.
2527 * Sets the file pointer of an open file.
2529 * The distance to move the file pointer is calculated from
2530 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2531 * @movedistance is the 32-bit signed value; otherwise, @movedistance
2532 * is the low 32 bits and @highmovedistance a pointer to the high 32
2533 * bits of a 64 bit signed value. A positive distance moves the file
2534 * pointer forward from the position specified by @method; a negative
2535 * distance moves the file pointer backward.
2537 * If the library is compiled without large file support,
2538 * @highmovedistance is ignored and its value is set to zero on a
2539 * successful return.
2541 * Return value: On success, the low 32 bits of the new file pointer.
2542 * If @highmovedistance is not %NULL, the high 32 bits of the new file
2543 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
2545 guint32 SetFilePointer(gpointer fd_handle, gint32 movedistance,
2546 gint32 *highmovedistance, WapiSeekMethod method)
2548 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2549 WapiHandleType type;
2551 if (handle == NULL) {
2552 SetLastError (ERROR_INVALID_HANDLE);
2553 return(INVALID_SET_FILE_POINTER);
2556 type = _wapi_handle_type (handle);
2558 if(io_ops[type].seek==NULL) {
2559 SetLastError (ERROR_INVALID_HANDLE);
2560 return(INVALID_SET_FILE_POINTER);
2563 return(io_ops[type].seek (handle, movedistance, highmovedistance,
2569 * @handle: The file handle to test.
2571 * Finds the type of file @handle.
2573 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2574 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
2575 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2576 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2578 WapiFileType GetFileType(gpointer fd_handle)
2580 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2581 WapiHandleType type;
2583 if (handle == NULL) {
2584 SetLastError (ERROR_INVALID_HANDLE);
2585 return(FILE_TYPE_UNKNOWN);
2588 type = _wapi_handle_type (handle);
2590 if(io_ops[type].getfiletype==NULL) {
2591 SetLastError (ERROR_INVALID_HANDLE);
2592 return(FILE_TYPE_UNKNOWN);
2595 return(io_ops[type].getfiletype ());
2600 * @handle: The file handle to query. The handle must have
2601 * %GENERIC_READ or %GENERIC_WRITE access.
2602 * @highsize: If non-%NULL, the high 32 bits of the file size are
2605 * Retrieves the size of the file @handle.
2607 * If the library is compiled without large file support, @highsize
2608 * has its value set to zero on a successful return.
2610 * Return value: On success, the low 32 bits of the file size. If
2611 * @highsize is non-%NULL then the high 32 bits of the file size are
2612 * stored here. On failure %INVALID_FILE_SIZE is returned.
2614 guint32 GetFileSize(gpointer fd_handle, guint32 *highsize)
2616 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2617 WapiHandleType type;
2619 if (handle == NULL) {
2620 SetLastError (ERROR_INVALID_HANDLE);
2621 return(INVALID_FILE_SIZE);
2624 type = _wapi_handle_type (handle);
2626 if(io_ops[type].getfilesize==NULL) {
2627 SetLastError (ERROR_INVALID_HANDLE);
2628 return(INVALID_FILE_SIZE);
2631 return(io_ops[type].getfilesize (handle, highsize));
2636 * @handle: The file handle to query. The handle must have
2637 * %GENERIC_READ access.
2638 * @create_time: Points to a %WapiFileTime structure to receive the
2639 * number of ticks since the epoch that file was created. May be
2641 * @last_access: Points to a %WapiFileTime structure to receive the
2642 * number of ticks since the epoch when file was last accessed. May be
2644 * @last_write: Points to a %WapiFileTime structure to receive the
2645 * number of ticks since the epoch when file was last written to. May
2648 * Finds the number of ticks since the epoch that the file referenced
2649 * by @handle was created, last accessed and last modified. A tick is
2650 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2653 * Create time isn't recorded on POSIX file systems or reported by
2654 * stat(2), so that time is guessed by returning the oldest of the
2657 * Return value: %TRUE on success, %FALSE otherwise.
2659 gboolean GetFileTime(gpointer fd_handle, WapiFileTime *create_time,
2660 WapiFileTime *last_access, WapiFileTime *last_write)
2662 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2663 WapiHandleType type;
2665 if (handle == NULL) {
2666 SetLastError (ERROR_INVALID_HANDLE);
2670 type = _wapi_handle_type (handle);
2672 if(io_ops[type].getfiletime==NULL) {
2673 SetLastError (ERROR_INVALID_HANDLE);
2677 return(io_ops[type].getfiletime (handle, create_time, last_access,
2683 * @handle: The file handle to set. The handle must have
2684 * %GENERIC_WRITE access.
2685 * @create_time: Points to a %WapiFileTime structure that contains the
2686 * number of ticks since the epoch that the file was created. May be
2688 * @last_access: Points to a %WapiFileTime structure that contains the
2689 * number of ticks since the epoch when the file was last accessed.
2691 * @last_write: Points to a %WapiFileTime structure that contains the
2692 * number of ticks since the epoch when the file was last written to.
2695 * Sets the number of ticks since the epoch that the file referenced
2696 * by @handle was created, last accessed or last modified. A tick is
2697 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2700 * Create time isn't recorded on POSIX file systems, and is ignored.
2702 * Return value: %TRUE on success, %FALSE otherwise.
2704 gboolean SetFileTime(gpointer fd_handle, const WapiFileTime *create_time,
2705 const WapiFileTime *last_access,
2706 const WapiFileTime *last_write)
2708 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2709 WapiHandleType type;
2711 if (handle == NULL) {
2712 SetLastError (ERROR_INVALID_HANDLE);
2716 type = _wapi_handle_type (handle);
2718 if(io_ops[type].setfiletime==NULL) {
2719 SetLastError (ERROR_INVALID_HANDLE);
2723 return(io_ops[type].setfiletime (handle, create_time, last_access,
2727 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2728 * January 1 1601 GMT
2731 #define TICKS_PER_MILLISECOND 10000L
2732 #define TICKS_PER_SECOND 10000000L
2733 #define TICKS_PER_MINUTE 600000000L
2734 #define TICKS_PER_HOUR 36000000000LL
2735 #define TICKS_PER_DAY 864000000000LL
2737 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2739 static const guint16 mon_yday[2][13]={
2740 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2741 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2745 * FileTimeToSystemTime:
2746 * @file_time: Points to a %WapiFileTime structure that contains the
2747 * number of ticks to convert.
2748 * @system_time: Points to a %WapiSystemTime structure to receive the
2751 * Converts a tick count into broken-out time values.
2753 * Return value: %TRUE on success, %FALSE otherwise.
2755 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2756 WapiSystemTime *system_time)
2758 gint64 file_ticks, totaldays, rem, y;
2761 if(system_time==NULL) {
2763 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2766 SetLastError (ERROR_INVALID_PARAMETER);
2770 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2771 file_time->dwLowDateTime;
2773 /* Really compares if file_ticks>=0x8000000000000000
2774 * (LLONG_MAX+1) but we're working with a signed value for the
2775 * year and day calculation to work later
2779 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2782 SetLastError (ERROR_INVALID_PARAMETER);
2786 totaldays=(file_ticks / TICKS_PER_DAY);
2787 rem = file_ticks % TICKS_PER_DAY;
2789 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2793 system_time->wHour=rem/TICKS_PER_HOUR;
2794 rem %= TICKS_PER_HOUR;
2796 g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2797 system_time->wHour, rem);
2800 system_time->wMinute = rem / TICKS_PER_MINUTE;
2801 rem %= TICKS_PER_MINUTE;
2803 g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2804 system_time->wMinute, rem);
2807 system_time->wSecond = rem / TICKS_PER_SECOND;
2808 rem %= TICKS_PER_SECOND;
2810 g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2811 system_time->wSecond, rem);
2814 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2816 g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2817 system_time->wMilliseconds);
2820 /* January 1, 1601 was a Monday, according to Emacs calendar */
2821 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2823 g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2824 system_time->wDayOfWeek);
2827 /* This algorithm to find year and month given days from epoch
2832 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2833 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2835 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2836 /* Guess a corrected year, assuming 365 days per year */
2837 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2839 g_message(G_GNUC_PRETTY_FUNCTION
2840 ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2842 g_message(G_GNUC_PRETTY_FUNCTION
2843 ": LEAPS(yg): %lld LEAPS(y): %lld",
2844 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2847 /* Adjust days and y to match the guessed year. */
2848 totaldays -= ((yg - y) * 365
2849 + LEAPS_THRU_END_OF (yg - 1)
2850 - LEAPS_THRU_END_OF (y - 1));
2852 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2857 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2861 system_time->wYear = y;
2863 g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2866 ip = mon_yday[isleap(y)];
2868 for(y=11; totaldays < ip[y]; --y) {
2873 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2876 system_time->wMonth = y + 1;
2878 g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2881 system_time->wDay = totaldays + 1;
2883 g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2890 file_compare (gconstpointer a, gconstpointer b)
2892 gchar *astr = *(gchar **) a;
2893 gchar *bstr = *(gchar **) b;
2895 return strcmp (astr, bstr);
2899 get_errno_from_g_file_error (gint error)
2903 case G_FILE_ERROR_ACCES:
2908 case G_FILE_ERROR_NAMETOOLONG:
2909 error = ENAMETOOLONG;
2913 case G_FILE_ERROR_NOENT:
2918 case G_FILE_ERROR_NOTDIR:
2923 case G_FILE_ERROR_NXIO:
2928 case G_FILE_ERROR_NODEV:
2933 case G_FILE_ERROR_ROFS:
2938 case G_FILE_ERROR_TXTBSY:
2943 case G_FILE_ERROR_FAULT:
2948 case G_FILE_ERROR_LOOP:
2953 case G_FILE_ERROR_NOSPC:
2958 case G_FILE_ERROR_NOMEM:
2963 case G_FILE_ERROR_MFILE:
2968 case G_FILE_ERROR_NFILE:
2973 case G_FILE_ERROR_BADF:
2978 case G_FILE_ERROR_INVAL:
2983 case G_FILE_ERROR_PIPE:
2988 case G_FILE_ERROR_AGAIN:
2993 case G_FILE_ERROR_INTR:
2998 case G_FILE_ERROR_IO:
3003 case G_FILE_ERROR_PERM:
3007 case G_FILE_ERROR_FAILED:
3008 error = ERROR_INVALID_PARAMETER;
3015 /* scandir using glib */
3017 mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
3019 GError *error = NULL;
3024 GPatternSpec *patspec;
3026 dir = g_dir_open (dirname, 0, &error);
3028 /* g_dir_open returns ENOENT on directories on which we don't
3029 * have read/x permission */
3030 gint errnum = get_errno_from_g_file_error (error->code);
3031 g_error_free (error);
3032 if (errnum == ENOENT && g_file_test (dirname, G_FILE_TEST_IS_DIR))
3039 patspec = g_pattern_spec_new (pattern);
3040 names = g_ptr_array_new ();
3041 while ((name = g_dir_read_name (dir)) != NULL) {
3042 if (g_pattern_match_string (patspec, name))
3043 g_ptr_array_add (names, g_strdup (name));
3046 g_pattern_spec_free (patspec);
3048 result = names->len;
3050 g_ptr_array_sort (names, file_compare);
3051 g_ptr_array_set_size (names, result + 1);
3053 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
3055 g_ptr_array_free (names, TRUE);
3061 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
3063 struct _WapiHandlePrivate_find *find_handle;
3064 gpointer handle, find_ret = INVALID_HANDLE_VALUE;
3066 gchar *utf8_pattern = NULL, *dir_part, *entry_part;
3069 gboolean unref = FALSE;
3071 if (pattern == NULL) {
3073 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
3076 SetLastError (ERROR_PATH_NOT_FOUND);
3077 return(INVALID_HANDLE_VALUE);
3080 utf8_pattern = mono_unicode_to_external (pattern);
3081 if (utf8_pattern == NULL) {
3083 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3086 SetLastError (ERROR_INVALID_NAME);
3087 return(INVALID_HANDLE_VALUE);
3091 g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
3095 /* Figure out which bit of the pattern is the directory */
3096 dir_part=g_path_get_dirname (utf8_pattern);
3097 entry_part=g_path_get_basename (utf8_pattern);
3100 /* Don't do this check for now, it breaks if directories
3101 * really do have metachars in their names (see bug 58116).
3102 * FIXME: Figure out a better solution to keep some checks...
3104 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3105 SetLastError (ERROR_INVALID_NAME);
3107 g_free (entry_part);
3108 g_free (utf8_pattern);
3109 return(INVALID_HANDLE_VALUE);
3113 handle=_wapi_handle_new (WAPI_HANDLE_FIND);
3114 if(handle==_WAPI_HANDLE_INVALID) {
3115 g_warning (G_GNUC_PRETTY_FUNCTION ": error creating find handle");
3117 g_free (entry_part);
3118 g_free (utf8_pattern);
3119 SetLastError (ERROR_GEN_FAILURE);
3121 return(INVALID_HANDLE_VALUE);
3124 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3126 thr_ret = _wapi_handle_lock_handle (handle);
3127 g_assert (thr_ret == 0);
3129 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3130 (gpointer *)&find_handle);
3132 g_warning (G_GNUC_PRETTY_FUNCTION
3133 ": error looking up find handle %p", handle);
3136 g_free (entry_part);
3138 g_free (utf8_pattern);
3139 utf8_pattern = NULL;
3140 SetLastError (ERROR_INVALID_HANDLE);
3144 /* The pattern can specify a directory or a set of files.
3146 * The pattern can have wildcard characters ? and *, but only
3147 * in the section after the last directory delimiter. (Return
3148 * ERROR_INVALID_NAME if there are wildcards in earlier path
3149 * sections.) "*" has the usual 0-or-more chars meaning. "?"
3150 * means "match one character", "??" seems to mean "match one
3151 * or two characters", "???" seems to mean "match one, two or
3152 * three characters", etc. Windows will also try and match
3153 * the mangled "short name" of files, so 8 character patterns
3154 * with wildcards will show some surprising results.
3156 * All the written documentation I can find says that '?'
3157 * should only match one character, and doesn't mention '??',
3158 * '???' etc. I'm going to assume that the strict behaviour
3159 * (ie '???' means three and only three characters) is the
3160 * correct one, because that lets me use fnmatch(3) rather
3161 * than mess around with regexes.
3164 find_handle->namelist = NULL;
3165 result = mono_io_scandir (dir_part, entry_part, &find_handle->namelist);
3169 gint errnum = errno;
3171 _wapi_set_last_error_from_errno ();
3173 g_message (G_GNUC_PRETTY_FUNCTION ": scandir error: %s", g_strerror (errnum));
3175 g_free (utf8_pattern);
3176 g_free (entry_part);
3182 g_free (utf8_pattern);
3183 g_free (entry_part);
3186 g_message (G_GNUC_PRETTY_FUNCTION ": Got %d matches", result);
3189 find_handle->dir_part = dir_part;
3190 find_handle->num = result;
3191 find_handle->count = 0;
3196 thr_ret = _wapi_handle_unlock_handle (handle);
3197 g_assert (thr_ret == 0);
3198 pthread_cleanup_pop (0);
3200 /* FindNextFile has to be called after unlocking the handle,
3201 * because it wants to lock the handle itself
3203 if (find_ret != INVALID_HANDLE_VALUE &&
3204 !FindNextFile (handle, find_data)) {
3206 SetLastError (ERROR_NO_MORE_FILES);
3207 find_ret = INVALID_HANDLE_VALUE;
3210 /* Must not call _wapi_handle_unref() with the handle already
3214 _wapi_handle_unref (handle);
3220 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
3222 struct _WapiHandlePrivate_find *find_handle;
3226 gchar *utf8_filename, *utf8_basename;
3227 gunichar2 *utf16_basename;
3231 gboolean ret = FALSE;
3233 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3234 (gpointer *)&find_handle);
3236 g_warning (G_GNUC_PRETTY_FUNCTION
3237 ": error looking up find handle %p", handle);
3238 SetLastError (ERROR_INVALID_HANDLE);
3242 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3244 thr_ret = _wapi_handle_lock_handle (handle);
3245 g_assert (thr_ret == 0);
3248 if (find_handle->count >= find_handle->num) {
3249 SetLastError (ERROR_NO_MORE_FILES);
3253 /* stat next match */
3255 filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3256 if (lstat (filename, &buf) != 0) {
3258 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
3265 /* Check for dangling symlinks, and ignore them (principle of
3266 * least surprise, avoiding confusion where we report the file
3267 * exists, but when someone tries to open it we would report
3270 if(S_ISLNK (buf.st_mode)) {
3271 if(stat (filename, &buf) != 0) {
3277 utf8_filename=mono_utf8_from_external (filename);
3278 if(utf8_filename==NULL) {
3279 /* We couldn't turn this filename into utf8 (eg the
3280 * encoding of the name wasn't convertible), so just
3289 g_message (G_GNUC_PRETTY_FUNCTION ": Found [%s]", utf8_filename);
3292 /* fill data block */
3294 if (buf.st_mtime < buf.st_ctime)
3295 create_time = buf.st_mtime;
3297 create_time = buf.st_ctime;
3299 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3301 _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
3302 _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3303 _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3305 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3306 find_data->nFileSizeHigh = 0;
3307 find_data->nFileSizeLow = 0;
3310 find_data->nFileSizeHigh = buf.st_size >> 32;
3311 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3314 find_data->dwReserved0 = 0;
3315 find_data->dwReserved1 = 0;
3317 utf8_basename = g_path_get_basename (utf8_filename);
3318 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3320 if(utf16_basename==NULL) {
3321 g_free (utf8_basename);
3322 g_free (utf8_filename);
3327 /* utf16 is 2 * utf8 */
3330 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3332 /* Truncating a utf16 string like this might leave the last
3335 memcpy (find_data->cFileName, utf16_basename,
3336 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3338 find_data->cAlternateFileName [0] = 0; /* not used */
3340 g_free (utf8_basename);
3341 g_free (utf8_filename);
3342 g_free (utf16_basename);
3345 thr_ret = _wapi_handle_unlock_handle (handle);
3346 g_assert (thr_ret == 0);
3347 pthread_cleanup_pop (0);
3354 * @wapi_handle: the find handle to close.
3356 * Closes find handle @wapi_handle
3358 * Return value: %TRUE on success, %FALSE otherwise.
3360 gboolean FindClose (gpointer handle)
3362 struct _WapiHandlePrivate_find *find_handle;
3366 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3367 (gpointer *)&find_handle);
3369 g_warning (G_GNUC_PRETTY_FUNCTION
3370 ": error looking up find handle %p", handle);
3371 SetLastError (ERROR_INVALID_HANDLE);
3375 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3377 thr_ret = _wapi_handle_lock_handle (handle);
3378 g_assert (thr_ret == 0);
3380 g_strfreev (find_handle->namelist);
3381 g_free (find_handle->dir_part);
3383 thr_ret = _wapi_handle_unlock_handle (handle);
3384 g_assert (thr_ret == 0);
3385 pthread_cleanup_pop (0);
3387 _wapi_handle_unref (handle);
3394 * @name: a pointer to a NULL-terminated unicode string, that names
3395 * the directory to be created.
3396 * @security: ignored for now
3398 * Creates directory @name
3400 * Return value: %TRUE on success, %FALSE otherwise.
3402 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
3411 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3414 SetLastError (ERROR_INVALID_NAME);
3418 utf8_name = mono_unicode_to_external (name);
3419 if (utf8_name == NULL) {
3421 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3424 SetLastError (ERROR_INVALID_NAME);
3428 result = mkdir (utf8_name, 0777);
3435 if (errno == EEXIST) {
3436 result = stat (utf8_name, &buf);
3438 _wapi_set_last_error_from_errno ();
3444 attrs = _wapi_stat_to_file_attributes (&buf);
3445 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
3449 _wapi_set_last_error_from_errno ();
3453 _wapi_set_last_error_from_errno ();
3460 * @name: a pointer to a NULL-terminated unicode string, that names
3461 * the directory to be removed.
3463 * Removes directory @name
3465 * Return value: %TRUE on success, %FALSE otherwise.
3467 gboolean RemoveDirectory (const gunichar2 *name)
3474 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3477 SetLastError (ERROR_INVALID_NAME);
3481 utf8_name = mono_unicode_to_external (name);
3482 if (utf8_name == NULL) {
3484 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3487 SetLastError (ERROR_INVALID_NAME);
3491 result = rmdir (utf8_name);
3497 _wapi_set_last_error_from_errno ();
3502 * GetFileAttributes:
3503 * @name: a pointer to a NULL-terminated unicode filename.
3505 * Gets the attributes for @name;
3507 * Return value: %INVALID_FILE_ATTRIBUTES on failure
3509 guint32 GetFileAttributes (const gunichar2 *name)
3517 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3520 SetLastError (ERROR_INVALID_NAME);
3524 utf8_name = mono_unicode_to_external (name);
3525 if (utf8_name == NULL) {
3527 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3530 SetLastError (ERROR_INVALID_PARAMETER);
3531 return (INVALID_FILE_ATTRIBUTES);
3534 result = stat (utf8_name, &buf);
3537 _wapi_set_last_error_from_errno ();
3539 return (INVALID_FILE_ATTRIBUTES);
3543 return _wapi_stat_to_file_attributes (&buf);
3547 * GetFileAttributesEx:
3548 * @name: a pointer to a NULL-terminated unicode filename.
3549 * @level: must be GetFileExInfoStandard
3550 * @info: pointer to a WapiFileAttributesData structure
3552 * Gets attributes, size and filetimes for @name;
3554 * Return value: %TRUE on success, %FALSE on failure
3556 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3559 WapiFileAttributesData *data;
3565 if (level != GetFileExInfoStandard) {
3567 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
3570 SetLastError (ERROR_INVALID_PARAMETER);
3576 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3579 SetLastError (ERROR_INVALID_NAME);
3583 utf8_name = mono_unicode_to_external (name);
3584 if (utf8_name == NULL) {
3586 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3589 SetLastError (ERROR_INVALID_PARAMETER);
3593 result = stat (utf8_name, &buf);
3597 SetLastError (ERROR_FILE_NOT_FOUND);
3601 /* fill data block */
3603 data = (WapiFileAttributesData *)info;
3605 if (buf.st_mtime < buf.st_ctime)
3606 create_time = buf.st_mtime;
3608 create_time = buf.st_ctime;
3610 data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3612 _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3613 _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3614 _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3616 if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3617 data->nFileSizeHigh = 0;
3618 data->nFileSizeLow = 0;
3621 data->nFileSizeHigh = buf.st_size >> 32;
3622 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3630 * @name: name of file
3631 * @attrs: attributes to set
3633 * Changes the attributes on a named file.
3635 * Return value: %TRUE on success, %FALSE on failure.
3637 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3639 /* FIXME: think of something clever to do on unix */
3645 * Currently we only handle one *internal* case, with a value that is
3646 * not standard: 0x80000000, which means `set executable bit'
3651 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3654 SetLastError (ERROR_INVALID_NAME);
3658 utf8_name = mono_unicode_to_external (name);
3659 if (utf8_name == NULL) {
3661 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3664 SetLastError (ERROR_INVALID_NAME);
3668 result = stat (utf8_name, &buf);
3671 SetLastError (ERROR_FILE_NOT_FOUND);
3675 /* Contrary to the documentation, ms allows NORMAL to be
3676 * specified along with other attributes, so dont bother to
3677 * catch that case here.
3679 if (attrs & FILE_ATTRIBUTE_READONLY) {
3680 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3682 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
3685 /* Ignore the other attributes for now */
3687 if (attrs & 0x80000000){
3688 mode_t exec_mask = 0;
3690 if ((buf.st_mode & S_IRUSR) != 0)
3691 exec_mask |= S_IXUSR;
3693 if ((buf.st_mode & S_IRGRP) != 0)
3694 exec_mask |= S_IXGRP;
3696 if ((buf.st_mode & S_IROTH) != 0)
3697 exec_mask |= S_IXOTH;
3699 result = chmod (utf8_name, buf.st_mode | exec_mask);
3701 /* Don't bother to reset executable (might need to change this
3711 * GetCurrentDirectory
3712 * @length: size of the buffer
3713 * @buffer: pointer to buffer that recieves path
3715 * Retrieves the current directory for the current process.
3717 * Return value: number of characters in buffer on success, zero on failure
3719 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3722 gunichar2 *utf16_path;
3726 path = g_get_current_dir ();
3730 utf16_path=mono_unicode_from_external (path, &bytes);
3732 /* if buffer too small, return number of characters required.
3733 * this is plain dumb.
3736 count = (bytes/2)+1;
3737 if (count > length) {
3739 g_free (utf16_path);
3744 /* Add the terminator */
3745 memset (buffer, '\0', bytes+2);
3746 memcpy (buffer, utf16_path, bytes);
3748 g_free (utf16_path);
3755 * SetCurrentDirectory
3756 * @path: path to new directory
3758 * Changes the directory path for the current process.
3760 * Return value: %TRUE on success, %FALSE on failure.
3762 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3767 utf8_path = mono_unicode_to_external (path);
3768 if (chdir (utf8_path) != 0) {
3769 _wapi_set_last_error_from_errno ();
3779 /* When we're confident there are no more bugs in the fd->handle
3780 * mapping, this can be replaced as a no-op: GPOINTER_TO_INT(fd_handle) == fd
3782 int _wapi_file_handle_to_fd (gpointer fd_handle)
3784 struct _WapiHandlePrivate_file *file_private_handle;
3786 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3789 g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
3792 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
3793 (gpointer *)&file_private_handle);
3795 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
3796 (gpointer *)&file_private_handle);
3798 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
3799 (gpointer *)&file_private_handle);
3802 g_message (G_GNUC_PRETTY_FUNCTION
3805 SetLastError (ERROR_INVALID_HANDLE);
3811 if (file_private_handle->fd_mapped.assigned == FALSE) {
3812 SetLastError (ERROR_INVALID_HANDLE);
3817 g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
3818 file_private_handle->fd_mapped.fd);
3821 g_assert (file_private_handle->fd_mapped.fd == GPOINTER_TO_INT (fd_handle));
3823 return(file_private_handle->fd_mapped.fd);
3826 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3827 WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3829 struct _WapiHandle_file *pipe_read_handle;
3830 struct _WapiHandle_file *pipe_write_handle;
3831 struct _WapiHandlePrivate_file *pipe_read_private_handle;
3832 struct _WapiHandlePrivate_file *pipe_write_private_handle;
3833 gpointer read_handle;
3834 gpointer write_handle;
3839 gboolean unref_read = FALSE, unref_write = FALSE;
3840 gboolean cp_ret = FALSE;
3842 mono_once (&io_ops_once, io_ops_init);
3845 g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
3851 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
3855 _wapi_set_last_error_from_errno ();
3859 if (filedes[0] >= _wapi_fd_offset_table_size ||
3860 filedes[1] >= _wapi_fd_offset_table_size) {
3862 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
3865 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3873 /* filedes[0] is open for reading, filedes[1] for writing */
3875 read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3876 if(read_handle==_WAPI_HANDLE_INVALID) {
3877 g_warning (G_GNUC_PRETTY_FUNCTION
3878 ": error creating pipe read handle");
3881 SetLastError (ERROR_GEN_FAILURE);
3886 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3888 thr_ret = _wapi_handle_lock_handle (read_handle);
3889 g_assert (thr_ret == 0);
3891 ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
3892 (gpointer *)&pipe_read_handle,
3893 (gpointer *)&pipe_read_private_handle);
3895 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3898 SetLastError (ERROR_INVALID_HANDLE);
3902 write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3903 if(write_handle==_WAPI_HANDLE_INVALID) {
3904 g_warning (G_GNUC_PRETTY_FUNCTION
3905 ": error creating pipe write handle");
3910 SetLastError (ERROR_GEN_FAILURE);
3915 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3917 thr_ret = _wapi_handle_lock_handle (write_handle);
3918 g_assert (thr_ret == 0);
3920 ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
3921 (gpointer *)&pipe_write_handle,
3922 (gpointer *)&pipe_write_private_handle);
3924 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3930 SetLastError (ERROR_INVALID_HANDLE);
3935 pipe_read_private_handle->fd_mapped.fd=filedes[0];
3936 pipe_read_private_handle->fd_mapped.assigned=TRUE;
3937 pipe_read_handle->fileaccess=GENERIC_READ;
3939 _wapi_handle_fd_offset_store (filedes[0], read_handle);
3940 *readpipe=GINT_TO_POINTER (filedes[0]);
3942 pipe_write_private_handle->fd_mapped.fd=filedes[1];
3943 pipe_write_private_handle->fd_mapped.assigned=TRUE;
3944 pipe_write_handle->fileaccess=GENERIC_WRITE;
3946 _wapi_handle_fd_offset_store (filedes[1], write_handle);
3947 *writepipe=GINT_TO_POINTER (filedes[1]);
3950 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]);
3954 thr_ret =_wapi_handle_unlock_handle (write_handle);
3955 g_assert (thr_ret == 0);
3956 pthread_cleanup_pop (0);
3959 _wapi_handle_unref (write_handle);
3963 thr_ret =_wapi_handle_unlock_handle (read_handle);
3964 g_assert (thr_ret == 0);
3965 pthread_cleanup_pop (0);
3967 /* Must not call _wapi_handle_unref() with the handle already
3971 _wapi_handle_unref (read_handle);
3977 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3979 gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3980 gunichar2 *tmpdir16=NULL;
3985 if(tmpdir[strlen (tmpdir)]!='/') {
3987 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3990 tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3991 if(tmpdir16==NULL) {
3999 g_message (G_GNUC_PRETTY_FUNCTION
4000 ": Size %d smaller than needed (%ld)", len,
4006 /* Add the terminator */
4007 memset (buf, '\0', bytes+2);
4008 memcpy (buf, tmpdir16, bytes);
4014 if(tmpdir16!=NULL) {
4023 _wapi_io_add_callback (gpointer fd_handle,
4024 WapiOverlappedCB callback,
4025 guint64 flags G_GNUC_UNUSED)
4027 struct _WapiHandle_file *file_handle;
4028 struct _WapiHandlePrivate_file *file_private_handle;
4031 gboolean ret = FALSE;
4032 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4034 if (handle == NULL) {
4035 SetLastError (ERROR_INVALID_HANDLE);
4039 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4040 (gpointer *) &file_handle,
4041 (gpointer *) &file_private_handle);
4044 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
4045 (gpointer *) &file_handle,
4046 (gpointer *) &file_private_handle);
4050 if (ok == FALSE || file_private_handle->async == FALSE) {
4051 SetLastError (ERROR_INVALID_HANDLE);
4055 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
4057 thr_ret = _wapi_handle_lock_handle (handle);
4058 g_assert (thr_ret == 0);
4060 if (file_private_handle->callback != NULL) {
4061 SetLastError (ERROR_INVALID_PARAMETER);
4066 file_private_handle->callback = callback;
4069 thr_ret = _wapi_handle_unlock_handle (handle);
4070 g_assert (thr_ret == 0);
4071 pthread_cleanup_pop (0);
4077 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
4080 gunichar2 *ptr, *dir;
4081 glong length, total = 0;
4085 memset (buf, 0, sizeof (gunichar2) * (len + 1));
4090 /* Sigh, mntent and friends don't work well.
4091 * It stops on the first line that doesn't begin with a '/'.
4092 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4093 fp = fopen ("/etc/mtab", "rt");
4095 fp = fopen ("/etc/mnttab", "rt");
4101 while (fgets (buffer, 512, fp) != NULL) {
4105 splitted = g_strsplit (buffer, " ", 0);
4106 if (!*splitted || !*(splitted + 1))
4109 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
4110 g_strfreev (splitted);
4111 if (total + length + 1 > len) {
4112 return len * 2; /* guess */
4115 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4117 total += length + 1;
4122 /* Commented out, does not work with my mtab!!! - Gonz */
4123 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4127 gunichar2 *ptr, *dir;
4128 glong len, total = 0;
4131 fp = setmntent ("/etc/mtab", "rt");
4133 fp = setmntent ("/etc/mnttab", "rt");
4139 while ((mnt = getmntent (fp)) != NULL) {
4140 g_print ("GOT %s\n", mnt->mnt_dir);
4141 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4142 if (total + len + 1 > len) {
4143 return len * 2; /* guess */
4146 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4157 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
4159 struct flock lock_data;
4162 lock_data.l_type = F_WRLCK;
4163 lock_data.l_whence = SEEK_SET;
4164 lock_data.l_start = offset;
4165 lock_data.l_len = length;
4168 ret = fcntl (fd, F_SETLK, &lock_data);
4170 while(ret == -1 && errno == EINTR);
4173 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4178 * if locks are not available (NFS for example), ignore the error
4180 if (errno == ENOLCK)
4183 SetLastError (ERROR_LOCK_VIOLATION);
4190 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
4192 struct flock lock_data;
4195 lock_data.l_type = F_UNLCK;
4196 lock_data.l_whence = SEEK_SET;
4197 lock_data.l_start = offset;
4198 lock_data.l_len = length;
4201 ret = fcntl (fd, F_SETLK, &lock_data);
4203 while(ret == -1 && errno == EINTR);
4206 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4210 SetLastError (ERROR_LOCK_VIOLATION);
4217 gboolean LockFile (gpointer fd_handle, guint32 offset_low, guint32 offset_high,
4218 guint32 length_low, guint32 length_high)
4220 struct _WapiHandle_file *file_handle;
4221 struct _WapiHandlePrivate_file *file_private_handle;
4223 off_t offset, length;
4224 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4226 if (handle == NULL) {
4227 SetLastError (ERROR_INVALID_HANDLE);
4231 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4232 (gpointer *)&file_handle,
4233 (gpointer *)&file_private_handle);
4235 g_warning (G_GNUC_PRETTY_FUNCTION
4236 ": error looking up file handle %p", handle);
4237 SetLastError (ERROR_INVALID_HANDLE);
4241 if (file_private_handle->fd_mapped.assigned == FALSE) {
4242 SetLastError (ERROR_INVALID_HANDLE);
4246 if (!(file_handle->fileaccess & GENERIC_READ) &&
4247 !(file_handle->fileaccess & GENERIC_WRITE) &&
4248 !(file_handle->fileaccess & GENERIC_ALL)) {
4250 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);
4252 SetLastError (ERROR_ACCESS_DENIED);
4256 #ifdef HAVE_LARGE_FILE_SUPPORT
4257 offset = ((gint64)offset_high << 32) | offset_low;
4258 length = ((gint64)length_high << 32) | length_low;
4261 g_message (G_GNUC_PRETTY_FUNCTION
4262 ": Locking handle %p fd %d, offset %lld, length %lld",
4263 handle, file_private_handle->fd_mapped.fd, offset, length);
4266 offset = offset_low;
4267 length = length_low;
4270 g_message (G_GNUC_PRETTY_FUNCTION
4271 ": Locking handle %p fd %d, offset %ld, length %ld",
4272 handle, file_private_handle->fd_mapped.fd, offset, length);
4276 return(_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
4280 gboolean UnlockFile (gpointer fd_handle, guint32 offset_low,
4281 guint32 offset_high, guint32 length_low,
4282 guint32 length_high)
4284 struct _WapiHandle_file *file_handle;
4285 struct _WapiHandlePrivate_file *file_private_handle;
4287 off_t offset, length;
4288 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4290 if (handle == NULL) {
4291 SetLastError (ERROR_INVALID_HANDLE);
4295 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4296 (gpointer *)&file_handle,
4297 (gpointer *)&file_private_handle);
4299 g_warning (G_GNUC_PRETTY_FUNCTION
4300 ": error looking up file handle %p", handle);
4301 SetLastError (ERROR_INVALID_HANDLE);
4305 if (file_private_handle->fd_mapped.assigned == FALSE) {
4306 SetLastError (ERROR_INVALID_HANDLE);
4310 if (!(file_handle->fileaccess & GENERIC_READ) &&
4311 !(file_handle->fileaccess & GENERIC_WRITE) &&
4312 !(file_handle->fileaccess & GENERIC_ALL)) {
4314 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);
4316 SetLastError (ERROR_ACCESS_DENIED);
4320 #ifdef HAVE_LARGE_FILE_SUPPORT
4321 offset = ((gint64)offset_high << 32) | offset_low;
4322 length = ((gint64)length_high << 32) | length_low;
4325 g_message (G_GNUC_PRETTY_FUNCTION
4326 ": Unlocking handle %p fd %d, offset %lld, length %lld",
4327 handle, file_private_handle->fd_mapped.fd, offset, length);
4330 offset = offset_low;
4331 length = length_low;
4334 g_message (G_GNUC_PRETTY_FUNCTION
4335 ": Unlocking handle %p fd %d, offset %ld, length %ld",
4336 handle, file_private_handle->fd_mapped.fd, offset, length);
4340 return(_wapi_unlock_file_region (file_private_handle->fd_mapped.fd,