2 * io.c: File, console and find handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
17 #include <sys/types.h>
23 #ifndef PLATFORM_WIN32
27 #elif defined(HAVE_SYS_AIO_H)
35 #include <mono/io-layer/wapi.h>
36 #include <mono/io-layer/wapi-private.h>
37 #include <mono/io-layer/handles-private.h>
38 #include <mono/io-layer/io-private.h>
39 #include <mono/io-layer/timefuncs-private.h>
40 #include <mono/io-layer/thread-private.h>
41 #include <mono/utils/strenc.h>
45 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length);
46 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length);
48 static void file_close_shared (gpointer handle);
49 static void file_close_private (gpointer handle);
50 static WapiFileType file_getfiletype(void);
51 static gboolean file_read(gpointer handle, gpointer buffer,
52 guint32 numbytes, guint32 *bytesread,
53 WapiOverlapped *overlapped);
54 static gboolean file_write(gpointer handle, gconstpointer buffer,
55 guint32 numbytes, guint32 *byteswritten,
56 WapiOverlapped *overlapped);
57 static gboolean file_flush(gpointer handle);
58 static guint32 file_seek(gpointer handle, gint32 movedistance,
59 gint32 *highmovedistance, WapiSeekMethod method);
60 static gboolean file_setendoffile(gpointer handle);
61 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
62 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
63 WapiFileTime *last_access,
64 WapiFileTime *last_write);
65 static gboolean file_setfiletime(gpointer handle,
66 const WapiFileTime *create_time,
67 const WapiFileTime *last_access,
68 const WapiFileTime *last_write);
70 /* File handle is only signalled for overlapped IO */
71 struct _WapiHandleOps _wapi_file_ops = {
72 file_close_shared, /* close_shared */
73 file_close_private, /* close_private */
79 static void console_close_shared (gpointer handle);
80 static void console_close_private (gpointer handle);
81 static WapiFileType console_getfiletype(void);
82 static gboolean console_read(gpointer handle, gpointer buffer,
83 guint32 numbytes, guint32 *bytesread,
84 WapiOverlapped *overlapped);
85 static gboolean console_write(gpointer handle, gconstpointer buffer,
86 guint32 numbytes, guint32 *byteswritten,
87 WapiOverlapped *overlapped);
89 /* Console is mostly the same as file, except it can block waiting for
92 struct _WapiHandleOps _wapi_console_ops = {
93 console_close_shared, /* close_shared */
94 console_close_private, /* close_private */
100 /* Find handle has no ops.
102 struct _WapiHandleOps _wapi_find_ops = {
103 NULL, /* close_shared */
104 NULL, /* close_private */
110 static void pipe_close_shared (gpointer handle);
111 static void pipe_close_private (gpointer handle);
112 static WapiFileType pipe_getfiletype (void);
113 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes,
114 guint32 *bytesread, WapiOverlapped *overlapped);
115 static gboolean pipe_write (gpointer handle, gconstpointer buffer,
116 guint32 numbytes, guint32 *byteswritten,
117 WapiOverlapped *overlapped);
121 struct _WapiHandleOps _wapi_pipe_ops = {
122 pipe_close_shared, /* close_shared */
123 pipe_close_private, /* close_private */
130 /* File, console and pipe handles */
131 WapiFileType (*getfiletype)(void);
133 /* File, console and pipe handles */
134 gboolean (*readfile)(gpointer handle, gpointer buffer,
135 guint32 numbytes, guint32 *bytesread,
136 WapiOverlapped *overlapped);
137 gboolean (*writefile)(gpointer handle, gconstpointer buffer,
138 guint32 numbytes, guint32 *byteswritten,
139 WapiOverlapped *overlapped);
140 gboolean (*flushfile)(gpointer handle);
143 guint32 (*seek)(gpointer handle, gint32 movedistance,
144 gint32 *highmovedistance, WapiSeekMethod method);
145 gboolean (*setendoffile)(gpointer handle);
146 guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
147 gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time,
148 WapiFileTime *last_access,
149 WapiFileTime *last_write);
150 gboolean (*setfiletime)(gpointer handle,
151 const WapiFileTime *create_time,
152 const WapiFileTime *last_access,
153 const WapiFileTime *last_write);
154 } io_ops[WAPI_HANDLE_COUNT]={
155 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
158 file_read, file_write,
159 file_flush, file_seek,
165 {console_getfiletype,
168 NULL, NULL, NULL, NULL, NULL, NULL},
170 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
172 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
174 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
176 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
177 /* socket (will need at least read and write) */
178 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
180 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
182 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
187 NULL, NULL, NULL, NULL, NULL, NULL},
191 static mono_once_t io_ops_once=MONO_ONCE_INIT;
193 static void io_ops_init (void)
195 /* _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
196 /* WAPI_HANDLE_CAP_WAIT); */
197 /* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
198 /* WAPI_HANDLE_CAP_WAIT); */
201 /* Some utility functions.
204 static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
208 /* FIXME: this could definitely be better */
210 if (S_ISDIR (buf->st_mode))
211 attrs |= FILE_ATTRIBUTE_DIRECTORY;
213 attrs |= FILE_ATTRIBUTE_ARCHIVE;
215 if (!(buf->st_mode & S_IWUSR))
216 attrs |= FILE_ATTRIBUTE_READONLY;
222 _wapi_set_last_error_from_errno (void)
224 SetLastError (_wapi_get_win32_file_error (errno));
229 static void file_close_shared (gpointer handle)
231 struct _WapiHandle_file *file_handle;
234 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
235 (gpointer *)&file_handle, NULL);
237 g_warning (G_GNUC_PRETTY_FUNCTION
238 ": error looking up file handle %p", handle);
243 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p", handle);
246 if(file_handle->filename!=0) {
247 _wapi_handle_scratch_delete (file_handle->filename);
248 file_handle->filename=0;
250 if(file_handle->security_attributes!=0) {
251 _wapi_handle_scratch_delete (file_handle->security_attributes);
252 file_handle->security_attributes=0;
256 static void file_close_private (gpointer handle)
258 struct _WapiHandlePrivate_file *file_private_handle;
261 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
262 (gpointer *)&file_private_handle);
264 g_warning (G_GNUC_PRETTY_FUNCTION
265 ": error looking up file handle %p", handle);
270 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
271 handle, file_private_handle->fd);
274 close(file_private_handle->fd);
277 static WapiFileType file_getfiletype(void)
279 return(FILE_TYPE_DISK);
285 WapiOverlapped *overlapped;
286 WapiOverlappedCB callback;
289 #define SIGPTR(a) a.SIGVAL_PTR
292 async_notifier (union sigval sig)
294 notifier_data_t *ndata = SIGPTR (sig);
298 error = aio_return (ndata->aio);
300 error = _wapi_get_win32_file_error (error);
307 ndata->callback (error, numbytes, ndata->overlapped);
314 static gboolean file_read(gpointer handle, gpointer buffer,
315 guint32 numbytes, guint32 *bytesread,
316 WapiOverlapped *overlapped)
318 struct _WapiHandle_file *file_handle;
319 struct _WapiHandlePrivate_file *file_private_handle;
323 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
324 (gpointer *)&file_handle,
325 (gpointer *)&file_private_handle);
327 g_warning (G_GNUC_PRETTY_FUNCTION
328 ": error looking up file handle %p", handle);
332 if(bytesread!=NULL) {
336 if(!(file_handle->fileaccess&GENERIC_READ) &&
337 !(file_handle->fileaccess&GENERIC_ALL)) {
339 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
345 if (file_private_handle->async == FALSE) {
347 ret=read(file_private_handle->fd, buffer, numbytes);
349 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
355 g_message(G_GNUC_PRETTY_FUNCTION
356 ": read of handle %p fd %d error: %s", handle,
357 file_private_handle->fd, strerror(err));
359 SetLastError (_wapi_get_win32_file_error (err));
363 if(bytesread!=NULL) {
371 SetLastError (ERROR_NOT_SUPPORTED);
374 if (overlapped == NULL || file_private_handle->callback == NULL) {
375 SetLastError (ERROR_INVALID_PARAMETER);
380 int fd = file_private_handle->fd;
383 notifier_data_t *ndata;
385 ndata = g_new0 (notifier_data_t, 1);
386 aio = g_new0 (struct aiocb, 1);
387 ndata->overlapped = overlapped;
389 ndata->callback = file_private_handle->callback;
391 aio->aio_fildes = fd;
392 aio->aio_lio_opcode = LIO_READ;
393 aio->aio_nbytes = numbytes;
394 aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
395 aio->aio_buf = buffer;
396 aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
397 aio->aio_sigevent.sigev_notify_function = async_notifier;
398 SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
400 result = aio_read (aio);
402 _wapi_set_last_error_from_errno ();
406 result = aio_error (aio);
408 g_print ("aio_error (read) returned %d for %d\n", result, fd);
411 numbytes = aio_return (aio);
413 g_print ("numbytes %d for %d\n", numbytes, fd);
417 _wapi_set_last_error_from_errno ();
422 *bytesread = numbytes;
429 static gboolean file_write(gpointer handle, gconstpointer buffer,
430 guint32 numbytes, guint32 *byteswritten,
431 WapiOverlapped *overlapped G_GNUC_UNUSED)
433 struct _WapiHandle_file *file_handle;
434 struct _WapiHandlePrivate_file *file_private_handle;
438 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
439 (gpointer *)&file_handle,
440 (gpointer *)&file_private_handle);
442 g_warning (G_GNUC_PRETTY_FUNCTION
443 ": error looking up file handle %p", handle);
447 if(byteswritten!=NULL) {
451 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
452 !(file_handle->fileaccess&GENERIC_ALL)) {
454 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
460 if (file_private_handle->async == FALSE) {
463 /* Need to lock the region we're about to write to,
464 * because we only do advisory locking on POSIX
467 current_pos = lseek (file_private_handle->fd, (off_t)0,
469 if (current_pos == -1) {
471 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d lseek failed: %s", handle, file_private_handle->fd, strerror (errno));
473 _wapi_set_last_error_from_errno ();
477 if (_wapi_lock_file_region (file_private_handle->fd,
478 current_pos, numbytes) == FALSE) {
479 /* The error has already been set */
484 ret=write(file_private_handle->fd, buffer, numbytes);
486 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
488 _wapi_unlock_file_region (file_private_handle->fd, current_pos,
493 g_message(G_GNUC_PRETTY_FUNCTION
494 ": write of handle %p fd %d error: %s", handle,
495 file_private_handle->fd, strerror(errno));
500 if(byteswritten!=NULL) {
507 SetLastError (ERROR_NOT_SUPPORTED);
510 if (overlapped == NULL || file_private_handle->callback == NULL) {
511 SetLastError (ERROR_INVALID_PARAMETER);
516 int fd = file_private_handle->fd;
519 notifier_data_t *ndata;
521 ndata = g_new0 (notifier_data_t, 1);
522 aio = g_new0 (struct aiocb, 1);
523 ndata->overlapped = overlapped;
525 ndata->callback = file_private_handle->callback;
527 aio->aio_fildes = fd;
528 aio->aio_lio_opcode = LIO_WRITE;
529 aio->aio_nbytes = numbytes;
530 aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
531 aio->aio_buf = (gpointer) buffer;
532 aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
533 aio->aio_sigevent.sigev_notify_function = async_notifier;
534 SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
536 result = aio_write (aio);
538 _wapi_set_last_error_from_errno ();
542 result = aio_error (aio);
544 g_print ("aio_error (write) returned %d for %d\n", result, fd);
547 numbytes = aio_return (aio);
549 g_print ("numbytes %d for %d\n", numbytes, fd);
553 _wapi_set_last_error_from_errno ();
558 *byteswritten = numbytes;
565 static gboolean file_flush(gpointer handle)
567 struct _WapiHandle_file *file_handle;
568 struct _WapiHandlePrivate_file *file_private_handle;
572 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
573 (gpointer *)&file_handle,
574 (gpointer *)&file_private_handle);
576 g_warning (G_GNUC_PRETTY_FUNCTION
577 ": error looking up file handle %p", handle);
581 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
582 !(file_handle->fileaccess&GENERIC_ALL)) {
584 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
590 ret=fsync(file_private_handle->fd);
593 g_message(G_GNUC_PRETTY_FUNCTION
594 ": write of handle %p fd %d error: %s", handle,
595 file_private_handle->fd, strerror(errno));
604 static guint32 file_seek(gpointer handle, gint32 movedistance,
605 gint32 *highmovedistance, WapiSeekMethod method)
607 struct _WapiHandle_file *file_handle;
608 struct _WapiHandlePrivate_file *file_private_handle;
610 off_t offset, newpos;
614 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
615 (gpointer *)&file_handle,
616 (gpointer *)&file_private_handle);
618 g_warning (G_GNUC_PRETTY_FUNCTION
619 ": error looking up file handle %p", handle);
620 return(INVALID_SET_FILE_POINTER);
623 if(!(file_handle->fileaccess&GENERIC_READ) &&
624 !(file_handle->fileaccess&GENERIC_WRITE) &&
625 !(file_handle->fileaccess&GENERIC_ALL)) {
627 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, file_handle->fileaccess);
630 return(INVALID_SET_FILE_POINTER);
645 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
649 return(INVALID_SET_FILE_POINTER);
652 #ifdef HAVE_LARGE_FILE_SUPPORT
653 if(highmovedistance==NULL) {
656 g_message(G_GNUC_PRETTY_FUNCTION
657 ": setting offset to %lld (low %d)", offset,
661 offset=((gint64) *highmovedistance << 32) | movedistance;
664 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);
672 #ifdef HAVE_LARGE_FILE_SUPPORT
673 g_message(G_GNUC_PRETTY_FUNCTION
674 ": moving handle %p fd %d by %lld bytes from %d", handle,
675 file_private_handle->fd, offset, whence);
677 g_message(G_GNUC_PRETTY_FUNCTION
678 ": moving handle %p fd %d by %ld bytes from %d", handle,
679 file_private_handle->fd, offset, whence);
683 newpos=lseek(file_private_handle->fd, offset, whence);
686 g_message(G_GNUC_PRETTY_FUNCTION
687 ": lseek on handle %p fd %d returned error %s",
688 handle, file_private_handle->fd, strerror(errno));
691 return(INVALID_SET_FILE_POINTER);
695 #ifdef HAVE_LARGE_FILE_SUPPORT
696 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
698 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
702 #ifdef HAVE_LARGE_FILE_SUPPORT
703 ret=newpos & 0xFFFFFFFF;
704 if(highmovedistance!=NULL) {
705 *highmovedistance=newpos>>32;
709 if(highmovedistance!=NULL) {
710 /* Accurate, but potentially dodgy :-) */
716 g_message(G_GNUC_PRETTY_FUNCTION
717 ": move of handle %p fd %d returning %d/%d", handle,
718 file_private_handle->fd, ret,
719 highmovedistance==NULL?0:*highmovedistance);
725 static gboolean file_setendoffile(gpointer handle)
727 struct _WapiHandle_file *file_handle;
728 struct _WapiHandlePrivate_file *file_private_handle;
734 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
735 (gpointer *)&file_handle,
736 (gpointer *)&file_private_handle);
738 g_warning (G_GNUC_PRETTY_FUNCTION
739 ": error looking up file handle %p", handle);
743 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
744 !(file_handle->fileaccess&GENERIC_ALL)) {
746 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
752 /* Find the current file position, and the file length. If
753 * the file position is greater than the length, write to
754 * extend the file with a hole. If the file position is less
755 * than the length, truncate the file.
758 ret=fstat(file_private_handle->fd, &statbuf);
761 g_message(G_GNUC_PRETTY_FUNCTION
762 ": handle %p fd %d fstat failed: %s", handle,
763 file_private_handle->fd, strerror(errno));
768 size=statbuf.st_size;
770 pos=lseek(file_private_handle->fd, (off_t)0, SEEK_CUR);
773 g_message(G_GNUC_PRETTY_FUNCTION
774 ": handle %p fd %d lseek failed: %s", handle,
775 file_private_handle->fd, strerror(errno));
784 ret=write(file_private_handle->fd, "", 1);
786 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
790 g_message(G_GNUC_PRETTY_FUNCTION
791 ": handle %p fd %d extend write failed: %s",
792 handle, file_private_handle->fd,
800 /* always truncate, because the extend write() adds an extra
801 * byte to the end of the file
804 ret=ftruncate(file_private_handle->fd, pos);
806 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
810 g_message(G_GNUC_PRETTY_FUNCTION
811 ": handle %p fd %d ftruncate failed: %s", handle,
812 file_private_handle->fd, strerror(errno));
821 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
823 struct _WapiHandle_file *file_handle;
824 struct _WapiHandlePrivate_file *file_private_handle;
830 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
831 (gpointer *)&file_handle,
832 (gpointer *)&file_private_handle);
834 g_warning (G_GNUC_PRETTY_FUNCTION
835 ": error looking up file handle %p", handle);
836 return(INVALID_FILE_SIZE);
839 if(!(file_handle->fileaccess&GENERIC_READ) &&
840 !(file_handle->fileaccess&GENERIC_WRITE) &&
841 !(file_handle->fileaccess&GENERIC_ALL)) {
843 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, file_handle->fileaccess);
846 return(INVALID_FILE_SIZE);
849 ret=fstat(file_private_handle->fd, &statbuf);
852 g_message(G_GNUC_PRETTY_FUNCTION
853 ": handle %p fd %d fstat failed: %s", handle,
854 file_private_handle->fd, strerror(errno));
857 return(INVALID_FILE_SIZE);
860 #ifdef HAVE_LARGE_FILE_SUPPORT
861 size=statbuf.st_size & 0xFFFFFFFF;
863 *highsize=statbuf.st_size>>32;
867 /* Accurate, but potentially dodgy :-) */
870 size=statbuf.st_size;
874 g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
881 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
882 WapiFileTime *last_access,
883 WapiFileTime *last_write)
885 struct _WapiHandle_file *file_handle;
886 struct _WapiHandlePrivate_file *file_private_handle;
889 guint64 create_ticks, access_ticks, write_ticks;
892 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
893 (gpointer *)&file_handle,
894 (gpointer *)&file_private_handle);
896 g_warning (G_GNUC_PRETTY_FUNCTION
897 ": error looking up file handle %p", handle);
901 if(!(file_handle->fileaccess&GENERIC_READ) &&
902 !(file_handle->fileaccess&GENERIC_ALL)) {
904 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
910 ret=fstat(file_private_handle->fd, &statbuf);
913 g_message(G_GNUC_PRETTY_FUNCTION
914 ": handle %p fd %d fstat failed: %s", handle,
915 file_private_handle->fd, strerror(errno));
922 g_message(G_GNUC_PRETTY_FUNCTION
923 ": atime: %ld ctime: %ld mtime: %ld",
924 statbuf.st_atime, statbuf.st_ctime,
928 /* Try and guess a meaningful create time by using the older
931 /* The magic constant comes from msdn documentation
932 * "Converting a time_t Value to a File Time"
934 if(statbuf.st_atime < statbuf.st_ctime) {
935 create_ticks=((guint64)statbuf.st_atime*10000000)
936 + 116444736000000000ULL;
938 create_ticks=((guint64)statbuf.st_ctime*10000000)
939 + 116444736000000000ULL;
942 access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
943 write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
946 g_message(G_GNUC_PRETTY_FUNCTION
947 ": aticks: %llu cticks: %llu wticks: %llu",
948 access_ticks, create_ticks, write_ticks);
951 if(create_time!=NULL) {
952 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
953 create_time->dwHighDateTime = create_ticks >> 32;
956 if(last_access!=NULL) {
957 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
958 last_access->dwHighDateTime = access_ticks >> 32;
961 if(last_write!=NULL) {
962 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
963 last_write->dwHighDateTime = write_ticks >> 32;
969 static gboolean file_setfiletime(gpointer handle,
970 const WapiFileTime *create_time G_GNUC_UNUSED,
971 const WapiFileTime *last_access,
972 const WapiFileTime *last_write)
974 struct _WapiHandle_file *file_handle;
975 struct _WapiHandlePrivate_file *file_private_handle;
978 struct utimbuf utbuf;
980 guint64 access_ticks, write_ticks;
983 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
984 (gpointer *)&file_handle,
985 (gpointer *)&file_private_handle);
987 g_warning (G_GNUC_PRETTY_FUNCTION
988 ": error looking up file handle %p", handle);
992 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
993 !(file_handle->fileaccess&GENERIC_ALL)) {
995 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
1001 if(file_handle->filename==0) {
1003 g_message(G_GNUC_PRETTY_FUNCTION
1004 ": handle %p fd %d unknown filename", handle,
1005 file_private_handle->fd);
1011 /* Get the current times, so we can put the same times back in
1012 * the event that one of the FileTime structs is NULL
1014 ret=fstat(file_private_handle->fd, &statbuf);
1017 g_message(G_GNUC_PRETTY_FUNCTION
1018 ": handle %p fd %d fstat failed: %s", handle,
1019 file_private_handle->fd, strerror(errno));
1025 if(last_access!=NULL) {
1026 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
1027 last_access->dwLowDateTime;
1028 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1030 utbuf.actime=statbuf.st_atime;
1033 if(last_write!=NULL) {
1034 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
1035 last_write->dwLowDateTime;
1036 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1038 utbuf.modtime=statbuf.st_mtime;
1042 g_message(G_GNUC_PRETTY_FUNCTION
1043 ": setting handle %p access %ld write %ld", handle,
1044 utbuf.actime, utbuf.modtime);
1047 name=_wapi_handle_scratch_lookup (file_handle->filename);
1049 ret=utime(name, &utbuf);
1052 g_message(G_GNUC_PRETTY_FUNCTION
1053 ": handle %p [%s] fd %d utime failed: %s", handle,
1054 name, file_private_handle->fd, strerror(errno));
1066 static void console_close_shared (gpointer handle)
1068 struct _WapiHandle_file *console_handle;
1071 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1072 (gpointer *)&console_handle, NULL);
1074 g_warning (G_GNUC_PRETTY_FUNCTION
1075 ": error looking up console handle %p", handle);
1080 g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
1083 if(console_handle->filename!=0) {
1084 _wapi_handle_scratch_delete (console_handle->filename);
1085 console_handle->filename=0;
1087 if(console_handle->security_attributes!=0) {
1088 _wapi_handle_scratch_delete (console_handle->security_attributes);
1089 console_handle->security_attributes=0;
1093 static void console_close_private (gpointer handle)
1095 struct _WapiHandlePrivate_file *console_private_handle;
1098 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
1099 (gpointer *)&console_private_handle);
1101 g_warning (G_GNUC_PRETTY_FUNCTION
1102 ": error looking up console handle %p", handle);
1107 g_message(G_GNUC_PRETTY_FUNCTION
1108 ": closing console handle %p with fd %d", handle,
1109 console_private_handle->fd);
1112 close(console_private_handle->fd);
1115 static WapiFileType console_getfiletype(void)
1117 return(FILE_TYPE_CHAR);
1120 static gboolean console_read(gpointer handle, gpointer buffer,
1121 guint32 numbytes, guint32 *bytesread,
1122 WapiOverlapped *overlapped G_GNUC_UNUSED)
1124 struct _WapiHandle_file *console_handle;
1125 struct _WapiHandlePrivate_file *console_private_handle;
1129 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1130 (gpointer *)&console_handle,
1131 (gpointer *)&console_private_handle);
1133 g_warning (G_GNUC_PRETTY_FUNCTION
1134 ": error looking up console handle %p", handle);
1138 if(bytesread!=NULL) {
1142 if(!(console_handle->fileaccess&GENERIC_READ) &&
1143 !(console_handle->fileaccess&GENERIC_ALL)) {
1145 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, console_private_handle->fd, console_handle->fileaccess);
1152 ret=read(console_private_handle->fd, buffer, numbytes);
1154 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1158 g_message(G_GNUC_PRETTY_FUNCTION
1159 ": read of handle %p fd %d error: %s", handle,
1160 console_private_handle->fd, strerror(errno));
1166 if(bytesread!=NULL) {
1173 static gboolean console_write(gpointer handle, gconstpointer buffer,
1174 guint32 numbytes, guint32 *byteswritten,
1175 WapiOverlapped *overlapped G_GNUC_UNUSED)
1177 struct _WapiHandle_file *console_handle;
1178 struct _WapiHandlePrivate_file *console_private_handle;
1182 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1183 (gpointer *)&console_handle,
1184 (gpointer *)&console_private_handle);
1186 g_warning (G_GNUC_PRETTY_FUNCTION
1187 ": error looking up console handle %p", handle);
1191 if(byteswritten!=NULL) {
1195 if(!(console_handle->fileaccess&GENERIC_WRITE) &&
1196 !(console_handle->fileaccess&GENERIC_ALL)) {
1198 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd, console_handle->fileaccess);
1205 ret=write(console_private_handle->fd, buffer, numbytes);
1207 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1211 g_message(G_GNUC_PRETTY_FUNCTION
1212 ": write of handle %p fd %d error: %s", handle,
1213 console_private_handle->fd, strerror(errno));
1218 if(byteswritten!=NULL) {
1225 static void pipe_close_shared (gpointer handle)
1227 struct _WapiHandle_file *pipe_handle;
1230 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1231 (gpointer *)&pipe_handle, NULL);
1233 g_warning (G_GNUC_PRETTY_FUNCTION
1234 ": error looking up pipe handle %p", handle);
1239 g_message(G_GNUC_PRETTY_FUNCTION ": closing pipe handle %p", handle);
1242 if(pipe_handle->filename!=0) {
1243 _wapi_handle_scratch_delete (pipe_handle->filename);
1244 pipe_handle->filename=0;
1246 if(pipe_handle->security_attributes!=0) {
1247 _wapi_handle_scratch_delete (pipe_handle->security_attributes);
1248 pipe_handle->security_attributes=0;
1252 static void pipe_close_private (gpointer handle)
1254 struct _WapiHandlePrivate_file *pipe_private_handle;
1257 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
1258 (gpointer *)&pipe_private_handle);
1260 g_warning (G_GNUC_PRETTY_FUNCTION
1261 ": error looking up pipe handle %p", handle);
1266 g_message(G_GNUC_PRETTY_FUNCTION
1267 ": closing pipe handle %p with fd %d", handle,
1268 pipe_private_handle->fd);
1271 close(pipe_private_handle->fd);
1274 static WapiFileType pipe_getfiletype(void)
1276 return(FILE_TYPE_PIPE);
1279 static gboolean pipe_read (gpointer handle, gpointer buffer,
1280 guint32 numbytes, guint32 *bytesread,
1281 WapiOverlapped *overlapped G_GNUC_UNUSED)
1283 struct _WapiHandle_file *pipe_handle;
1284 struct _WapiHandlePrivate_file *pipe_private_handle;
1288 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1289 (gpointer *)&pipe_handle,
1290 (gpointer *)&pipe_private_handle);
1292 g_warning (G_GNUC_PRETTY_FUNCTION
1293 ": error looking up pipe handle %p", handle);
1297 if(bytesread!=NULL) {
1301 if(!(pipe_handle->fileaccess&GENERIC_READ) &&
1302 !(pipe_handle->fileaccess&GENERIC_ALL)) {
1304 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, pipe_private_handle->fd, pipe_handle->fileaccess);
1311 g_message (G_GNUC_PRETTY_FUNCTION
1312 ": reading up to %d bytes from pipe %p (fd %d)", numbytes,
1313 handle, pipe_private_handle->fd);
1317 ret=read(pipe_private_handle->fd, buffer, numbytes);
1319 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1323 g_message(G_GNUC_PRETTY_FUNCTION
1324 ": read of handle %p fd %d error: %s", handle,
1325 pipe_private_handle->fd, strerror(errno));
1332 g_message (G_GNUC_PRETTY_FUNCTION ": read %d bytes from pipe", ret);
1335 if(bytesread!=NULL) {
1342 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1343 guint32 numbytes, guint32 *byteswritten,
1344 WapiOverlapped *overlapped G_GNUC_UNUSED)
1346 struct _WapiHandle_file *pipe_handle;
1347 struct _WapiHandlePrivate_file *pipe_private_handle;
1351 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1352 (gpointer *)&pipe_handle,
1353 (gpointer *)&pipe_private_handle);
1355 g_warning (G_GNUC_PRETTY_FUNCTION
1356 ": error looking up pipe handle %p", handle);
1360 if(byteswritten!=NULL) {
1364 if(!(pipe_handle->fileaccess&GENERIC_WRITE) &&
1365 !(pipe_handle->fileaccess&GENERIC_ALL)) {
1367 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, pipe_private_handle->fd, pipe_handle->fileaccess);
1374 g_message (G_GNUC_PRETTY_FUNCTION
1375 ": writing up to %d bytes to pipe %p (fd %d)", numbytes,
1376 handle, pipe_private_handle->fd);
1380 ret=write(pipe_private_handle->fd, buffer, numbytes);
1382 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1386 g_message(G_GNUC_PRETTY_FUNCTION
1387 ": write of handle %p fd %d error: %s", handle,
1388 pipe_private_handle->fd, strerror(errno));
1393 if(byteswritten!=NULL) {
1400 static int convert_flags(guint32 fileaccess, guint32 createmode)
1404 switch(fileaccess) {
1411 case GENERIC_READ|GENERIC_WRITE:
1416 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
1422 switch(createmode) {
1424 flags|=O_CREAT|O_EXCL;
1427 flags|=O_CREAT|O_TRUNC;
1434 case TRUNCATE_EXISTING:
1439 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
1448 static guint32 convert_from_flags(int flags)
1450 guint32 fileaccess=0;
1453 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1456 if((flags & O_ACCMODE) == O_RDONLY) {
1457 fileaccess=GENERIC_READ;
1458 } else if ((flags & O_ACCMODE) == O_WRONLY) {
1459 fileaccess=GENERIC_WRITE;
1460 } else if ((flags & O_ACCMODE) == O_RDWR) {
1461 fileaccess=GENERIC_READ|GENERIC_WRITE;
1464 g_message(G_GNUC_PRETTY_FUNCTION
1465 ": Can't figure out flags 0x%x", flags);
1469 /* Maybe sort out create mode too */
1474 static mode_t convert_perms(guint32 sharemode)
1478 if(sharemode&FILE_SHARE_READ) {
1481 if(sharemode&FILE_SHARE_WRITE) {
1491 * @name: a pointer to a NULL-terminated unicode string, that names
1492 * the file or other object to create.
1493 * @fileaccess: specifies the file access mode
1494 * @sharemode: whether the file should be shared. This parameter is
1495 * currently ignored.
1496 * @security: Ignored for now.
1497 * @createmode: specifies whether to create a new file, whether to
1498 * overwrite an existing file, whether to truncate the file, etc.
1499 * @attrs: specifies file attributes and flags. On win32 attributes
1500 * are characteristics of the file, not the handle, and are ignored
1501 * when an existing file is opened. Flags give the library hints on
1502 * how to process a file to optimise performance.
1503 * @template: the handle of an open %GENERIC_READ file that specifies
1504 * attributes to apply to a newly created file, ignoring @attrs.
1505 * Normally this parameter is NULL. This parameter is ignored when an
1506 * existing file is opened.
1508 * Creates a new file handle. This only applies to normal files:
1509 * pipes are handled by CreatePipe(), and console handles are created
1510 * with GetStdHandle().
1512 * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1514 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1515 guint32 sharemode, WapiSecurityAttributes *security,
1516 guint32 createmode, guint32 attrs,
1517 gpointer template G_GNUC_UNUSED)
1519 struct _WapiHandle_file *file_handle;
1520 struct _WapiHandlePrivate_file *file_private_handle;
1523 int flags=convert_flags(fileaccess, createmode);
1524 mode_t perms=convert_perms(sharemode);
1528 gpointer cf_ret = INVALID_HANDLE_VALUE;
1529 struct stat statbuf;
1530 gboolean file_already_shared;
1531 guint32 file_existing_share, file_existing_access;
1533 mono_once (&io_ops_once, io_ops_init);
1537 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1540 return(INVALID_HANDLE_VALUE);
1543 filename=mono_unicode_to_external (name);
1544 if(filename==NULL) {
1546 g_message(G_GNUC_PRETTY_FUNCTION
1547 ": unicode conversion returned NULL");
1550 return(INVALID_HANDLE_VALUE);
1554 g_message (G_GNUC_PRETTY_FUNCTION ": Opening %s with share 0x%x and access 0x%x", filename, sharemode, fileaccess);
1557 fd = open(filename, flags, perms);
1559 /* If we were trying to open a directory with write permissions
1560 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1561 * EISDIR. However, this is a bit bogus because calls to
1562 * manipulate the directory (e.g. SetFileTime) will still work on
1563 * the directory because they use other API calls
1564 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1565 * to open the directory again without write permission.
1567 if (fd == -1 && errno == EISDIR)
1569 /* Try again but don't try to make it writable */
1570 fd = open(filename, flags & ~(O_RDWR|O_WRONLY), perms);
1575 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
1576 filename, strerror(errno));
1578 _wapi_set_last_error_from_errno ();
1581 return(INVALID_HANDLE_VALUE);
1584 ret = fstat (fd, &statbuf);
1587 g_message (G_GNUC_PRETTY_FUNCTION ": fstat error of file %s: %s", filename, strerror (errno));
1589 _wapi_set_last_error_from_errno ();
1593 return(INVALID_HANDLE_VALUE);
1596 file_already_shared = _wapi_handle_get_or_set_share (statbuf.st_dev, statbuf.st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access);
1598 if (file_already_shared) {
1599 if (file_existing_share == 0) {
1600 /* Quick and easy, no possibility to share */
1602 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing = NONE", fileaccess);
1604 SetLastError (ERROR_SHARING_VIOLATION);
1608 return(INVALID_HANDLE_VALUE);
1611 if (((file_existing_share == FILE_SHARE_READ) &&
1612 (fileaccess != GENERIC_READ)) ||
1613 ((file_existing_share == FILE_SHARE_WRITE) &&
1614 (fileaccess != GENERIC_WRITE))) {
1615 /* New access mode doesn't match up */
1617 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", fileaccess, file_existing_share);
1619 SetLastError (ERROR_SHARING_VIOLATION);
1623 return(INVALID_HANDLE_VALUE);
1626 if (((file_existing_access & GENERIC_READ) &&
1627 !(sharemode & FILE_SHARE_READ)) ||
1628 ((file_existing_access & GENERIC_WRITE) &&
1629 !(sharemode & FILE_SHARE_WRITE))) {
1630 /* New share mode doesn't match up */
1632 g_message (G_GNUC_PRETTY_FUNCTION ": Access mode prevents open: requested share: 0x%x, file has access: 0x%x", sharemode, file_existing_access);
1634 SetLastError (ERROR_SHARING_VIOLATION);
1638 return(INVALID_HANDLE_VALUE);
1642 g_message (G_GNUC_PRETTY_FUNCTION ": New file!");
1646 handle=_wapi_handle_new (WAPI_HANDLE_FILE);
1647 if(handle==_WAPI_HANDLE_INVALID) {
1648 g_warning (G_GNUC_PRETTY_FUNCTION
1649 ": error creating file handle");
1653 return(INVALID_HANDLE_VALUE);
1656 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
1658 thr_ret = _wapi_handle_lock_handle (handle);
1659 g_assert (thr_ret == 0);
1661 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1662 (gpointer *)&file_handle,
1663 (gpointer *)&file_private_handle);
1665 g_warning (G_GNUC_PRETTY_FUNCTION
1666 ": error looking up file handle %p", handle);
1672 file_private_handle->fd=fd;
1673 file_private_handle->assigned=TRUE;
1674 file_private_handle->async = ((attrs & FILE_FLAG_OVERLAPPED) != 0);
1675 file_handle->filename=_wapi_handle_scratch_store (filename,
1677 if(security!=NULL) {
1678 file_handle->security_attributes=_wapi_handle_scratch_store (
1679 security, sizeof(WapiSecurityAttributes));
1682 file_handle->fileaccess=fileaccess;
1683 file_handle->sharemode=sharemode;
1684 file_handle->attrs=attrs;
1685 file_handle->device = statbuf.st_dev;
1686 file_handle->inode = statbuf.st_ino;
1689 g_message(G_GNUC_PRETTY_FUNCTION
1690 ": returning handle %p with fd %d", handle,
1691 file_private_handle->fd);
1695 thr_ret = _wapi_handle_unlock_handle (handle);
1696 g_assert (thr_ret == 0);
1697 pthread_cleanup_pop (0);
1706 * @name: a pointer to a NULL-terminated unicode string, that names
1707 * the file to be deleted.
1709 * Deletes file @name.
1711 * Return value: %TRUE on success, %FALSE otherwise.
1713 gboolean DeleteFile(const gunichar2 *name)
1720 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1726 filename=mono_unicode_to_external(name);
1727 if(filename==NULL) {
1729 g_message(G_GNUC_PRETTY_FUNCTION
1730 ": unicode conversion returned NULL");
1736 ret=unlink(filename);
1744 _wapi_set_last_error_from_errno ();
1750 * @name: a pointer to a NULL-terminated unicode string, that names
1751 * the file to be moved.
1752 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1753 * new name for the file.
1755 * Renames file @name to @dest_name
1757 * Return value: %TRUE on success, %FALSE otherwise.
1759 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1761 gchar *utf8_name, *utf8_dest_name;
1764 utf8_name = mono_unicode_to_external (name);
1765 if (utf8_name == NULL) {
1767 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1773 utf8_dest_name = mono_unicode_to_external (dest_name);
1774 if (utf8_dest_name == NULL) {
1776 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1783 result = rename (utf8_name, utf8_dest_name);
1785 g_free (utf8_dest_name);
1787 if (result != 0 && errno == EXDEV) {
1788 /* Try a copy to the new location, and delete the source */
1789 if (CopyFile (name, dest_name, TRUE)==FALSE) {
1793 return(DeleteFile (name));
1802 SetLastError (ERROR_ALREADY_EXISTS);
1806 _wapi_set_last_error_from_errno ();
1815 * @name: a pointer to a NULL-terminated unicode string, that names
1816 * the file to be copied.
1817 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1818 * new name for the file.
1819 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1821 * Copies file @name to @dest_name
1823 * Return value: %TRUE on success, %FALSE otherwise.
1825 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
1826 gboolean fail_if_exists)
1828 gchar *utf8_src, *utf8_dest;
1829 int src_fd, dest_fd;
1835 utf8_src = mono_unicode_to_external (name);
1836 if (utf8_src == NULL) {
1838 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of source returned NULL");
1841 SetLastError (ERROR_INVALID_PARAMETER);
1845 utf8_dest = mono_unicode_to_external (dest_name);
1846 if (utf8_dest == NULL) {
1848 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of dest returned NULL");
1851 SetLastError (ERROR_INVALID_PARAMETER);
1858 src_fd = open (utf8_src, O_RDONLY);
1860 _wapi_set_last_error_from_errno ();
1868 if (fstat (src_fd, &st) < 0) {
1869 _wapi_set_last_error_from_errno ();
1878 if (fail_if_exists) {
1879 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
1881 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
1883 /* O_TRUNC might cause a fail if the file
1886 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
1891 _wapi_set_last_error_from_errno ();
1900 buf_size = st.st_blksize;
1901 buf = (char *) alloca (buf_size);
1904 remain = read (src_fd, buf, buf_size);
1907 if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
1911 _wapi_set_last_error_from_errno ();
1925 while (remain > 0) {
1926 if ((n = write (dest_fd, buf, remain)) < 0) {
1927 if (errno == EINTR && !_wapi_thread_cur_apc_pending())
1930 _wapi_set_last_error_from_errno ();
1932 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
1955 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
1956 static gpointer stdin_handle=NULL;
1957 static gpointer stdout_handle=NULL;
1958 static gpointer stderr_handle=NULL;
1960 static gpointer stdhandle_create (int fd, const guchar *name)
1962 struct _WapiHandle_file *file_handle;
1963 struct _WapiHandlePrivate_file *file_private_handle;
1965 gpointer handle, ret = NULL;
1970 g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
1974 /* Check if fd is valid */
1976 flags=fcntl(fd, F_GETFL);
1978 while (flags==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1981 /* Invalid fd. Not really much point checking for EBADF
1985 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
1986 fd, strerror(errno));
1989 return(INVALID_HANDLE_VALUE);
1992 handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
1993 if(handle==_WAPI_HANDLE_INVALID) {
1994 g_warning (G_GNUC_PRETTY_FUNCTION
1995 ": error creating file handle");
1999 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2001 thr_ret = _wapi_handle_lock_handle (handle);
2002 g_assert (thr_ret == 0);
2004 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2005 (gpointer *)&file_handle,
2006 (gpointer *)&file_private_handle);
2008 g_warning (G_GNUC_PRETTY_FUNCTION
2009 ": error looking up console handle %p", handle);
2014 file_private_handle->fd=fd;
2015 file_private_handle->assigned=TRUE;
2016 file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
2017 /* some default security attributes might be needed */
2018 file_handle->security_attributes=0;
2019 file_handle->fileaccess=convert_from_flags(flags);
2020 file_handle->sharemode=0;
2021 file_handle->attrs=0;
2024 g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
2025 handle, file_private_handle->fd);
2029 thr_ret = _wapi_handle_unlock_handle (handle);
2030 g_assert (thr_ret == 0);
2031 pthread_cleanup_pop (0);
2036 static void stdhandle_init (void)
2038 stdin_handle=stdhandle_create (0, "<stdin>");
2039 stdout_handle=stdhandle_create (1, "<stdout>");
2040 stderr_handle=stdhandle_create (2, "<stderr>");
2045 * @stdhandle: specifies the file descriptor
2047 * Returns a handle for stdin, stdout, or stderr. Always returns the
2048 * same handle for the same @stdhandle.
2050 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2053 gpointer GetStdHandle(WapiStdHandle stdhandle)
2057 mono_once (&io_ops_once, io_ops_init);
2058 mono_once (&stdhandle_once, stdhandle_init);
2061 case STD_INPUT_HANDLE:
2062 handle=stdin_handle;
2065 case STD_OUTPUT_HANDLE:
2066 handle=stdout_handle;
2069 case STD_ERROR_HANDLE:
2070 handle=stderr_handle;
2075 g_message(G_GNUC_PRETTY_FUNCTION
2076 ": unknown standard handle type");
2079 return(INVALID_HANDLE_VALUE);
2082 /* Add a reference to this handle */
2083 _wapi_handle_ref (handle);
2090 * @handle: The file handle to read from. The handle must have
2091 * %GENERIC_READ access.
2092 * @buffer: The buffer to store read data in
2093 * @numbytes: The maximum number of bytes to read
2094 * @bytesread: The actual number of bytes read is stored here. This
2095 * value can be zero if the handle is positioned at the end of the
2097 * @overlapped: points to a required %WapiOverlapped structure if
2098 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2101 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2102 * function reads up to @numbytes bytes from the file from the current
2103 * file position, and stores them in @buffer. If there are not enough
2104 * bytes left in the file, just the amount available will be read.
2105 * The actual number of bytes read is stored in @bytesread.
2107 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2108 * file position is ignored and the read position is taken from data
2109 * in the @overlapped structure.
2111 * Return value: %TRUE if the read succeeds (even if no bytes were
2112 * read due to an attempt to read past the end of the file), %FALSE on
2115 gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes,
2116 guint32 *bytesread, WapiOverlapped *overlapped)
2118 WapiHandleType type=_wapi_handle_type (handle);
2120 if(io_ops[type].readfile==NULL) {
2124 return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2130 * @handle: The file handle to write to. The handle must have
2131 * %GENERIC_WRITE access.
2132 * @buffer: The buffer to read data from.
2133 * @numbytes: The maximum number of bytes to write.
2134 * @byteswritten: The actual number of bytes written is stored here.
2135 * If the handle is positioned at the file end, the length of the file
2136 * is extended. This parameter may be %NULL.
2137 * @overlapped: points to a required %WapiOverlapped structure if
2138 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2141 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2142 * function writes up to @numbytes bytes from @buffer to the file at
2143 * the current file position. If @handle is positioned at the end of
2144 * the file, the file is extended. The actual number of bytes written
2145 * is stored in @byteswritten.
2147 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2148 * file position is ignored and the write position is taken from data
2149 * in the @overlapped structure.
2151 * Return value: %TRUE if the write succeeds, %FALSE on error.
2153 gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes,
2154 guint32 *byteswritten, WapiOverlapped *overlapped)
2156 WapiHandleType type=_wapi_handle_type (handle);
2158 if(io_ops[type].writefile==NULL) {
2162 return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2168 * @handle: Handle to open file. The handle must have
2169 * %GENERIC_WRITE access.
2171 * Flushes buffers of the file and causes all unwritten data to
2174 * Return value: %TRUE on success, %FALSE otherwise.
2176 gboolean FlushFileBuffers(gpointer handle)
2178 WapiHandleType type=_wapi_handle_type (handle);
2180 if(io_ops[type].flushfile==NULL) {
2184 return(io_ops[type].flushfile (handle));
2189 * @handle: The file handle to set. The handle must have
2190 * %GENERIC_WRITE access.
2192 * Moves the end-of-file position to the current position of the file
2193 * pointer. This function is used to truncate or extend a file.
2195 * Return value: %TRUE on success, %FALSE otherwise.
2197 gboolean SetEndOfFile(gpointer handle)
2199 WapiHandleType type=_wapi_handle_type (handle);
2201 if(io_ops[type].setendoffile==NULL) {
2205 return(io_ops[type].setendoffile (handle));
2210 * @handle: The file handle to set. The handle must have
2211 * %GENERIC_READ or %GENERIC_WRITE access.
2212 * @movedistance: Low 32 bits of a signed value that specifies the
2213 * number of bytes to move the file pointer.
2214 * @highmovedistance: Pointer to the high 32 bits of a signed value
2215 * that specifies the number of bytes to move the file pointer, or
2217 * @method: The starting point for the file pointer move.
2219 * Sets the file pointer of an open file.
2221 * The distance to move the file pointer is calculated from
2222 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2223 * @movedistance is the 32-bit signed value; otherwise, @movedistance
2224 * is the low 32 bits and @highmovedistance a pointer to the high 32
2225 * bits of a 64 bit signed value. A positive distance moves the file
2226 * pointer forward from the position specified by @method; a negative
2227 * distance moves the file pointer backward.
2229 * If the library is compiled without large file support,
2230 * @highmovedistance is ignored and its value is set to zero on a
2231 * successful return.
2233 * Return value: On success, the low 32 bits of the new file pointer.
2234 * If @highmovedistance is not %NULL, the high 32 bits of the new file
2235 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
2237 guint32 SetFilePointer(gpointer handle, gint32 movedistance,
2238 gint32 *highmovedistance, WapiSeekMethod method)
2240 WapiHandleType type=_wapi_handle_type (handle);
2242 if(io_ops[type].seek==NULL) {
2246 return(io_ops[type].seek (handle, movedistance, highmovedistance,
2252 * @handle: The file handle to test.
2254 * Finds the type of file @handle.
2256 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2257 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
2258 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2259 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2261 WapiFileType GetFileType(gpointer handle)
2263 WapiHandleType type=_wapi_handle_type (handle);
2265 if(io_ops[type].getfiletype==NULL) {
2266 return(FILE_TYPE_UNKNOWN);
2269 return(io_ops[type].getfiletype ());
2274 * @handle: The file handle to query. The handle must have
2275 * %GENERIC_READ or %GENERIC_WRITE access.
2276 * @highsize: If non-%NULL, the high 32 bits of the file size are
2279 * Retrieves the size of the file @handle.
2281 * If the library is compiled without large file support, @highsize
2282 * has its value set to zero on a successful return.
2284 * Return value: On success, the low 32 bits of the file size. If
2285 * @highsize is non-%NULL then the high 32 bits of the file size are
2286 * stored here. On failure %INVALID_FILE_SIZE is returned.
2288 guint32 GetFileSize(gpointer handle, guint32 *highsize)
2290 WapiHandleType type=_wapi_handle_type (handle);
2292 if(io_ops[type].getfilesize==NULL) {
2296 return(io_ops[type].getfilesize (handle, highsize));
2301 * @handle: The file handle to query. The handle must have
2302 * %GENERIC_READ access.
2303 * @create_time: Points to a %WapiFileTime structure to receive the
2304 * number of ticks since the epoch that file was created. May be
2306 * @last_access: Points to a %WapiFileTime structure to receive the
2307 * number of ticks since the epoch when file was last accessed. May be
2309 * @last_write: Points to a %WapiFileTime structure to receive the
2310 * number of ticks since the epoch when file was last written to. May
2313 * Finds the number of ticks since the epoch that the file referenced
2314 * by @handle was created, last accessed and last modified. A tick is
2315 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2318 * Create time isn't recorded on POSIX file systems or reported by
2319 * stat(2), so that time is guessed by returning the oldest of the
2322 * Return value: %TRUE on success, %FALSE otherwise.
2324 gboolean GetFileTime(gpointer handle, WapiFileTime *create_time,
2325 WapiFileTime *last_access, WapiFileTime *last_write)
2327 WapiHandleType type=_wapi_handle_type (handle);
2329 if(io_ops[type].getfiletime==NULL) {
2333 return(io_ops[type].getfiletime (handle, create_time, last_access,
2339 * @handle: The file handle to set. The handle must have
2340 * %GENERIC_WRITE access.
2341 * @create_time: Points to a %WapiFileTime structure that contains the
2342 * number of ticks since the epoch that the file was created. May be
2344 * @last_access: Points to a %WapiFileTime structure that contains the
2345 * number of ticks since the epoch when the file was last accessed.
2347 * @last_write: Points to a %WapiFileTime structure that contains the
2348 * number of ticks since the epoch when the file was last written to.
2351 * Sets the number of ticks since the epoch that the file referenced
2352 * by @handle was created, last accessed or last modified. A tick is
2353 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2356 * Create time isn't recorded on POSIX file systems, and is ignored.
2358 * Return value: %TRUE on success, %FALSE otherwise.
2360 gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time,
2361 const WapiFileTime *last_access,
2362 const WapiFileTime *last_write)
2364 WapiHandleType type=_wapi_handle_type (handle);
2366 if(io_ops[type].setfiletime==NULL) {
2370 return(io_ops[type].setfiletime (handle, create_time, last_access,
2374 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2375 * January 1 1601 GMT
2378 #define TICKS_PER_MILLISECOND 10000L
2379 #define TICKS_PER_SECOND 10000000L
2380 #define TICKS_PER_MINUTE 600000000L
2381 #define TICKS_PER_HOUR 36000000000LL
2382 #define TICKS_PER_DAY 864000000000LL
2384 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2386 static const guint16 mon_yday[2][13]={
2387 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2388 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2392 * FileTimeToSystemTime:
2393 * @file_time: Points to a %WapiFileTime structure that contains the
2394 * number of ticks to convert.
2395 * @system_time: Points to a %WapiSystemTime structure to receive the
2398 * Converts a tick count into broken-out time values.
2400 * Return value: %TRUE on success, %FALSE otherwise.
2402 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2403 WapiSystemTime *system_time)
2405 gint64 file_ticks, totaldays, rem, y;
2408 if(system_time==NULL) {
2410 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2416 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2417 file_time->dwLowDateTime;
2419 /* Really compares if file_ticks>=0x8000000000000000
2420 * (LLONG_MAX+1) but we're working with a signed value for the
2421 * year and day calculation to work later
2425 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2431 totaldays=(file_ticks / TICKS_PER_DAY);
2432 rem = file_ticks % TICKS_PER_DAY;
2434 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2438 system_time->wHour=rem/TICKS_PER_HOUR;
2439 rem %= TICKS_PER_HOUR;
2441 g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2442 system_time->wHour, rem);
2445 system_time->wMinute = rem / TICKS_PER_MINUTE;
2446 rem %= TICKS_PER_MINUTE;
2448 g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2449 system_time->wMinute, rem);
2452 system_time->wSecond = rem / TICKS_PER_SECOND;
2453 rem %= TICKS_PER_SECOND;
2455 g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2456 system_time->wSecond, rem);
2459 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2461 g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2462 system_time->wMilliseconds);
2465 /* January 1, 1601 was a Monday, according to Emacs calendar */
2466 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2468 g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2469 system_time->wDayOfWeek);
2472 /* This algorithm to find year and month given days from epoch
2477 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2478 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2480 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2481 /* Guess a corrected year, assuming 365 days per year */
2482 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2484 g_message(G_GNUC_PRETTY_FUNCTION
2485 ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2487 g_message(G_GNUC_PRETTY_FUNCTION
2488 ": LEAPS(yg): %lld LEAPS(y): %lld",
2489 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2492 /* Adjust days and y to match the guessed year. */
2493 totaldays -= ((yg - y) * 365
2494 + LEAPS_THRU_END_OF (yg - 1)
2495 - LEAPS_THRU_END_OF (y - 1));
2497 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2502 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2506 system_time->wYear = y;
2508 g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2511 ip = mon_yday[isleap(y)];
2513 for(y=11; totaldays < ip[y]; --y) {
2518 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2521 system_time->wMonth = y + 1;
2523 g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2526 system_time->wDay = totaldays + 1;
2528 g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2535 file_compare (gconstpointer a, gconstpointer b)
2537 gchar *astr = *(gchar **) a;
2538 gchar *bstr = *(gchar **) b;
2540 return strcmp (astr, bstr);
2544 get_errno_from_g_file_error (gint error)
2548 case G_FILE_ERROR_ACCES:
2553 case G_FILE_ERROR_NAMETOOLONG:
2554 error = ENAMETOOLONG;
2558 case G_FILE_ERROR_NOENT:
2563 case G_FILE_ERROR_NOTDIR:
2568 case G_FILE_ERROR_NXIO:
2573 case G_FILE_ERROR_NODEV:
2578 case G_FILE_ERROR_ROFS:
2583 case G_FILE_ERROR_TXTBSY:
2588 case G_FILE_ERROR_FAULT:
2593 case G_FILE_ERROR_LOOP:
2598 case G_FILE_ERROR_NOSPC:
2603 case G_FILE_ERROR_NOMEM:
2608 case G_FILE_ERROR_MFILE:
2613 case G_FILE_ERROR_NFILE:
2618 case G_FILE_ERROR_BADF:
2623 case G_FILE_ERROR_INVAL:
2628 case G_FILE_ERROR_PIPE:
2633 case G_FILE_ERROR_AGAIN:
2638 case G_FILE_ERROR_INTR:
2643 case G_FILE_ERROR_IO:
2648 case G_FILE_ERROR_PERM:
2652 case G_FILE_ERROR_FAILED:
2653 error = ERROR_INVALID_PARAMETER;
2660 /* scandir using glib */
2662 mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
2664 GError *error = NULL;
2669 GPatternSpec *patspec;
2671 dir = g_dir_open (dirname, 0, &error);
2673 /* g_dir_open returns ENOENT on directories on which we don't
2674 * have read/x permission */
2675 gint errnum = get_errno_from_g_file_error (error->code);
2676 g_error_free (error);
2677 if (errnum == ENOENT && g_file_test (dirname, G_FILE_TEST_IS_DIR))
2684 patspec = g_pattern_spec_new (pattern);
2685 names = g_ptr_array_new ();
2686 while ((name = g_dir_read_name (dir)) != NULL) {
2687 if (g_pattern_match_string (patspec, name))
2688 g_ptr_array_add (names, g_strdup (name));
2691 g_pattern_spec_free (patspec);
2693 result = names->len;
2695 g_ptr_array_sort (names, file_compare);
2696 g_ptr_array_set_size (names, result + 1);
2698 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
2700 g_ptr_array_free (names, TRUE);
2706 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
2708 struct _WapiHandlePrivate_find *find_handle;
2709 gpointer handle, find_ret = INVALID_HANDLE_VALUE;
2711 gchar *utf8_pattern = NULL, *dir_part, *entry_part;
2714 gboolean unref = FALSE;
2716 if (pattern == NULL) {
2718 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
2721 SetLastError (ERROR_PATH_NOT_FOUND);
2722 return(INVALID_HANDLE_VALUE);
2725 utf8_pattern = mono_unicode_to_external (pattern);
2726 if (utf8_pattern == NULL) {
2728 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2731 SetLastError (ERROR_INVALID_NAME);
2732 return(INVALID_HANDLE_VALUE);
2736 g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
2740 /* Figure out which bit of the pattern is the directory */
2741 dir_part=g_path_get_dirname (utf8_pattern);
2742 entry_part=g_path_get_basename (utf8_pattern);
2745 /* Don't do this check for now, it breaks if directories
2746 * really do have metachars in their names (see bug 58116).
2747 * FIXME: Figure out a better solution to keep some checks...
2749 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
2750 SetLastError (ERROR_INVALID_NAME);
2752 g_free (entry_part);
2753 g_free (utf8_pattern);
2754 return(INVALID_HANDLE_VALUE);
2758 handle=_wapi_handle_new (WAPI_HANDLE_FIND);
2759 if(handle==_WAPI_HANDLE_INVALID) {
2760 g_warning (G_GNUC_PRETTY_FUNCTION ": error creating find handle");
2762 g_free (entry_part);
2763 g_free (utf8_pattern);
2765 return(INVALID_HANDLE_VALUE);
2768 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2770 thr_ret = _wapi_handle_lock_handle (handle);
2771 g_assert (thr_ret == 0);
2773 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
2774 (gpointer *)&find_handle);
2776 g_warning (G_GNUC_PRETTY_FUNCTION
2777 ": error looking up find handle %p", handle);
2780 g_free (entry_part);
2782 g_free (utf8_pattern);
2783 utf8_pattern = NULL;
2787 /* The pattern can specify a directory or a set of files.
2789 * The pattern can have wildcard characters ? and *, but only
2790 * in the section after the last directory delimiter. (Return
2791 * ERROR_INVALID_NAME if there are wildcards in earlier path
2792 * sections.) "*" has the usual 0-or-more chars meaning. "?"
2793 * means "match one character", "??" seems to mean "match one
2794 * or two characters", "???" seems to mean "match one, two or
2795 * three characters", etc. Windows will also try and match
2796 * the mangled "short name" of files, so 8 character patterns
2797 * with wildcards will show some surprising results.
2799 * All the written documentation I can find says that '?'
2800 * should only match one character, and doesn't mention '??',
2801 * '???' etc. I'm going to assume that the strict behaviour
2802 * (ie '???' means three and only three characters) is the
2803 * correct one, because that lets me use fnmatch(3) rather
2804 * than mess around with regexes.
2807 find_handle->namelist = NULL;
2808 result = mono_io_scandir (dir_part, entry_part, &find_handle->namelist);
2812 gint errnum = errno;
2814 _wapi_set_last_error_from_errno ();
2816 g_message (G_GNUC_PRETTY_FUNCTION ": scandir error: %s", g_strerror (errnum));
2818 g_free (utf8_pattern);
2819 g_free (entry_part);
2825 g_free (utf8_pattern);
2826 g_free (entry_part);
2829 g_message (G_GNUC_PRETTY_FUNCTION ": Got %d matches", result);
2832 find_handle->dir_part = dir_part;
2833 find_handle->num = result;
2834 find_handle->count = 0;
2839 thr_ret = _wapi_handle_unlock_handle (handle);
2840 g_assert (thr_ret == 0);
2841 pthread_cleanup_pop (0);
2843 /* FindNextFile has to be called after unlocking the handle,
2844 * because it wants to lock the handle itself
2846 if (find_ret != INVALID_HANDLE_VALUE &&
2847 !FindNextFile (handle, find_data)) {
2849 SetLastError (ERROR_NO_MORE_FILES);
2850 find_ret = INVALID_HANDLE_VALUE;
2853 /* Must not call _wapi_handle_unref() with the handle already
2857 _wapi_handle_unref (handle);
2863 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
2865 struct _WapiHandlePrivate_find *find_handle;
2869 gchar *utf8_filename, *utf8_basename;
2870 gunichar2 *utf16_basename;
2874 gboolean ret = FALSE;
2876 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
2877 (gpointer *)&find_handle);
2879 g_warning (G_GNUC_PRETTY_FUNCTION
2880 ": error looking up find handle %p", handle);
2881 SetLastError (ERROR_INVALID_HANDLE);
2885 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2887 thr_ret = _wapi_handle_lock_handle (handle);
2888 g_assert (thr_ret == 0);
2891 if (find_handle->count >= find_handle->num) {
2892 SetLastError (ERROR_NO_MORE_FILES);
2896 /* stat next match */
2898 filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
2899 if (lstat (filename, &buf) != 0) {
2901 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
2908 /* Check for dangling symlinks, and ignore them (principle of
2909 * least surprise, avoiding confusion where we report the file
2910 * exists, but when someone tries to open it we would report
2913 if(S_ISLNK (buf.st_mode)) {
2914 if(stat (filename, &buf) != 0) {
2920 utf8_filename=mono_utf8_from_external (filename);
2921 if(utf8_filename==NULL) {
2922 /* We couldn't turn this filename into utf8 (eg the
2923 * encoding of the name wasn't convertible), so just
2932 g_message (G_GNUC_PRETTY_FUNCTION ": Found [%s]", utf8_filename);
2935 /* fill data block */
2937 if (buf.st_mtime < buf.st_ctime)
2938 create_time = buf.st_mtime;
2940 create_time = buf.st_ctime;
2942 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
2944 _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
2945 _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
2946 _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
2948 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2949 find_data->nFileSizeHigh = 0;
2950 find_data->nFileSizeLow = 0;
2953 find_data->nFileSizeHigh = buf.st_size >> 32;
2954 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
2957 find_data->dwReserved0 = 0;
2958 find_data->dwReserved1 = 0;
2960 utf8_basename = g_path_get_basename (utf8_filename);
2961 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
2963 if(utf16_basename==NULL) {
2964 g_free (utf8_basename);
2965 g_free (utf8_filename);
2970 /* utf16 is 2 * utf8 */
2973 memset (find_data->cFileName, '\0', (MAX_PATH*2));
2975 /* Truncating a utf16 string like this might leave the last
2978 memcpy (find_data->cFileName, utf16_basename,
2979 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
2981 find_data->cAlternateFileName [0] = 0; /* not used */
2983 g_free (utf8_basename);
2984 g_free (utf8_filename);
2985 g_free (utf16_basename);
2988 thr_ret = _wapi_handle_unlock_handle (handle);
2989 g_assert (thr_ret == 0);
2990 pthread_cleanup_pop (0);
2997 * @wapi_handle: the find handle to close.
2999 * Closes find handle @wapi_handle
3001 * Return value: %TRUE on success, %FALSE otherwise.
3003 gboolean FindClose (gpointer handle)
3005 struct _WapiHandlePrivate_find *find_handle;
3009 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3010 (gpointer *)&find_handle);
3012 g_warning (G_GNUC_PRETTY_FUNCTION
3013 ": error looking up find handle %p", handle);
3014 SetLastError (ERROR_INVALID_HANDLE);
3018 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3020 thr_ret = _wapi_handle_lock_handle (handle);
3021 g_assert (thr_ret == 0);
3023 g_strfreev (find_handle->namelist);
3024 g_free (find_handle->dir_part);
3026 thr_ret = _wapi_handle_unlock_handle (handle);
3027 g_assert (thr_ret == 0);
3028 pthread_cleanup_pop (0);
3030 _wapi_handle_unref (handle);
3037 * @name: a pointer to a NULL-terminated unicode string, that names
3038 * the directory to be created.
3039 * @security: ignored for now
3041 * Creates directory @name
3043 * Return value: %TRUE on success, %FALSE otherwise.
3045 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
3052 utf8_name = mono_unicode_to_external (name);
3053 if (utf8_name == NULL) {
3055 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3061 result = mkdir (utf8_name, 0777);
3068 if (errno == EEXIST) {
3069 result = stat (utf8_name, &buf);
3071 _wapi_set_last_error_from_errno ();
3077 attrs = _wapi_stat_to_file_attributes (&buf);
3078 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
3082 _wapi_set_last_error_from_errno ();
3086 _wapi_set_last_error_from_errno ();
3093 * @name: a pointer to a NULL-terminated unicode string, that names
3094 * the directory to be removed.
3096 * Removes directory @name
3098 * Return value: %TRUE on success, %FALSE otherwise.
3100 gboolean RemoveDirectory (const gunichar2 *name)
3105 utf8_name = mono_unicode_to_external (name);
3106 if (utf8_name == NULL) {
3108 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3114 result = rmdir (utf8_name);
3120 _wapi_set_last_error_from_errno ();
3125 * GetFileAttributes:
3126 * @name: a pointer to a NULL-terminated unicode filename.
3128 * Gets the attributes for @name;
3130 * Return value: %INVALID_FILE_ATTRIBUTES on failure
3132 guint32 GetFileAttributes (const gunichar2 *name)
3138 utf8_name = mono_unicode_to_external (name);
3139 if (utf8_name == NULL) {
3141 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3144 SetLastError (ERROR_INVALID_PARAMETER);
3145 return (INVALID_FILE_ATTRIBUTES);
3148 result = stat (utf8_name, &buf);
3151 _wapi_set_last_error_from_errno ();
3153 return (INVALID_FILE_ATTRIBUTES);
3157 return _wapi_stat_to_file_attributes (&buf);
3161 * GetFileAttributesEx:
3162 * @name: a pointer to a NULL-terminated unicode filename.
3163 * @level: must be GetFileExInfoStandard
3164 * @info: pointer to a WapiFileAttributesData structure
3166 * Gets attributes, size and filetimes for @name;
3168 * Return value: %TRUE on success, %FALSE on failure
3170 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3173 WapiFileAttributesData *data;
3179 if (level != GetFileExInfoStandard) {
3181 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
3187 utf8_name = mono_unicode_to_external (name);
3188 if (utf8_name == NULL) {
3190 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3193 SetLastError (ERROR_INVALID_PARAMETER);
3197 result = stat (utf8_name, &buf);
3201 SetLastError (ERROR_FILE_NOT_FOUND);
3205 /* fill data block */
3207 data = (WapiFileAttributesData *)info;
3209 if (buf.st_mtime < buf.st_ctime)
3210 create_time = buf.st_mtime;
3212 create_time = buf.st_ctime;
3214 data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3216 _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3217 _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3218 _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3220 if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3221 data->nFileSizeHigh = 0;
3222 data->nFileSizeLow = 0;
3225 data->nFileSizeHigh = buf.st_size >> 32;
3226 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3234 * @name: name of file
3235 * @attrs: attributes to set
3237 * Changes the attributes on a named file.
3239 * Return value: %TRUE on success, %FALSE on failure.
3241 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3243 /* FIXME: think of something clever to do on unix */
3249 * Currently we only handle one *internal* case, with a value that is
3250 * not standard: 0x80000000, which means `set executable bit'
3253 utf8_name = mono_unicode_to_external (name);
3254 result = stat (utf8_name, &buf);
3257 SetLastError (ERROR_FILE_NOT_FOUND);
3261 /* Contrary to the documentation, ms allows NORMAL to be
3262 * specified along with other attributes, so dont bother to
3263 * catch that case here.
3265 if (attrs & FILE_ATTRIBUTE_READONLY) {
3266 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3268 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
3271 /* Ignore the other attributes for now */
3273 if (attrs & 0x80000000){
3274 mode_t exec_mask = 0;
3276 if ((buf.st_mode & S_IRUSR) != 0)
3277 exec_mask |= S_IXUSR;
3279 if ((buf.st_mode & S_IRGRP) != 0)
3280 exec_mask |= S_IXGRP;
3282 if ((buf.st_mode & S_IROTH) != 0)
3283 exec_mask |= S_IXOTH;
3285 result = chmod (utf8_name, buf.st_mode | exec_mask);
3287 /* Don't bother to reset executable (might need to change this
3297 * GetCurrentDirectory
3298 * @length: size of the buffer
3299 * @buffer: pointer to buffer that recieves path
3301 * Retrieves the current directory for the current process.
3303 * Return value: number of characters in buffer on success, zero on failure
3305 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3308 gunichar2 *utf16_path;
3312 path = g_get_current_dir ();
3316 utf16_path=mono_unicode_from_external (path, &bytes);
3318 /* if buffer too small, return number of characters required.
3319 * this is plain dumb.
3322 count = (bytes/2)+1;
3323 if (count > length) {
3325 g_free (utf16_path);
3330 /* Add the terminator */
3331 memset (buffer, '\0', bytes+2);
3332 memcpy (buffer, utf16_path, bytes);
3334 g_free (utf16_path);
3341 * SetCurrentDirectory
3342 * @path: path to new directory
3344 * Changes the directory path for the current process.
3346 * Return value: %TRUE on success, %FALSE on failure.
3348 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3353 utf8_path = mono_unicode_to_external (path);
3354 if (chdir (utf8_path) != 0) {
3355 _wapi_set_last_error_from_errno ();
3365 int _wapi_file_handle_to_fd (gpointer handle)
3367 struct _WapiHandlePrivate_file *file_private_handle;
3371 g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
3374 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
3375 (gpointer *)&file_private_handle);
3377 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
3378 (gpointer *)&file_private_handle);
3380 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
3381 (gpointer *)&file_private_handle);
3384 g_message (G_GNUC_PRETTY_FUNCTION
3393 g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
3394 file_private_handle->fd);
3397 return(file_private_handle->fd);
3400 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3401 WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3403 struct _WapiHandle_file *pipe_read_handle;
3404 struct _WapiHandle_file *pipe_write_handle;
3405 struct _WapiHandlePrivate_file *pipe_read_private_handle;
3406 struct _WapiHandlePrivate_file *pipe_write_private_handle;
3407 gpointer read_handle;
3408 gpointer write_handle;
3413 gboolean unref_read = FALSE, unref_write = FALSE;
3414 gboolean cp_ret = FALSE;
3416 mono_once (&io_ops_once, io_ops_init);
3419 g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
3425 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
3429 _wapi_set_last_error_from_errno ();
3433 /* filedes[0] is open for reading, filedes[1] for writing */
3435 read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3436 if(read_handle==_WAPI_HANDLE_INVALID) {
3437 g_warning (G_GNUC_PRETTY_FUNCTION
3438 ": error creating pipe read handle");
3444 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3446 thr_ret = _wapi_handle_lock_handle (read_handle);
3447 g_assert (thr_ret == 0);
3449 ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
3450 (gpointer *)&pipe_read_handle,
3451 (gpointer *)&pipe_read_private_handle);
3453 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3459 write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3460 if(write_handle==_WAPI_HANDLE_INVALID) {
3461 g_warning (G_GNUC_PRETTY_FUNCTION
3462 ": error creating pipe write handle");
3470 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3472 thr_ret = _wapi_handle_lock_handle (write_handle);
3473 g_assert (thr_ret == 0);
3475 ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
3476 (gpointer *)&pipe_write_handle,
3477 (gpointer *)&pipe_write_private_handle);
3479 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3489 pipe_read_private_handle->fd=filedes[0];
3490 pipe_read_private_handle->assigned=TRUE;
3491 pipe_read_handle->fileaccess=GENERIC_READ;
3493 *readpipe=read_handle;
3495 pipe_write_private_handle->fd=filedes[1];
3496 pipe_write_private_handle->assigned=TRUE;
3497 pipe_write_handle->fileaccess=GENERIC_WRITE;
3499 *writepipe=write_handle;
3502 g_message (G_GNUC_PRETTY_FUNCTION
3503 ": Returning pipe: read handle %p, write handle %p",
3504 read_handle, write_handle);
3508 thr_ret =_wapi_handle_unlock_handle (write_handle);
3509 g_assert (thr_ret == 0);
3510 pthread_cleanup_pop (0);
3513 _wapi_handle_unref (write_handle);
3517 thr_ret =_wapi_handle_unlock_handle (read_handle);
3518 g_assert (thr_ret == 0);
3519 pthread_cleanup_pop (0);
3521 /* Must not call _wapi_handle_unref() with the handle already
3525 _wapi_handle_unref (read_handle);
3531 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3533 gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3534 gunichar2 *tmpdir16=NULL;
3539 if(tmpdir[strlen (tmpdir)]!='/') {
3541 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3544 tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3545 if(tmpdir16==NULL) {
3553 g_message (G_GNUC_PRETTY_FUNCTION
3554 ": Size %d smaller than needed (%ld)", len,
3560 /* Add the terminator */
3561 memset (buf, '\0', bytes+2);
3562 memcpy (buf, tmpdir16, bytes);
3568 if(tmpdir16!=NULL) {
3577 _wapi_io_add_callback (gpointer handle,
3578 WapiOverlappedCB callback,
3579 guint64 flags G_GNUC_UNUSED)
3581 struct _WapiHandle_file *file_handle;
3582 struct _WapiHandlePrivate_file *file_private_handle;
3585 gboolean ret = FALSE;
3587 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3588 (gpointer *) &file_handle,
3589 (gpointer *) &file_private_handle);
3592 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
3593 (gpointer *) &file_handle,
3594 (gpointer *) &file_private_handle);
3598 if (ok == FALSE || file_private_handle->async == FALSE) {
3599 SetLastError (ERROR_INVALID_HANDLE);
3603 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3605 thr_ret = _wapi_handle_lock_handle (handle);
3606 g_assert (thr_ret == 0);
3608 if (file_private_handle->callback != NULL) {
3609 SetLastError (ERROR_INVALID_PARAMETER);
3614 file_private_handle->callback = callback;
3617 thr_ret = _wapi_handle_unlock_handle (handle);
3618 g_assert (thr_ret == 0);
3619 pthread_cleanup_pop (0);
3625 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
3628 gunichar2 *ptr, *dir;
3629 glong length, total = 0;
3633 memset (buf, 0, sizeof (gunichar2) * (len + 1));
3638 /* Sigh, mntent and friends don't work well.
3639 * It stops on the first line that doesn't begin with a '/'.
3640 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
3641 fp = fopen ("/etc/mtab", "rt");
3643 fp = fopen ("/etc/mnttab", "rt");
3649 while (fgets (buffer, 512, fp) != NULL) {
3653 splitted = g_strsplit (buffer, " ", 0);
3654 if (!*splitted || !*(splitted + 1))
3657 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
3658 g_strfreev (splitted);
3659 if (total + length + 1 > len) {
3660 return len * 2; /* guess */
3663 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
3665 total += length + 1;
3670 /* Commented out, does not work with my mtab!!! - Gonz */
3671 #ifdef NOTENABLED /* HAVE_MNTENT_H */
3675 gunichar2 *ptr, *dir;
3676 glong len, total = 0;
3679 fp = setmntent ("/etc/mtab", "rt");
3681 fp = setmntent ("/etc/mnttab", "rt");
3687 while ((mnt = getmntent (fp)) != NULL) {
3688 g_print ("GOT %s\n", mnt->mnt_dir);
3689 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
3690 if (total + len + 1 > len) {
3691 return len * 2; /* guess */
3694 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
3705 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
3707 struct flock lock_data;
3710 lock_data.l_type = F_WRLCK;
3711 lock_data.l_whence = SEEK_SET;
3712 lock_data.l_start = offset;
3713 lock_data.l_len = length;
3716 ret = fcntl (fd, F_SETLK, &lock_data);
3718 while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
3721 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
3725 SetLastError (ERROR_LOCK_VIOLATION);
3732 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
3734 struct flock lock_data;
3737 lock_data.l_type = F_UNLCK;
3738 lock_data.l_whence = SEEK_SET;
3739 lock_data.l_start = offset;
3740 lock_data.l_len = length;
3743 ret = fcntl (fd, F_SETLK, &lock_data);
3745 while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
3748 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
3752 SetLastError (ERROR_LOCK_VIOLATION);
3759 gboolean LockFile (gpointer handle, guint32 offset_low, guint32 offset_high,
3760 guint32 length_low, guint32 length_high)
3762 struct _WapiHandle_file *file_handle;
3763 struct _WapiHandlePrivate_file *file_private_handle;
3765 off_t offset, length;
3767 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3768 (gpointer *)&file_handle,
3769 (gpointer *)&file_private_handle);
3771 g_warning (G_GNUC_PRETTY_FUNCTION
3772 ": error looking up file handle %p", handle);
3773 SetLastError (ERROR_INVALID_HANDLE);
3777 if (!(file_handle->fileaccess & GENERIC_READ) &&
3778 !(file_handle->fileaccess & GENERIC_WRITE) &&
3779 !(file_handle->fileaccess & GENERIC_ALL)) {
3781 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, file_handle->fileaccess);
3783 SetLastError (ERROR_ACCESS_DENIED);
3787 #ifdef HAVE_LARGE_FILE_SUPPORT
3788 offset = ((gint64)offset_high << 32) | offset_low;
3789 length = ((gint64)length_high << 32) | length_low;
3792 g_message (G_GNUC_PRETTY_FUNCTION
3793 ": Locking handle %p fd %d, offset %lld, length %lld",
3794 handle, file_private_handle->fd, offset, length);
3797 offset = offset_low;
3798 length = length_low;
3801 g_message (G_GNUC_PRETTY_FUNCTION
3802 ": Locking handle %p fd %d, offset %ld, length %ld",
3803 handle, file_private_handle->fd, offset, length);
3807 return(_wapi_lock_file_region (file_private_handle->fd, offset,
3811 gboolean UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high,
3812 guint32 length_low, guint32 length_high)
3814 struct _WapiHandle_file *file_handle;
3815 struct _WapiHandlePrivate_file *file_private_handle;
3817 off_t offset, length;
3819 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3820 (gpointer *)&file_handle,
3821 (gpointer *)&file_private_handle);
3823 g_warning (G_GNUC_PRETTY_FUNCTION
3824 ": error looking up file handle %p", handle);
3825 SetLastError (ERROR_INVALID_HANDLE);
3829 if (!(file_handle->fileaccess & GENERIC_READ) &&
3830 !(file_handle->fileaccess & GENERIC_WRITE) &&
3831 !(file_handle->fileaccess & GENERIC_ALL)) {
3833 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, file_handle->fileaccess);
3835 SetLastError (ERROR_ACCESS_DENIED);
3839 #ifdef HAVE_LARGE_FILE_SUPPORT
3840 offset = ((gint64)offset_high << 32) | offset_low;
3841 length = ((gint64)length_high << 32) | length_low;
3844 g_message (G_GNUC_PRETTY_FUNCTION
3845 ": Unlocking handle %p fd %d, offset %lld, length %lld",
3846 handle, file_private_handle->fd, offset, length);
3849 offset = offset_low;
3850 length = length_low;
3853 g_message (G_GNUC_PRETTY_FUNCTION
3854 ": Unlocking handle %p fd %d, offset %ld, length %ld",
3855 handle, file_private_handle->fd, offset, length);
3859 return(_wapi_unlock_file_region (file_private_handle->fd, offset,