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 /* Blank out the mapping, to make catching errors easier */
275 _wapi_handle_fd_offset_store (file_private_handle->fd, NULL);
277 close(file_private_handle->fd);
280 static WapiFileType file_getfiletype(void)
282 return(FILE_TYPE_DISK);
288 WapiOverlapped *overlapped;
289 WapiOverlappedCB callback;
292 #define SIGPTR(a) a.SIGVAL_PTR
295 async_notifier (union sigval sig)
297 notifier_data_t *ndata = SIGPTR (sig);
301 error = aio_return (ndata->aio);
303 error = _wapi_get_win32_file_error (error);
310 ndata->callback (error, numbytes, ndata->overlapped);
317 static gboolean file_read(gpointer handle, gpointer buffer,
318 guint32 numbytes, guint32 *bytesread,
319 WapiOverlapped *overlapped)
321 struct _WapiHandle_file *file_handle;
322 struct _WapiHandlePrivate_file *file_private_handle;
326 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
327 (gpointer *)&file_handle,
328 (gpointer *)&file_private_handle);
330 g_warning (G_GNUC_PRETTY_FUNCTION
331 ": error looking up file handle %p", handle);
335 if(bytesread!=NULL) {
339 if(!(file_handle->fileaccess&GENERIC_READ) &&
340 !(file_handle->fileaccess&GENERIC_ALL)) {
342 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);
348 if (file_private_handle->async == FALSE) {
350 ret=read(file_private_handle->fd, buffer, numbytes);
352 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
358 g_message(G_GNUC_PRETTY_FUNCTION
359 ": read of handle %p fd %d error: %s", handle,
360 file_private_handle->fd, strerror(err));
362 SetLastError (_wapi_get_win32_file_error (err));
366 if(bytesread!=NULL) {
374 SetLastError (ERROR_NOT_SUPPORTED);
377 if (overlapped == NULL || file_private_handle->callback == NULL) {
378 SetLastError (ERROR_INVALID_PARAMETER);
383 int fd = file_private_handle->fd;
386 notifier_data_t *ndata;
388 ndata = g_new0 (notifier_data_t, 1);
389 aio = g_new0 (struct aiocb, 1);
390 ndata->overlapped = overlapped;
392 ndata->callback = file_private_handle->callback;
394 aio->aio_fildes = fd;
395 aio->aio_lio_opcode = LIO_READ;
396 aio->aio_nbytes = numbytes;
397 aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
398 aio->aio_buf = buffer;
399 aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
400 aio->aio_sigevent.sigev_notify_function = async_notifier;
401 SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
403 result = aio_read (aio);
405 _wapi_set_last_error_from_errno ();
409 result = aio_error (aio);
411 g_print ("aio_error (read) returned %d for %d\n", result, fd);
414 numbytes = aio_return (aio);
416 g_print ("numbytes %d for %d\n", numbytes, fd);
420 _wapi_set_last_error_from_errno ();
425 *bytesread = numbytes;
432 static gboolean file_write(gpointer handle, gconstpointer buffer,
433 guint32 numbytes, guint32 *byteswritten,
434 WapiOverlapped *overlapped G_GNUC_UNUSED)
436 struct _WapiHandle_file *file_handle;
437 struct _WapiHandlePrivate_file *file_private_handle;
441 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
442 (gpointer *)&file_handle,
443 (gpointer *)&file_private_handle);
445 g_warning (G_GNUC_PRETTY_FUNCTION
446 ": error looking up file handle %p", handle);
450 if(byteswritten!=NULL) {
454 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
455 !(file_handle->fileaccess&GENERIC_ALL)) {
457 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);
463 if (file_private_handle->async == FALSE) {
466 /* Need to lock the region we're about to write to,
467 * because we only do advisory locking on POSIX
470 current_pos = lseek (file_private_handle->fd, (off_t)0,
472 if (current_pos == -1) {
474 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d lseek failed: %s", handle, file_private_handle->fd, strerror (errno));
476 _wapi_set_last_error_from_errno ();
480 if (_wapi_lock_file_region (file_private_handle->fd,
481 current_pos, numbytes) == FALSE) {
482 /* The error has already been set */
487 ret=write(file_private_handle->fd, buffer, numbytes);
489 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
491 _wapi_unlock_file_region (file_private_handle->fd, current_pos,
496 g_message(G_GNUC_PRETTY_FUNCTION
497 ": write of handle %p fd %d error: %s", handle,
498 file_private_handle->fd, strerror(errno));
503 if(byteswritten!=NULL) {
510 SetLastError (ERROR_NOT_SUPPORTED);
513 if (overlapped == NULL || file_private_handle->callback == NULL) {
514 SetLastError (ERROR_INVALID_PARAMETER);
519 int fd = file_private_handle->fd;
522 notifier_data_t *ndata;
524 ndata = g_new0 (notifier_data_t, 1);
525 aio = g_new0 (struct aiocb, 1);
526 ndata->overlapped = overlapped;
528 ndata->callback = file_private_handle->callback;
530 aio->aio_fildes = fd;
531 aio->aio_lio_opcode = LIO_WRITE;
532 aio->aio_nbytes = numbytes;
533 aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
534 aio->aio_buf = (gpointer) buffer;
535 aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
536 aio->aio_sigevent.sigev_notify_function = async_notifier;
537 SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
539 result = aio_write (aio);
541 _wapi_set_last_error_from_errno ();
545 result = aio_error (aio);
547 g_print ("aio_error (write) returned %d for %d\n", result, fd);
550 numbytes = aio_return (aio);
552 g_print ("numbytes %d for %d\n", numbytes, fd);
556 _wapi_set_last_error_from_errno ();
561 *byteswritten = numbytes;
568 static gboolean file_flush(gpointer handle)
570 struct _WapiHandle_file *file_handle;
571 struct _WapiHandlePrivate_file *file_private_handle;
575 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
576 (gpointer *)&file_handle,
577 (gpointer *)&file_private_handle);
579 g_warning (G_GNUC_PRETTY_FUNCTION
580 ": error looking up file handle %p", handle);
584 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
585 !(file_handle->fileaccess&GENERIC_ALL)) {
587 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);
593 ret=fsync(file_private_handle->fd);
596 g_message(G_GNUC_PRETTY_FUNCTION
597 ": write of handle %p fd %d error: %s", handle,
598 file_private_handle->fd, strerror(errno));
607 static guint32 file_seek(gpointer handle, gint32 movedistance,
608 gint32 *highmovedistance, WapiSeekMethod method)
610 struct _WapiHandle_file *file_handle;
611 struct _WapiHandlePrivate_file *file_private_handle;
613 off_t offset, newpos;
617 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
618 (gpointer *)&file_handle,
619 (gpointer *)&file_private_handle);
621 g_warning (G_GNUC_PRETTY_FUNCTION
622 ": error looking up file handle %p", handle);
623 SetLastError (ERROR_INVALID_HANDLE);
624 return(INVALID_SET_FILE_POINTER);
627 if(!(file_handle->fileaccess&GENERIC_READ) &&
628 !(file_handle->fileaccess&GENERIC_WRITE) &&
629 !(file_handle->fileaccess&GENERIC_ALL)) {
631 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);
634 SetLastError (ERROR_ACCESS_DENIED);
635 return(INVALID_SET_FILE_POINTER);
650 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
654 SetLastError (ERROR_INVALID_PARAMETER);
655 return(INVALID_SET_FILE_POINTER);
658 #ifdef HAVE_LARGE_FILE_SUPPORT
659 if(highmovedistance==NULL) {
662 g_message(G_GNUC_PRETTY_FUNCTION
663 ": setting offset to %lld (low %d)", offset,
667 offset=((gint64) *highmovedistance << 32) | (unsigned long)movedistance;
670 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);
678 #ifdef HAVE_LARGE_FILE_SUPPORT
679 g_message(G_GNUC_PRETTY_FUNCTION
680 ": moving handle %p fd %d by %lld bytes from %d", handle,
681 file_private_handle->fd, offset, whence);
683 g_message(G_GNUC_PRETTY_FUNCTION
684 ": moving handle %p fd %d by %ld bytes from %d", handle,
685 file_private_handle->fd, offset, whence);
689 newpos=lseek(file_private_handle->fd, offset, whence);
692 g_message(G_GNUC_PRETTY_FUNCTION
693 ": lseek on handle %p fd %d returned error %s",
694 handle, file_private_handle->fd, strerror(errno));
697 _wapi_set_last_error_from_errno ();
698 return(INVALID_SET_FILE_POINTER);
702 #ifdef HAVE_LARGE_FILE_SUPPORT
703 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
705 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
709 #ifdef HAVE_LARGE_FILE_SUPPORT
710 ret=newpos & 0xFFFFFFFF;
711 if(highmovedistance!=NULL) {
712 *highmovedistance=newpos>>32;
716 if(highmovedistance!=NULL) {
717 /* Accurate, but potentially dodgy :-) */
723 g_message(G_GNUC_PRETTY_FUNCTION
724 ": move of handle %p fd %d returning %d/%d", handle,
725 file_private_handle->fd, ret,
726 highmovedistance==NULL?0:*highmovedistance);
732 static gboolean file_setendoffile(gpointer handle)
734 struct _WapiHandle_file *file_handle;
735 struct _WapiHandlePrivate_file *file_private_handle;
741 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
742 (gpointer *)&file_handle,
743 (gpointer *)&file_private_handle);
745 g_warning (G_GNUC_PRETTY_FUNCTION
746 ": error looking up file handle %p", handle);
750 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
751 !(file_handle->fileaccess&GENERIC_ALL)) {
753 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);
759 /* Find the current file position, and the file length. If
760 * the file position is greater than the length, write to
761 * extend the file with a hole. If the file position is less
762 * than the length, truncate the file.
765 ret=fstat(file_private_handle->fd, &statbuf);
768 g_message(G_GNUC_PRETTY_FUNCTION
769 ": handle %p fd %d fstat failed: %s", handle,
770 file_private_handle->fd, strerror(errno));
775 size=statbuf.st_size;
777 pos=lseek(file_private_handle->fd, (off_t)0, SEEK_CUR);
780 g_message(G_GNUC_PRETTY_FUNCTION
781 ": handle %p fd %d lseek failed: %s", handle,
782 file_private_handle->fd, strerror(errno));
791 ret=write(file_private_handle->fd, "", 1);
793 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
797 g_message(G_GNUC_PRETTY_FUNCTION
798 ": handle %p fd %d extend write failed: %s",
799 handle, file_private_handle->fd,
807 /* always truncate, because the extend write() adds an extra
808 * byte to the end of the file
811 ret=ftruncate(file_private_handle->fd, pos);
813 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
817 g_message(G_GNUC_PRETTY_FUNCTION
818 ": handle %p fd %d ftruncate failed: %s", handle,
819 file_private_handle->fd, strerror(errno));
828 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
830 struct _WapiHandle_file *file_handle;
831 struct _WapiHandlePrivate_file *file_private_handle;
837 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
838 (gpointer *)&file_handle,
839 (gpointer *)&file_private_handle);
841 g_warning (G_GNUC_PRETTY_FUNCTION
842 ": error looking up file handle %p", handle);
843 return(INVALID_FILE_SIZE);
846 if(!(file_handle->fileaccess&GENERIC_READ) &&
847 !(file_handle->fileaccess&GENERIC_WRITE) &&
848 !(file_handle->fileaccess&GENERIC_ALL)) {
850 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);
853 return(INVALID_FILE_SIZE);
856 ret=fstat(file_private_handle->fd, &statbuf);
859 g_message(G_GNUC_PRETTY_FUNCTION
860 ": handle %p fd %d fstat failed: %s", handle,
861 file_private_handle->fd, strerror(errno));
864 return(INVALID_FILE_SIZE);
867 #ifdef HAVE_LARGE_FILE_SUPPORT
868 size=statbuf.st_size & 0xFFFFFFFF;
870 *highsize=statbuf.st_size>>32;
874 /* Accurate, but potentially dodgy :-) */
877 size=statbuf.st_size;
881 g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
888 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
889 WapiFileTime *last_access,
890 WapiFileTime *last_write)
892 struct _WapiHandle_file *file_handle;
893 struct _WapiHandlePrivate_file *file_private_handle;
896 guint64 create_ticks, access_ticks, write_ticks;
899 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
900 (gpointer *)&file_handle,
901 (gpointer *)&file_private_handle);
903 g_warning (G_GNUC_PRETTY_FUNCTION
904 ": error looking up file handle %p", handle);
908 if(!(file_handle->fileaccess&GENERIC_READ) &&
909 !(file_handle->fileaccess&GENERIC_ALL)) {
911 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);
917 ret=fstat(file_private_handle->fd, &statbuf);
920 g_message(G_GNUC_PRETTY_FUNCTION
921 ": handle %p fd %d fstat failed: %s", handle,
922 file_private_handle->fd, strerror(errno));
929 g_message(G_GNUC_PRETTY_FUNCTION
930 ": atime: %ld ctime: %ld mtime: %ld",
931 statbuf.st_atime, statbuf.st_ctime,
935 /* Try and guess a meaningful create time by using the older
938 /* The magic constant comes from msdn documentation
939 * "Converting a time_t Value to a File Time"
941 if(statbuf.st_atime < statbuf.st_ctime) {
942 create_ticks=((guint64)statbuf.st_atime*10000000)
943 + 116444736000000000ULL;
945 create_ticks=((guint64)statbuf.st_ctime*10000000)
946 + 116444736000000000ULL;
949 access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
950 write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
953 g_message(G_GNUC_PRETTY_FUNCTION
954 ": aticks: %llu cticks: %llu wticks: %llu",
955 access_ticks, create_ticks, write_ticks);
958 if(create_time!=NULL) {
959 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
960 create_time->dwHighDateTime = create_ticks >> 32;
963 if(last_access!=NULL) {
964 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
965 last_access->dwHighDateTime = access_ticks >> 32;
968 if(last_write!=NULL) {
969 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
970 last_write->dwHighDateTime = write_ticks >> 32;
976 static gboolean file_setfiletime(gpointer handle,
977 const WapiFileTime *create_time G_GNUC_UNUSED,
978 const WapiFileTime *last_access,
979 const WapiFileTime *last_write)
981 struct _WapiHandle_file *file_handle;
982 struct _WapiHandlePrivate_file *file_private_handle;
985 struct utimbuf utbuf;
987 guint64 access_ticks, write_ticks;
990 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
991 (gpointer *)&file_handle,
992 (gpointer *)&file_private_handle);
994 g_warning (G_GNUC_PRETTY_FUNCTION
995 ": error looking up file handle %p", handle);
996 SetLastError (ERROR_INVALID_HANDLE);
1000 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
1001 !(file_handle->fileaccess&GENERIC_ALL)) {
1003 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);
1006 SetLastError (ERROR_ACCESS_DENIED);
1010 if(file_handle->filename==0) {
1012 g_message(G_GNUC_PRETTY_FUNCTION
1013 ": handle %p fd %d unknown filename", handle,
1014 file_private_handle->fd);
1017 SetLastError (ERROR_INVALID_HANDLE);
1021 /* Get the current times, so we can put the same times back in
1022 * the event that one of the FileTime structs is NULL
1024 ret=fstat(file_private_handle->fd, &statbuf);
1027 g_message(G_GNUC_PRETTY_FUNCTION
1028 ": handle %p fd %d fstat failed: %s", handle,
1029 file_private_handle->fd, strerror(errno));
1032 SetLastError (ERROR_INVALID_PARAMETER);
1036 if(last_access!=NULL) {
1037 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
1038 last_access->dwLowDateTime;
1039 /* This is (time_t)0. We can actually go to INT_MIN,
1040 * but this will do for now.
1042 if (access_ticks < 116444736000000000ULL) {
1044 g_message (G_GNUC_PRETTY_FUNCTION
1045 ": attempt to set access time too early");
1047 SetLastError (ERROR_INVALID_PARAMETER);
1051 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1053 utbuf.actime=statbuf.st_atime;
1056 if(last_write!=NULL) {
1057 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
1058 last_write->dwLowDateTime;
1059 /* This is (time_t)0. We can actually go to INT_MIN,
1060 * but this will do for now.
1062 if (write_ticks < 116444736000000000ULL) {
1064 g_message (G_GNUC_PRETTY_FUNCTION
1065 ": attempt to set write time too early");
1067 SetLastError (ERROR_INVALID_PARAMETER);
1071 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1073 utbuf.modtime=statbuf.st_mtime;
1077 g_message(G_GNUC_PRETTY_FUNCTION
1078 ": setting handle %p access %ld write %ld", handle,
1079 utbuf.actime, utbuf.modtime);
1082 name=_wapi_handle_scratch_lookup (file_handle->filename);
1084 ret=utime(name, &utbuf);
1087 g_message(G_GNUC_PRETTY_FUNCTION
1088 ": handle %p [%s] fd %d utime failed: %s", handle,
1089 name, file_private_handle->fd, strerror(errno));
1093 SetLastError (ERROR_INVALID_PARAMETER);
1102 static void console_close_shared (gpointer handle)
1104 struct _WapiHandle_file *console_handle;
1107 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1108 (gpointer *)&console_handle, NULL);
1110 g_warning (G_GNUC_PRETTY_FUNCTION
1111 ": error looking up console handle %p", handle);
1116 g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
1119 if(console_handle->filename!=0) {
1120 _wapi_handle_scratch_delete (console_handle->filename);
1121 console_handle->filename=0;
1123 if(console_handle->security_attributes!=0) {
1124 _wapi_handle_scratch_delete (console_handle->security_attributes);
1125 console_handle->security_attributes=0;
1129 static void console_close_private (gpointer handle)
1131 struct _WapiHandlePrivate_file *console_private_handle;
1134 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
1135 (gpointer *)&console_private_handle);
1137 g_warning (G_GNUC_PRETTY_FUNCTION
1138 ": error looking up console handle %p", handle);
1143 g_message(G_GNUC_PRETTY_FUNCTION
1144 ": closing console handle %p with fd %d", handle,
1145 console_private_handle->fd);
1148 /* Blank out the mapping, to make catching errors easier */
1149 _wapi_handle_fd_offset_store (console_private_handle->fd, NULL);
1151 close(console_private_handle->fd);
1154 static WapiFileType console_getfiletype(void)
1156 return(FILE_TYPE_CHAR);
1159 static gboolean console_read(gpointer handle, gpointer buffer,
1160 guint32 numbytes, guint32 *bytesread,
1161 WapiOverlapped *overlapped G_GNUC_UNUSED)
1163 struct _WapiHandle_file *console_handle;
1164 struct _WapiHandlePrivate_file *console_private_handle;
1168 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1169 (gpointer *)&console_handle,
1170 (gpointer *)&console_private_handle);
1172 g_warning (G_GNUC_PRETTY_FUNCTION
1173 ": error looking up console handle %p", handle);
1177 if(bytesread!=NULL) {
1181 if(!(console_handle->fileaccess&GENERIC_READ) &&
1182 !(console_handle->fileaccess&GENERIC_ALL)) {
1184 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);
1191 ret=read(console_private_handle->fd, buffer, numbytes);
1193 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1197 g_message(G_GNUC_PRETTY_FUNCTION
1198 ": read of handle %p fd %d error: %s", handle,
1199 console_private_handle->fd, strerror(errno));
1205 if(bytesread!=NULL) {
1212 static gboolean console_write(gpointer handle, gconstpointer buffer,
1213 guint32 numbytes, guint32 *byteswritten,
1214 WapiOverlapped *overlapped G_GNUC_UNUSED)
1216 struct _WapiHandle_file *console_handle;
1217 struct _WapiHandlePrivate_file *console_private_handle;
1221 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1222 (gpointer *)&console_handle,
1223 (gpointer *)&console_private_handle);
1225 g_warning (G_GNUC_PRETTY_FUNCTION
1226 ": error looking up console handle %p", handle);
1230 if(byteswritten!=NULL) {
1234 if(!(console_handle->fileaccess&GENERIC_WRITE) &&
1235 !(console_handle->fileaccess&GENERIC_ALL)) {
1237 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);
1244 ret=write(console_private_handle->fd, buffer, numbytes);
1246 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1250 g_message(G_GNUC_PRETTY_FUNCTION
1251 ": write of handle %p fd %d error: %s", handle,
1252 console_private_handle->fd, strerror(errno));
1257 if(byteswritten!=NULL) {
1264 static void pipe_close_shared (gpointer handle)
1266 struct _WapiHandle_file *pipe_handle;
1269 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1270 (gpointer *)&pipe_handle, NULL);
1272 g_warning (G_GNUC_PRETTY_FUNCTION
1273 ": error looking up pipe handle %p", handle);
1278 g_message(G_GNUC_PRETTY_FUNCTION ": closing pipe handle %p", handle);
1281 if(pipe_handle->filename!=0) {
1282 _wapi_handle_scratch_delete (pipe_handle->filename);
1283 pipe_handle->filename=0;
1285 if(pipe_handle->security_attributes!=0) {
1286 _wapi_handle_scratch_delete (pipe_handle->security_attributes);
1287 pipe_handle->security_attributes=0;
1291 static void pipe_close_private (gpointer handle)
1293 struct _WapiHandlePrivate_file *pipe_private_handle;
1296 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
1297 (gpointer *)&pipe_private_handle);
1299 g_warning (G_GNUC_PRETTY_FUNCTION
1300 ": error looking up pipe handle %p", handle);
1305 g_message(G_GNUC_PRETTY_FUNCTION
1306 ": closing pipe handle %p with fd %d", handle,
1307 pipe_private_handle->fd);
1310 /* Blank out the mapping, to make catching errors easier */
1311 _wapi_handle_fd_offset_store (pipe_private_handle->fd, NULL);
1313 close(pipe_private_handle->fd);
1316 static WapiFileType pipe_getfiletype(void)
1318 return(FILE_TYPE_PIPE);
1321 static gboolean pipe_read (gpointer handle, gpointer buffer,
1322 guint32 numbytes, guint32 *bytesread,
1323 WapiOverlapped *overlapped G_GNUC_UNUSED)
1325 struct _WapiHandle_file *pipe_handle;
1326 struct _WapiHandlePrivate_file *pipe_private_handle;
1330 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1331 (gpointer *)&pipe_handle,
1332 (gpointer *)&pipe_private_handle);
1334 g_warning (G_GNUC_PRETTY_FUNCTION
1335 ": error looking up pipe handle %p", handle);
1339 if(bytesread!=NULL) {
1343 if(!(pipe_handle->fileaccess&GENERIC_READ) &&
1344 !(pipe_handle->fileaccess&GENERIC_ALL)) {
1346 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);
1353 g_message (G_GNUC_PRETTY_FUNCTION
1354 ": reading up to %d bytes from pipe %p (fd %d)", numbytes,
1355 handle, pipe_private_handle->fd);
1359 ret=read(pipe_private_handle->fd, buffer, numbytes);
1361 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1365 g_message(G_GNUC_PRETTY_FUNCTION
1366 ": read of handle %p fd %d error: %s", handle,
1367 pipe_private_handle->fd, strerror(errno));
1374 g_message (G_GNUC_PRETTY_FUNCTION ": read %d bytes from pipe", ret);
1377 if(bytesread!=NULL) {
1384 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1385 guint32 numbytes, guint32 *byteswritten,
1386 WapiOverlapped *overlapped G_GNUC_UNUSED)
1388 struct _WapiHandle_file *pipe_handle;
1389 struct _WapiHandlePrivate_file *pipe_private_handle;
1393 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1394 (gpointer *)&pipe_handle,
1395 (gpointer *)&pipe_private_handle);
1397 g_warning (G_GNUC_PRETTY_FUNCTION
1398 ": error looking up pipe handle %p", handle);
1402 if(byteswritten!=NULL) {
1406 if(!(pipe_handle->fileaccess&GENERIC_WRITE) &&
1407 !(pipe_handle->fileaccess&GENERIC_ALL)) {
1409 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);
1416 g_message (G_GNUC_PRETTY_FUNCTION
1417 ": writing up to %d bytes to pipe %p (fd %d)", numbytes,
1418 handle, pipe_private_handle->fd);
1422 ret=write(pipe_private_handle->fd, buffer, numbytes);
1424 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1428 g_message(G_GNUC_PRETTY_FUNCTION
1429 ": write of handle %p fd %d error: %s", handle,
1430 pipe_private_handle->fd, strerror(errno));
1435 if(byteswritten!=NULL) {
1442 static int convert_flags(guint32 fileaccess, guint32 createmode)
1446 switch(fileaccess) {
1453 case GENERIC_READ|GENERIC_WRITE:
1458 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
1464 switch(createmode) {
1466 flags|=O_CREAT|O_EXCL;
1469 flags|=O_CREAT|O_TRUNC;
1476 case TRUNCATE_EXISTING:
1481 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
1490 static guint32 convert_from_flags(int flags)
1492 guint32 fileaccess=0;
1495 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1498 if((flags & O_ACCMODE) == O_RDONLY) {
1499 fileaccess=GENERIC_READ;
1500 } else if ((flags & O_ACCMODE) == O_WRONLY) {
1501 fileaccess=GENERIC_WRITE;
1502 } else if ((flags & O_ACCMODE) == O_RDWR) {
1503 fileaccess=GENERIC_READ|GENERIC_WRITE;
1506 g_message(G_GNUC_PRETTY_FUNCTION
1507 ": Can't figure out flags 0x%x", flags);
1511 /* Maybe sort out create mode too */
1516 static mode_t convert_perms(guint32 sharemode)
1520 if(sharemode&FILE_SHARE_READ) {
1523 if(sharemode&FILE_SHARE_WRITE) {
1533 * @name: a pointer to a NULL-terminated unicode string, that names
1534 * the file or other object to create.
1535 * @fileaccess: specifies the file access mode
1536 * @sharemode: whether the file should be shared. This parameter is
1537 * currently ignored.
1538 * @security: Ignored for now.
1539 * @createmode: specifies whether to create a new file, whether to
1540 * overwrite an existing file, whether to truncate the file, etc.
1541 * @attrs: specifies file attributes and flags. On win32 attributes
1542 * are characteristics of the file, not the handle, and are ignored
1543 * when an existing file is opened. Flags give the library hints on
1544 * how to process a file to optimise performance.
1545 * @template: the handle of an open %GENERIC_READ file that specifies
1546 * attributes to apply to a newly created file, ignoring @attrs.
1547 * Normally this parameter is NULL. This parameter is ignored when an
1548 * existing file is opened.
1550 * Creates a new file handle. This only applies to normal files:
1551 * pipes are handled by CreatePipe(), and console handles are created
1552 * with GetStdHandle().
1554 * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1556 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1557 guint32 sharemode, WapiSecurityAttributes *security,
1558 guint32 createmode, guint32 attrs,
1559 gpointer template G_GNUC_UNUSED)
1561 struct _WapiHandle_file *file_handle;
1562 struct _WapiHandlePrivate_file *file_private_handle;
1565 int flags=convert_flags(fileaccess, createmode);
1566 /*mode_t perms=convert_perms(sharemode);*/
1567 /* we don't use sharemode, because that relates to sharing of the file
1568 * when the file is open and is already handled by other code, perms instead
1569 * are the on-disk permissions and this is a sane default.
1575 gpointer cf_ret = INVALID_HANDLE_VALUE;
1576 struct stat statbuf;
1577 gboolean file_already_shared;
1578 guint32 file_existing_share, file_existing_access;
1580 mono_once (&io_ops_once, io_ops_init);
1584 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1587 return(INVALID_HANDLE_VALUE);
1590 filename=mono_unicode_to_external (name);
1591 if(filename==NULL) {
1593 g_message(G_GNUC_PRETTY_FUNCTION
1594 ": unicode conversion returned NULL");
1597 return(INVALID_HANDLE_VALUE);
1601 g_message (G_GNUC_PRETTY_FUNCTION ": Opening %s with share 0x%x and access 0x%x", filename, sharemode, fileaccess);
1604 fd = open(filename, flags, perms);
1606 /* If we were trying to open a directory with write permissions
1607 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1608 * EISDIR. However, this is a bit bogus because calls to
1609 * manipulate the directory (e.g. SetFileTime) will still work on
1610 * the directory because they use other API calls
1611 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1612 * to open the directory again without write permission.
1614 if (fd == -1 && errno == EISDIR)
1616 /* Try again but don't try to make it writable */
1617 fd = open(filename, flags & ~(O_RDWR|O_WRONLY), perms);
1622 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
1623 filename, strerror(errno));
1625 _wapi_set_last_error_from_errno ();
1628 return(INVALID_HANDLE_VALUE);
1631 if (fd >= _wapi_fd_offset_table_size) {
1633 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
1636 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1641 return(INVALID_HANDLE_VALUE);
1644 ret = fstat (fd, &statbuf);
1647 g_message (G_GNUC_PRETTY_FUNCTION ": fstat error of file %s: %s", filename, strerror (errno));
1649 _wapi_set_last_error_from_errno ();
1653 return(INVALID_HANDLE_VALUE);
1656 file_already_shared = _wapi_handle_get_or_set_share (statbuf.st_dev, statbuf.st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access);
1658 if (file_already_shared) {
1659 if (file_existing_share == 0) {
1660 /* Quick and easy, no possibility to share */
1662 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing = NONE", fileaccess);
1664 SetLastError (ERROR_SHARING_VIOLATION);
1668 return(INVALID_HANDLE_VALUE);
1671 if (((file_existing_share == FILE_SHARE_READ) &&
1672 (fileaccess != GENERIC_READ)) ||
1673 ((file_existing_share == FILE_SHARE_WRITE) &&
1674 (fileaccess != GENERIC_WRITE))) {
1675 /* New access mode doesn't match up */
1677 g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", fileaccess, file_existing_share);
1679 SetLastError (ERROR_SHARING_VIOLATION);
1683 return(INVALID_HANDLE_VALUE);
1686 if (((file_existing_access & GENERIC_READ) &&
1687 !(sharemode & FILE_SHARE_READ)) ||
1688 ((file_existing_access & GENERIC_WRITE) &&
1689 !(sharemode & FILE_SHARE_WRITE))) {
1690 /* New share mode doesn't match up */
1692 g_message (G_GNUC_PRETTY_FUNCTION ": Access mode prevents open: requested share: 0x%x, file has access: 0x%x", sharemode, file_existing_access);
1694 SetLastError (ERROR_SHARING_VIOLATION);
1698 return(INVALID_HANDLE_VALUE);
1702 g_message (G_GNUC_PRETTY_FUNCTION ": New file!");
1706 handle=_wapi_handle_new (WAPI_HANDLE_FILE);
1707 if(handle==_WAPI_HANDLE_INVALID) {
1708 g_warning (G_GNUC_PRETTY_FUNCTION
1709 ": error creating file handle");
1713 return(INVALID_HANDLE_VALUE);
1716 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
1718 thr_ret = _wapi_handle_lock_handle (handle);
1719 g_assert (thr_ret == 0);
1721 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1722 (gpointer *)&file_handle,
1723 (gpointer *)&file_private_handle);
1725 g_warning (G_GNUC_PRETTY_FUNCTION
1726 ": error looking up file handle %p", handle);
1731 _wapi_handle_fd_offset_store (fd, handle);
1732 cf_ret = GINT_TO_POINTER (fd);
1734 file_private_handle->fd=fd;
1735 file_private_handle->assigned=TRUE;
1736 file_private_handle->async = ((attrs & FILE_FLAG_OVERLAPPED) != 0);
1737 file_handle->filename=_wapi_handle_scratch_store (filename,
1739 if(security!=NULL) {
1740 file_handle->security_attributes=_wapi_handle_scratch_store (
1741 security, sizeof(WapiSecurityAttributes));
1744 file_handle->fileaccess=fileaccess;
1745 file_handle->sharemode=sharemode;
1746 file_handle->attrs=attrs;
1747 file_handle->device = statbuf.st_dev;
1748 file_handle->inode = statbuf.st_ino;
1751 g_message(G_GNUC_PRETTY_FUNCTION
1752 ": returning handle %p with fd %d", handle,
1753 file_private_handle->fd);
1757 thr_ret = _wapi_handle_unlock_handle (handle);
1758 g_assert (thr_ret == 0);
1759 pthread_cleanup_pop (0);
1768 * @name: a pointer to a NULL-terminated unicode string, that names
1769 * the file to be deleted.
1771 * Deletes file @name.
1773 * Return value: %TRUE on success, %FALSE otherwise.
1775 gboolean DeleteFile(const gunichar2 *name)
1782 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1788 filename=mono_unicode_to_external(name);
1789 if(filename==NULL) {
1791 g_message(G_GNUC_PRETTY_FUNCTION
1792 ": unicode conversion returned NULL");
1798 ret=unlink(filename);
1806 _wapi_set_last_error_from_errno ();
1812 * @name: a pointer to a NULL-terminated unicode string, that names
1813 * the file to be moved.
1814 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1815 * new name for the file.
1817 * Renames file @name to @dest_name
1819 * Return value: %TRUE on success, %FALSE otherwise.
1821 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1823 gchar *utf8_name, *utf8_dest_name;
1826 utf8_name = mono_unicode_to_external (name);
1827 if (utf8_name == NULL) {
1829 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1835 utf8_dest_name = mono_unicode_to_external (dest_name);
1836 if (utf8_dest_name == NULL) {
1838 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1845 result = rename (utf8_name, utf8_dest_name);
1847 g_free (utf8_dest_name);
1849 if (result != 0 && errno == EXDEV) {
1850 /* Try a copy to the new location, and delete the source */
1851 if (CopyFile (name, dest_name, TRUE)==FALSE) {
1855 return(DeleteFile (name));
1864 SetLastError (ERROR_ALREADY_EXISTS);
1868 _wapi_set_last_error_from_errno ();
1877 * @name: a pointer to a NULL-terminated unicode string, that names
1878 * the file to be copied.
1879 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1880 * new name for the file.
1881 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1883 * Copies file @name to @dest_name
1885 * Return value: %TRUE on success, %FALSE otherwise.
1887 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
1888 gboolean fail_if_exists)
1890 gchar *utf8_src, *utf8_dest;
1891 int src_fd, dest_fd;
1897 utf8_src = mono_unicode_to_external (name);
1898 if (utf8_src == NULL) {
1900 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of source returned NULL");
1903 SetLastError (ERROR_INVALID_PARAMETER);
1907 utf8_dest = mono_unicode_to_external (dest_name);
1908 if (utf8_dest == NULL) {
1910 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of dest returned NULL");
1913 SetLastError (ERROR_INVALID_PARAMETER);
1920 src_fd = open (utf8_src, O_RDONLY);
1922 _wapi_set_last_error_from_errno ();
1930 if (fstat (src_fd, &st) < 0) {
1931 _wapi_set_last_error_from_errno ();
1940 if (fail_if_exists) {
1941 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
1943 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
1945 /* O_TRUNC might cause a fail if the file
1948 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
1953 _wapi_set_last_error_from_errno ();
1962 buf_size = st.st_blksize;
1963 buf = (char *) alloca (buf_size);
1966 remain = read (src_fd, buf, buf_size);
1969 if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
1973 _wapi_set_last_error_from_errno ();
1987 while (remain > 0) {
1988 if ((n = write (dest_fd, buf, remain)) < 0) {
1989 if (errno == EINTR && !_wapi_thread_cur_apc_pending())
1992 _wapi_set_last_error_from_errno ();
1994 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
2017 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
2018 static gpointer stdin_handle=INVALID_HANDLE_VALUE;
2019 static gpointer stdout_handle=INVALID_HANDLE_VALUE;
2020 static gpointer stderr_handle=INVALID_HANDLE_VALUE;
2022 static gpointer stdhandle_create (int fd, const guchar *name)
2024 struct _WapiHandle_file *file_handle;
2025 struct _WapiHandlePrivate_file *file_private_handle;
2027 gpointer handle, ret = INVALID_HANDLE_VALUE;
2032 g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
2036 /* Check if fd is valid */
2038 flags=fcntl(fd, F_GETFL);
2040 while (flags==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
2043 /* Invalid fd. Not really much point checking for EBADF
2047 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
2048 fd, strerror(errno));
2051 return(INVALID_HANDLE_VALUE);
2054 handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
2055 if(handle==_WAPI_HANDLE_INVALID) {
2056 g_warning (G_GNUC_PRETTY_FUNCTION
2057 ": error creating file handle");
2058 return(INVALID_HANDLE_VALUE);
2061 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2063 thr_ret = _wapi_handle_lock_handle (handle);
2064 g_assert (thr_ret == 0);
2066 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2067 (gpointer *)&file_handle,
2068 (gpointer *)&file_private_handle);
2070 g_warning (G_GNUC_PRETTY_FUNCTION
2071 ": error looking up console handle %p", handle);
2075 /* We know this is fd 0, 1 or 2 */
2076 _wapi_handle_fd_offset_store (fd, handle);
2077 ret = GINT_TO_POINTER (fd);
2079 file_private_handle->fd=fd;
2080 file_private_handle->assigned=TRUE;
2081 file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
2082 /* some default security attributes might be needed */
2083 file_handle->security_attributes=0;
2084 file_handle->fileaccess=convert_from_flags(flags);
2085 file_handle->sharemode=0;
2086 file_handle->attrs=0;
2089 g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
2090 handle, file_private_handle->fd);
2094 thr_ret = _wapi_handle_unlock_handle (handle);
2095 g_assert (thr_ret == 0);
2096 pthread_cleanup_pop (0);
2101 static void stdhandle_init (void)
2103 stdin_handle=stdhandle_create (0, "<stdin>");
2104 stdout_handle=stdhandle_create (1, "<stdout>");
2105 stderr_handle=stdhandle_create (2, "<stderr>");
2110 * @stdhandle: specifies the file descriptor
2112 * Returns a handle for stdin, stdout, or stderr. Always returns the
2113 * same handle for the same @stdhandle.
2115 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2118 gpointer GetStdHandle(WapiStdHandle stdhandle)
2122 mono_once (&io_ops_once, io_ops_init);
2123 mono_once (&stdhandle_once, stdhandle_init);
2126 case STD_INPUT_HANDLE:
2127 handle=stdin_handle;
2130 case STD_OUTPUT_HANDLE:
2131 handle=stdout_handle;
2134 case STD_ERROR_HANDLE:
2135 handle=stderr_handle;
2140 g_message(G_GNUC_PRETTY_FUNCTION
2141 ": unknown standard handle type");
2144 return(INVALID_HANDLE_VALUE);
2147 if (handle == INVALID_HANDLE_VALUE) {
2148 SetLastError (ERROR_NO_MORE_FILES);
2149 return(INVALID_HANDLE_VALUE);
2152 /* Add a reference to this handle */
2153 _wapi_handle_ref (_wapi_handle_fd_offset_to_handle (handle));
2160 * @handle: The file handle to read from. The handle must have
2161 * %GENERIC_READ access.
2162 * @buffer: The buffer to store read data in
2163 * @numbytes: The maximum number of bytes to read
2164 * @bytesread: The actual number of bytes read is stored here. This
2165 * value can be zero if the handle is positioned at the end of the
2167 * @overlapped: points to a required %WapiOverlapped structure if
2168 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2171 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2172 * function reads up to @numbytes bytes from the file from the current
2173 * file position, and stores them in @buffer. If there are not enough
2174 * bytes left in the file, just the amount available will be read.
2175 * The actual number of bytes read is stored in @bytesread.
2177 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2178 * file position is ignored and the read position is taken from data
2179 * in the @overlapped structure.
2181 * Return value: %TRUE if the read succeeds (even if no bytes were
2182 * read due to an attempt to read past the end of the file), %FALSE on
2185 gboolean ReadFile(gpointer fd_handle, gpointer buffer, guint32 numbytes,
2186 guint32 *bytesread, WapiOverlapped *overlapped)
2188 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2189 WapiHandleType type;
2191 if (handle == NULL) {
2192 SetLastError (ERROR_INVALID_HANDLE);
2196 type = _wapi_handle_type (handle);
2198 if(io_ops[type].readfile==NULL) {
2199 SetLastError (ERROR_INVALID_HANDLE);
2203 return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2209 * @handle: The file handle to write to. The handle must have
2210 * %GENERIC_WRITE access.
2211 * @buffer: The buffer to read data from.
2212 * @numbytes: The maximum number of bytes to write.
2213 * @byteswritten: The actual number of bytes written is stored here.
2214 * If the handle is positioned at the file end, the length of the file
2215 * is extended. This parameter may be %NULL.
2216 * @overlapped: points to a required %WapiOverlapped structure if
2217 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2220 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2221 * function writes up to @numbytes bytes from @buffer to the file at
2222 * the current file position. If @handle is positioned at the end of
2223 * the file, the file is extended. The actual number of bytes written
2224 * is stored in @byteswritten.
2226 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2227 * file position is ignored and the write position is taken from data
2228 * in the @overlapped structure.
2230 * Return value: %TRUE if the write succeeds, %FALSE on error.
2232 gboolean WriteFile(gpointer fd_handle, gconstpointer buffer, guint32 numbytes,
2233 guint32 *byteswritten, WapiOverlapped *overlapped)
2235 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2236 WapiHandleType type;
2238 if (handle == NULL) {
2239 SetLastError (ERROR_INVALID_HANDLE);
2243 type = _wapi_handle_type (handle);
2245 if(io_ops[type].writefile==NULL) {
2246 SetLastError (ERROR_INVALID_HANDLE);
2250 return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2256 * @handle: Handle to open file. The handle must have
2257 * %GENERIC_WRITE access.
2259 * Flushes buffers of the file and causes all unwritten data to
2262 * Return value: %TRUE on success, %FALSE otherwise.
2264 gboolean FlushFileBuffers(gpointer fd_handle)
2266 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2267 WapiHandleType type;
2269 if (handle == NULL) {
2270 SetLastError (ERROR_INVALID_HANDLE);
2274 type = _wapi_handle_type (handle);
2276 if(io_ops[type].flushfile==NULL) {
2277 SetLastError (ERROR_INVALID_HANDLE);
2281 return(io_ops[type].flushfile (handle));
2286 * @handle: The file handle to set. The handle must have
2287 * %GENERIC_WRITE access.
2289 * Moves the end-of-file position to the current position of the file
2290 * pointer. This function is used to truncate or extend a file.
2292 * Return value: %TRUE on success, %FALSE otherwise.
2294 gboolean SetEndOfFile(gpointer fd_handle)
2296 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2297 WapiHandleType type;
2299 if (handle == NULL) {
2300 SetLastError (ERROR_INVALID_HANDLE);
2304 type = _wapi_handle_type (handle);
2306 if(io_ops[type].setendoffile==NULL) {
2307 SetLastError (ERROR_INVALID_HANDLE);
2311 return(io_ops[type].setendoffile (handle));
2316 * @handle: The file handle to set. The handle must have
2317 * %GENERIC_READ or %GENERIC_WRITE access.
2318 * @movedistance: Low 32 bits of a signed value that specifies the
2319 * number of bytes to move the file pointer.
2320 * @highmovedistance: Pointer to the high 32 bits of a signed value
2321 * that specifies the number of bytes to move the file pointer, or
2323 * @method: The starting point for the file pointer move.
2325 * Sets the file pointer of an open file.
2327 * The distance to move the file pointer is calculated from
2328 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2329 * @movedistance is the 32-bit signed value; otherwise, @movedistance
2330 * is the low 32 bits and @highmovedistance a pointer to the high 32
2331 * bits of a 64 bit signed value. A positive distance moves the file
2332 * pointer forward from the position specified by @method; a negative
2333 * distance moves the file pointer backward.
2335 * If the library is compiled without large file support,
2336 * @highmovedistance is ignored and its value is set to zero on a
2337 * successful return.
2339 * Return value: On success, the low 32 bits of the new file pointer.
2340 * If @highmovedistance is not %NULL, the high 32 bits of the new file
2341 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
2343 guint32 SetFilePointer(gpointer fd_handle, gint32 movedistance,
2344 gint32 *highmovedistance, WapiSeekMethod method)
2346 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2347 WapiHandleType type;
2349 if (handle == NULL) {
2350 SetLastError (ERROR_INVALID_HANDLE);
2351 return(INVALID_SET_FILE_POINTER);
2354 type = _wapi_handle_type (handle);
2356 if(io_ops[type].seek==NULL) {
2357 SetLastError (ERROR_INVALID_HANDLE);
2358 return(INVALID_SET_FILE_POINTER);
2361 return(io_ops[type].seek (handle, movedistance, highmovedistance,
2367 * @handle: The file handle to test.
2369 * Finds the type of file @handle.
2371 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2372 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
2373 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2374 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2376 WapiFileType GetFileType(gpointer fd_handle)
2378 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2379 WapiHandleType type;
2381 if (handle == NULL) {
2382 SetLastError (ERROR_INVALID_HANDLE);
2383 return(FILE_TYPE_UNKNOWN);
2386 type = _wapi_handle_type (handle);
2388 if(io_ops[type].getfiletype==NULL) {
2389 SetLastError (ERROR_INVALID_HANDLE);
2390 return(FILE_TYPE_UNKNOWN);
2393 return(io_ops[type].getfiletype ());
2398 * @handle: The file handle to query. The handle must have
2399 * %GENERIC_READ or %GENERIC_WRITE access.
2400 * @highsize: If non-%NULL, the high 32 bits of the file size are
2403 * Retrieves the size of the file @handle.
2405 * If the library is compiled without large file support, @highsize
2406 * has its value set to zero on a successful return.
2408 * Return value: On success, the low 32 bits of the file size. If
2409 * @highsize is non-%NULL then the high 32 bits of the file size are
2410 * stored here. On failure %INVALID_FILE_SIZE is returned.
2412 guint32 GetFileSize(gpointer fd_handle, guint32 *highsize)
2414 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2415 WapiHandleType type;
2417 if (handle == NULL) {
2418 SetLastError (ERROR_INVALID_HANDLE);
2419 return(INVALID_FILE_SIZE);
2422 type = _wapi_handle_type (handle);
2424 if(io_ops[type].getfilesize==NULL) {
2425 SetLastError (ERROR_INVALID_HANDLE);
2426 return(INVALID_FILE_SIZE);
2429 return(io_ops[type].getfilesize (handle, highsize));
2434 * @handle: The file handle to query. The handle must have
2435 * %GENERIC_READ access.
2436 * @create_time: Points to a %WapiFileTime structure to receive the
2437 * number of ticks since the epoch that file was created. May be
2439 * @last_access: Points to a %WapiFileTime structure to receive the
2440 * number of ticks since the epoch when file was last accessed. May be
2442 * @last_write: Points to a %WapiFileTime structure to receive the
2443 * number of ticks since the epoch when file was last written to. May
2446 * Finds the number of ticks since the epoch that the file referenced
2447 * by @handle was created, last accessed and last modified. A tick is
2448 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2451 * Create time isn't recorded on POSIX file systems or reported by
2452 * stat(2), so that time is guessed by returning the oldest of the
2455 * Return value: %TRUE on success, %FALSE otherwise.
2457 gboolean GetFileTime(gpointer fd_handle, WapiFileTime *create_time,
2458 WapiFileTime *last_access, WapiFileTime *last_write)
2460 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2461 WapiHandleType type;
2463 if (handle == NULL) {
2464 SetLastError (ERROR_INVALID_HANDLE);
2468 type = _wapi_handle_type (handle);
2470 if(io_ops[type].getfiletime==NULL) {
2471 SetLastError (ERROR_INVALID_HANDLE);
2475 return(io_ops[type].getfiletime (handle, create_time, last_access,
2481 * @handle: The file handle to set. The handle must have
2482 * %GENERIC_WRITE access.
2483 * @create_time: Points to a %WapiFileTime structure that contains the
2484 * number of ticks since the epoch that the file was created. May be
2486 * @last_access: Points to a %WapiFileTime structure that contains the
2487 * number of ticks since the epoch when the file was last accessed.
2489 * @last_write: Points to a %WapiFileTime structure that contains the
2490 * number of ticks since the epoch when the file was last written to.
2493 * Sets the number of ticks since the epoch that the file referenced
2494 * by @handle was created, last accessed or last modified. A tick is
2495 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2498 * Create time isn't recorded on POSIX file systems, and is ignored.
2500 * Return value: %TRUE on success, %FALSE otherwise.
2502 gboolean SetFileTime(gpointer fd_handle, const WapiFileTime *create_time,
2503 const WapiFileTime *last_access,
2504 const WapiFileTime *last_write)
2506 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2507 WapiHandleType type;
2509 if (handle == NULL) {
2510 SetLastError (ERROR_INVALID_HANDLE);
2514 type = _wapi_handle_type (handle);
2516 if(io_ops[type].setfiletime==NULL) {
2517 SetLastError (ERROR_INVALID_HANDLE);
2521 return(io_ops[type].setfiletime (handle, create_time, last_access,
2525 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2526 * January 1 1601 GMT
2529 #define TICKS_PER_MILLISECOND 10000L
2530 #define TICKS_PER_SECOND 10000000L
2531 #define TICKS_PER_MINUTE 600000000L
2532 #define TICKS_PER_HOUR 36000000000LL
2533 #define TICKS_PER_DAY 864000000000LL
2535 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2537 static const guint16 mon_yday[2][13]={
2538 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2539 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2543 * FileTimeToSystemTime:
2544 * @file_time: Points to a %WapiFileTime structure that contains the
2545 * number of ticks to convert.
2546 * @system_time: Points to a %WapiSystemTime structure to receive the
2549 * Converts a tick count into broken-out time values.
2551 * Return value: %TRUE on success, %FALSE otherwise.
2553 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2554 WapiSystemTime *system_time)
2556 gint64 file_ticks, totaldays, rem, y;
2559 if(system_time==NULL) {
2561 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2567 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2568 file_time->dwLowDateTime;
2570 /* Really compares if file_ticks>=0x8000000000000000
2571 * (LLONG_MAX+1) but we're working with a signed value for the
2572 * year and day calculation to work later
2576 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2582 totaldays=(file_ticks / TICKS_PER_DAY);
2583 rem = file_ticks % TICKS_PER_DAY;
2585 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2589 system_time->wHour=rem/TICKS_PER_HOUR;
2590 rem %= TICKS_PER_HOUR;
2592 g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2593 system_time->wHour, rem);
2596 system_time->wMinute = rem / TICKS_PER_MINUTE;
2597 rem %= TICKS_PER_MINUTE;
2599 g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2600 system_time->wMinute, rem);
2603 system_time->wSecond = rem / TICKS_PER_SECOND;
2604 rem %= TICKS_PER_SECOND;
2606 g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2607 system_time->wSecond, rem);
2610 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2612 g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2613 system_time->wMilliseconds);
2616 /* January 1, 1601 was a Monday, according to Emacs calendar */
2617 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2619 g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2620 system_time->wDayOfWeek);
2623 /* This algorithm to find year and month given days from epoch
2628 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2629 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2631 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2632 /* Guess a corrected year, assuming 365 days per year */
2633 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2635 g_message(G_GNUC_PRETTY_FUNCTION
2636 ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2638 g_message(G_GNUC_PRETTY_FUNCTION
2639 ": LEAPS(yg): %lld LEAPS(y): %lld",
2640 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2643 /* Adjust days and y to match the guessed year. */
2644 totaldays -= ((yg - y) * 365
2645 + LEAPS_THRU_END_OF (yg - 1)
2646 - LEAPS_THRU_END_OF (y - 1));
2648 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2653 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2657 system_time->wYear = y;
2659 g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2662 ip = mon_yday[isleap(y)];
2664 for(y=11; totaldays < ip[y]; --y) {
2669 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2672 system_time->wMonth = y + 1;
2674 g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2677 system_time->wDay = totaldays + 1;
2679 g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2686 file_compare (gconstpointer a, gconstpointer b)
2688 gchar *astr = *(gchar **) a;
2689 gchar *bstr = *(gchar **) b;
2691 return strcmp (astr, bstr);
2695 get_errno_from_g_file_error (gint error)
2699 case G_FILE_ERROR_ACCES:
2704 case G_FILE_ERROR_NAMETOOLONG:
2705 error = ENAMETOOLONG;
2709 case G_FILE_ERROR_NOENT:
2714 case G_FILE_ERROR_NOTDIR:
2719 case G_FILE_ERROR_NXIO:
2724 case G_FILE_ERROR_NODEV:
2729 case G_FILE_ERROR_ROFS:
2734 case G_FILE_ERROR_TXTBSY:
2739 case G_FILE_ERROR_FAULT:
2744 case G_FILE_ERROR_LOOP:
2749 case G_FILE_ERROR_NOSPC:
2754 case G_FILE_ERROR_NOMEM:
2759 case G_FILE_ERROR_MFILE:
2764 case G_FILE_ERROR_NFILE:
2769 case G_FILE_ERROR_BADF:
2774 case G_FILE_ERROR_INVAL:
2779 case G_FILE_ERROR_PIPE:
2784 case G_FILE_ERROR_AGAIN:
2789 case G_FILE_ERROR_INTR:
2794 case G_FILE_ERROR_IO:
2799 case G_FILE_ERROR_PERM:
2803 case G_FILE_ERROR_FAILED:
2804 error = ERROR_INVALID_PARAMETER;
2811 /* scandir using glib */
2813 mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
2815 GError *error = NULL;
2820 GPatternSpec *patspec;
2822 dir = g_dir_open (dirname, 0, &error);
2824 /* g_dir_open returns ENOENT on directories on which we don't
2825 * have read/x permission */
2826 gint errnum = get_errno_from_g_file_error (error->code);
2827 g_error_free (error);
2828 if (errnum == ENOENT && g_file_test (dirname, G_FILE_TEST_IS_DIR))
2835 patspec = g_pattern_spec_new (pattern);
2836 names = g_ptr_array_new ();
2837 while ((name = g_dir_read_name (dir)) != NULL) {
2838 if (g_pattern_match_string (patspec, name))
2839 g_ptr_array_add (names, g_strdup (name));
2842 g_pattern_spec_free (patspec);
2844 result = names->len;
2846 g_ptr_array_sort (names, file_compare);
2847 g_ptr_array_set_size (names, result + 1);
2849 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
2851 g_ptr_array_free (names, TRUE);
2857 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
2859 struct _WapiHandlePrivate_find *find_handle;
2860 gpointer handle, find_ret = INVALID_HANDLE_VALUE;
2862 gchar *utf8_pattern = NULL, *dir_part, *entry_part;
2865 gboolean unref = FALSE;
2867 if (pattern == NULL) {
2869 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
2872 SetLastError (ERROR_PATH_NOT_FOUND);
2873 return(INVALID_HANDLE_VALUE);
2876 utf8_pattern = mono_unicode_to_external (pattern);
2877 if (utf8_pattern == NULL) {
2879 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2882 SetLastError (ERROR_INVALID_NAME);
2883 return(INVALID_HANDLE_VALUE);
2887 g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
2891 /* Figure out which bit of the pattern is the directory */
2892 dir_part=g_path_get_dirname (utf8_pattern);
2893 entry_part=g_path_get_basename (utf8_pattern);
2896 /* Don't do this check for now, it breaks if directories
2897 * really do have metachars in their names (see bug 58116).
2898 * FIXME: Figure out a better solution to keep some checks...
2900 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
2901 SetLastError (ERROR_INVALID_NAME);
2903 g_free (entry_part);
2904 g_free (utf8_pattern);
2905 return(INVALID_HANDLE_VALUE);
2909 handle=_wapi_handle_new (WAPI_HANDLE_FIND);
2910 if(handle==_WAPI_HANDLE_INVALID) {
2911 g_warning (G_GNUC_PRETTY_FUNCTION ": error creating find handle");
2913 g_free (entry_part);
2914 g_free (utf8_pattern);
2916 return(INVALID_HANDLE_VALUE);
2919 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2921 thr_ret = _wapi_handle_lock_handle (handle);
2922 g_assert (thr_ret == 0);
2924 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
2925 (gpointer *)&find_handle);
2927 g_warning (G_GNUC_PRETTY_FUNCTION
2928 ": error looking up find handle %p", handle);
2931 g_free (entry_part);
2933 g_free (utf8_pattern);
2934 utf8_pattern = NULL;
2938 /* The pattern can specify a directory or a set of files.
2940 * The pattern can have wildcard characters ? and *, but only
2941 * in the section after the last directory delimiter. (Return
2942 * ERROR_INVALID_NAME if there are wildcards in earlier path
2943 * sections.) "*" has the usual 0-or-more chars meaning. "?"
2944 * means "match one character", "??" seems to mean "match one
2945 * or two characters", "???" seems to mean "match one, two or
2946 * three characters", etc. Windows will also try and match
2947 * the mangled "short name" of files, so 8 character patterns
2948 * with wildcards will show some surprising results.
2950 * All the written documentation I can find says that '?'
2951 * should only match one character, and doesn't mention '??',
2952 * '???' etc. I'm going to assume that the strict behaviour
2953 * (ie '???' means three and only three characters) is the
2954 * correct one, because that lets me use fnmatch(3) rather
2955 * than mess around with regexes.
2958 find_handle->namelist = NULL;
2959 result = mono_io_scandir (dir_part, entry_part, &find_handle->namelist);
2963 gint errnum = errno;
2965 _wapi_set_last_error_from_errno ();
2967 g_message (G_GNUC_PRETTY_FUNCTION ": scandir error: %s", g_strerror (errnum));
2969 g_free (utf8_pattern);
2970 g_free (entry_part);
2976 g_free (utf8_pattern);
2977 g_free (entry_part);
2980 g_message (G_GNUC_PRETTY_FUNCTION ": Got %d matches", result);
2983 find_handle->dir_part = dir_part;
2984 find_handle->num = result;
2985 find_handle->count = 0;
2990 thr_ret = _wapi_handle_unlock_handle (handle);
2991 g_assert (thr_ret == 0);
2992 pthread_cleanup_pop (0);
2994 /* FindNextFile has to be called after unlocking the handle,
2995 * because it wants to lock the handle itself
2997 if (find_ret != INVALID_HANDLE_VALUE &&
2998 !FindNextFile (handle, find_data)) {
3000 SetLastError (ERROR_NO_MORE_FILES);
3001 find_ret = INVALID_HANDLE_VALUE;
3004 /* Must not call _wapi_handle_unref() with the handle already
3008 _wapi_handle_unref (handle);
3014 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
3016 struct _WapiHandlePrivate_find *find_handle;
3020 gchar *utf8_filename, *utf8_basename;
3021 gunichar2 *utf16_basename;
3025 gboolean ret = FALSE;
3027 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3028 (gpointer *)&find_handle);
3030 g_warning (G_GNUC_PRETTY_FUNCTION
3031 ": error looking up find handle %p", handle);
3032 SetLastError (ERROR_INVALID_HANDLE);
3036 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3038 thr_ret = _wapi_handle_lock_handle (handle);
3039 g_assert (thr_ret == 0);
3042 if (find_handle->count >= find_handle->num) {
3043 SetLastError (ERROR_NO_MORE_FILES);
3047 /* stat next match */
3049 filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3050 if (lstat (filename, &buf) != 0) {
3052 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
3059 /* Check for dangling symlinks, and ignore them (principle of
3060 * least surprise, avoiding confusion where we report the file
3061 * exists, but when someone tries to open it we would report
3064 if(S_ISLNK (buf.st_mode)) {
3065 if(stat (filename, &buf) != 0) {
3071 utf8_filename=mono_utf8_from_external (filename);
3072 if(utf8_filename==NULL) {
3073 /* We couldn't turn this filename into utf8 (eg the
3074 * encoding of the name wasn't convertible), so just
3083 g_message (G_GNUC_PRETTY_FUNCTION ": Found [%s]", utf8_filename);
3086 /* fill data block */
3088 if (buf.st_mtime < buf.st_ctime)
3089 create_time = buf.st_mtime;
3091 create_time = buf.st_ctime;
3093 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3095 _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
3096 _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3097 _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3099 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3100 find_data->nFileSizeHigh = 0;
3101 find_data->nFileSizeLow = 0;
3104 find_data->nFileSizeHigh = buf.st_size >> 32;
3105 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3108 find_data->dwReserved0 = 0;
3109 find_data->dwReserved1 = 0;
3111 utf8_basename = g_path_get_basename (utf8_filename);
3112 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3114 if(utf16_basename==NULL) {
3115 g_free (utf8_basename);
3116 g_free (utf8_filename);
3121 /* utf16 is 2 * utf8 */
3124 memset (find_data->cFileName, '\0', (MAX_PATH*2));
3126 /* Truncating a utf16 string like this might leave the last
3129 memcpy (find_data->cFileName, utf16_basename,
3130 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3132 find_data->cAlternateFileName [0] = 0; /* not used */
3134 g_free (utf8_basename);
3135 g_free (utf8_filename);
3136 g_free (utf16_basename);
3139 thr_ret = _wapi_handle_unlock_handle (handle);
3140 g_assert (thr_ret == 0);
3141 pthread_cleanup_pop (0);
3148 * @wapi_handle: the find handle to close.
3150 * Closes find handle @wapi_handle
3152 * Return value: %TRUE on success, %FALSE otherwise.
3154 gboolean FindClose (gpointer handle)
3156 struct _WapiHandlePrivate_find *find_handle;
3160 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3161 (gpointer *)&find_handle);
3163 g_warning (G_GNUC_PRETTY_FUNCTION
3164 ": error looking up find handle %p", handle);
3165 SetLastError (ERROR_INVALID_HANDLE);
3169 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3171 thr_ret = _wapi_handle_lock_handle (handle);
3172 g_assert (thr_ret == 0);
3174 g_strfreev (find_handle->namelist);
3175 g_free (find_handle->dir_part);
3177 thr_ret = _wapi_handle_unlock_handle (handle);
3178 g_assert (thr_ret == 0);
3179 pthread_cleanup_pop (0);
3181 _wapi_handle_unref (handle);
3188 * @name: a pointer to a NULL-terminated unicode string, that names
3189 * the directory to be created.
3190 * @security: ignored for now
3192 * Creates directory @name
3194 * Return value: %TRUE on success, %FALSE otherwise.
3196 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
3203 utf8_name = mono_unicode_to_external (name);
3204 if (utf8_name == NULL) {
3206 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3212 result = mkdir (utf8_name, 0777);
3219 if (errno == EEXIST) {
3220 result = stat (utf8_name, &buf);
3222 _wapi_set_last_error_from_errno ();
3228 attrs = _wapi_stat_to_file_attributes (&buf);
3229 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
3233 _wapi_set_last_error_from_errno ();
3237 _wapi_set_last_error_from_errno ();
3244 * @name: a pointer to a NULL-terminated unicode string, that names
3245 * the directory to be removed.
3247 * Removes directory @name
3249 * Return value: %TRUE on success, %FALSE otherwise.
3251 gboolean RemoveDirectory (const gunichar2 *name)
3256 utf8_name = mono_unicode_to_external (name);
3257 if (utf8_name == NULL) {
3259 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3265 result = rmdir (utf8_name);
3271 _wapi_set_last_error_from_errno ();
3276 * GetFileAttributes:
3277 * @name: a pointer to a NULL-terminated unicode filename.
3279 * Gets the attributes for @name;
3281 * Return value: %INVALID_FILE_ATTRIBUTES on failure
3283 guint32 GetFileAttributes (const gunichar2 *name)
3289 utf8_name = mono_unicode_to_external (name);
3290 if (utf8_name == NULL) {
3292 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3295 SetLastError (ERROR_INVALID_PARAMETER);
3296 return (INVALID_FILE_ATTRIBUTES);
3299 result = stat (utf8_name, &buf);
3302 _wapi_set_last_error_from_errno ();
3304 return (INVALID_FILE_ATTRIBUTES);
3308 return _wapi_stat_to_file_attributes (&buf);
3312 * GetFileAttributesEx:
3313 * @name: a pointer to a NULL-terminated unicode filename.
3314 * @level: must be GetFileExInfoStandard
3315 * @info: pointer to a WapiFileAttributesData structure
3317 * Gets attributes, size and filetimes for @name;
3319 * Return value: %TRUE on success, %FALSE on failure
3321 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3324 WapiFileAttributesData *data;
3330 if (level != GetFileExInfoStandard) {
3332 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
3338 utf8_name = mono_unicode_to_external (name);
3339 if (utf8_name == NULL) {
3341 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3344 SetLastError (ERROR_INVALID_PARAMETER);
3348 result = stat (utf8_name, &buf);
3352 SetLastError (ERROR_FILE_NOT_FOUND);
3356 /* fill data block */
3358 data = (WapiFileAttributesData *)info;
3360 if (buf.st_mtime < buf.st_ctime)
3361 create_time = buf.st_mtime;
3363 create_time = buf.st_ctime;
3365 data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3367 _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3368 _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3369 _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3371 if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3372 data->nFileSizeHigh = 0;
3373 data->nFileSizeLow = 0;
3376 data->nFileSizeHigh = buf.st_size >> 32;
3377 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3385 * @name: name of file
3386 * @attrs: attributes to set
3388 * Changes the attributes on a named file.
3390 * Return value: %TRUE on success, %FALSE on failure.
3392 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3394 /* FIXME: think of something clever to do on unix */
3400 * Currently we only handle one *internal* case, with a value that is
3401 * not standard: 0x80000000, which means `set executable bit'
3404 utf8_name = mono_unicode_to_external (name);
3405 result = stat (utf8_name, &buf);
3408 SetLastError (ERROR_FILE_NOT_FOUND);
3412 /* Contrary to the documentation, ms allows NORMAL to be
3413 * specified along with other attributes, so dont bother to
3414 * catch that case here.
3416 if (attrs & FILE_ATTRIBUTE_READONLY) {
3417 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3419 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
3422 /* Ignore the other attributes for now */
3424 if (attrs & 0x80000000){
3425 mode_t exec_mask = 0;
3427 if ((buf.st_mode & S_IRUSR) != 0)
3428 exec_mask |= S_IXUSR;
3430 if ((buf.st_mode & S_IRGRP) != 0)
3431 exec_mask |= S_IXGRP;
3433 if ((buf.st_mode & S_IROTH) != 0)
3434 exec_mask |= S_IXOTH;
3436 result = chmod (utf8_name, buf.st_mode | exec_mask);
3438 /* Don't bother to reset executable (might need to change this
3448 * GetCurrentDirectory
3449 * @length: size of the buffer
3450 * @buffer: pointer to buffer that recieves path
3452 * Retrieves the current directory for the current process.
3454 * Return value: number of characters in buffer on success, zero on failure
3456 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3459 gunichar2 *utf16_path;
3463 path = g_get_current_dir ();
3467 utf16_path=mono_unicode_from_external (path, &bytes);
3469 /* if buffer too small, return number of characters required.
3470 * this is plain dumb.
3473 count = (bytes/2)+1;
3474 if (count > length) {
3476 g_free (utf16_path);
3481 /* Add the terminator */
3482 memset (buffer, '\0', bytes+2);
3483 memcpy (buffer, utf16_path, bytes);
3485 g_free (utf16_path);
3492 * SetCurrentDirectory
3493 * @path: path to new directory
3495 * Changes the directory path for the current process.
3497 * Return value: %TRUE on success, %FALSE on failure.
3499 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3504 utf8_path = mono_unicode_to_external (path);
3505 if (chdir (utf8_path) != 0) {
3506 _wapi_set_last_error_from_errno ();
3516 /* When we're confident there are no more bugs in the fd->handle
3517 * mapping, this can be replaced as a no-op: GPOINTER_TO_INT(fd_handle) == fd
3519 int _wapi_file_handle_to_fd (gpointer fd_handle)
3521 struct _WapiHandlePrivate_file *file_private_handle;
3523 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3526 g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
3529 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
3530 (gpointer *)&file_private_handle);
3532 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
3533 (gpointer *)&file_private_handle);
3535 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
3536 (gpointer *)&file_private_handle);
3539 g_message (G_GNUC_PRETTY_FUNCTION
3548 g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
3549 file_private_handle->fd);
3552 g_assert (file_private_handle->fd == GPOINTER_TO_INT (fd_handle));
3554 return(file_private_handle->fd);
3557 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3558 WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3560 struct _WapiHandle_file *pipe_read_handle;
3561 struct _WapiHandle_file *pipe_write_handle;
3562 struct _WapiHandlePrivate_file *pipe_read_private_handle;
3563 struct _WapiHandlePrivate_file *pipe_write_private_handle;
3564 gpointer read_handle;
3565 gpointer write_handle;
3570 gboolean unref_read = FALSE, unref_write = FALSE;
3571 gboolean cp_ret = FALSE;
3573 mono_once (&io_ops_once, io_ops_init);
3576 g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
3582 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
3586 _wapi_set_last_error_from_errno ();
3590 if (filedes[0] >= _wapi_fd_offset_table_size ||
3591 filedes[1] >= _wapi_fd_offset_table_size) {
3593 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
3596 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3604 /* filedes[0] is open for reading, filedes[1] for writing */
3606 read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3607 if(read_handle==_WAPI_HANDLE_INVALID) {
3608 g_warning (G_GNUC_PRETTY_FUNCTION
3609 ": error creating pipe read handle");
3615 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3617 thr_ret = _wapi_handle_lock_handle (read_handle);
3618 g_assert (thr_ret == 0);
3620 ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
3621 (gpointer *)&pipe_read_handle,
3622 (gpointer *)&pipe_read_private_handle);
3624 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3630 write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3631 if(write_handle==_WAPI_HANDLE_INVALID) {
3632 g_warning (G_GNUC_PRETTY_FUNCTION
3633 ": error creating pipe write handle");
3641 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3643 thr_ret = _wapi_handle_lock_handle (write_handle);
3644 g_assert (thr_ret == 0);
3646 ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
3647 (gpointer *)&pipe_write_handle,
3648 (gpointer *)&pipe_write_private_handle);
3650 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3660 pipe_read_private_handle->fd=filedes[0];
3661 pipe_read_private_handle->assigned=TRUE;
3662 pipe_read_handle->fileaccess=GENERIC_READ;
3664 _wapi_handle_fd_offset_store (filedes[0], read_handle);
3665 *readpipe=GINT_TO_POINTER (filedes[0]);
3667 pipe_write_private_handle->fd=filedes[1];
3668 pipe_write_private_handle->assigned=TRUE;
3669 pipe_write_handle->fileaccess=GENERIC_WRITE;
3671 _wapi_handle_fd_offset_store (filedes[1], write_handle);
3672 *writepipe=GINT_TO_POINTER (filedes[1]);
3675 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]);
3679 thr_ret =_wapi_handle_unlock_handle (write_handle);
3680 g_assert (thr_ret == 0);
3681 pthread_cleanup_pop (0);
3684 _wapi_handle_unref (write_handle);
3688 thr_ret =_wapi_handle_unlock_handle (read_handle);
3689 g_assert (thr_ret == 0);
3690 pthread_cleanup_pop (0);
3692 /* Must not call _wapi_handle_unref() with the handle already
3696 _wapi_handle_unref (read_handle);
3702 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3704 gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3705 gunichar2 *tmpdir16=NULL;
3710 if(tmpdir[strlen (tmpdir)]!='/') {
3712 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3715 tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3716 if(tmpdir16==NULL) {
3724 g_message (G_GNUC_PRETTY_FUNCTION
3725 ": Size %d smaller than needed (%ld)", len,
3731 /* Add the terminator */
3732 memset (buf, '\0', bytes+2);
3733 memcpy (buf, tmpdir16, bytes);
3739 if(tmpdir16!=NULL) {
3748 _wapi_io_add_callback (gpointer fd_handle,
3749 WapiOverlappedCB callback,
3750 guint64 flags G_GNUC_UNUSED)
3752 struct _WapiHandle_file *file_handle;
3753 struct _WapiHandlePrivate_file *file_private_handle;
3756 gboolean ret = FALSE;
3757 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3759 if (handle == NULL) {
3760 SetLastError (ERROR_INVALID_HANDLE);
3764 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3765 (gpointer *) &file_handle,
3766 (gpointer *) &file_private_handle);
3769 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
3770 (gpointer *) &file_handle,
3771 (gpointer *) &file_private_handle);
3775 if (ok == FALSE || file_private_handle->async == FALSE) {
3776 SetLastError (ERROR_INVALID_HANDLE);
3780 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3782 thr_ret = _wapi_handle_lock_handle (handle);
3783 g_assert (thr_ret == 0);
3785 if (file_private_handle->callback != NULL) {
3786 SetLastError (ERROR_INVALID_PARAMETER);
3791 file_private_handle->callback = callback;
3794 thr_ret = _wapi_handle_unlock_handle (handle);
3795 g_assert (thr_ret == 0);
3796 pthread_cleanup_pop (0);
3802 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
3805 gunichar2 *ptr, *dir;
3806 glong length, total = 0;
3810 memset (buf, 0, sizeof (gunichar2) * (len + 1));
3815 /* Sigh, mntent and friends don't work well.
3816 * It stops on the first line that doesn't begin with a '/'.
3817 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
3818 fp = fopen ("/etc/mtab", "rt");
3820 fp = fopen ("/etc/mnttab", "rt");
3826 while (fgets (buffer, 512, fp) != NULL) {
3830 splitted = g_strsplit (buffer, " ", 0);
3831 if (!*splitted || !*(splitted + 1))
3834 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
3835 g_strfreev (splitted);
3836 if (total + length + 1 > len) {
3837 return len * 2; /* guess */
3840 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
3842 total += length + 1;
3847 /* Commented out, does not work with my mtab!!! - Gonz */
3848 #ifdef NOTENABLED /* HAVE_MNTENT_H */
3852 gunichar2 *ptr, *dir;
3853 glong len, total = 0;
3856 fp = setmntent ("/etc/mtab", "rt");
3858 fp = setmntent ("/etc/mnttab", "rt");
3864 while ((mnt = getmntent (fp)) != NULL) {
3865 g_print ("GOT %s\n", mnt->mnt_dir);
3866 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
3867 if (total + len + 1 > len) {
3868 return len * 2; /* guess */
3871 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
3882 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
3884 struct flock lock_data;
3887 lock_data.l_type = F_WRLCK;
3888 lock_data.l_whence = SEEK_SET;
3889 lock_data.l_start = offset;
3890 lock_data.l_len = length;
3893 ret = fcntl (fd, F_SETLK, &lock_data);
3895 while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
3898 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
3902 SetLastError (ERROR_LOCK_VIOLATION);
3909 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
3911 struct flock lock_data;
3914 lock_data.l_type = F_UNLCK;
3915 lock_data.l_whence = SEEK_SET;
3916 lock_data.l_start = offset;
3917 lock_data.l_len = length;
3920 ret = fcntl (fd, F_SETLK, &lock_data);
3922 while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
3925 g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
3929 SetLastError (ERROR_LOCK_VIOLATION);
3936 gboolean LockFile (gpointer fd_handle, guint32 offset_low, guint32 offset_high,
3937 guint32 length_low, guint32 length_high)
3939 struct _WapiHandle_file *file_handle;
3940 struct _WapiHandlePrivate_file *file_private_handle;
3942 off_t offset, length;
3943 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3945 if (handle == NULL) {
3946 SetLastError (ERROR_INVALID_HANDLE);
3950 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3951 (gpointer *)&file_handle,
3952 (gpointer *)&file_private_handle);
3954 g_warning (G_GNUC_PRETTY_FUNCTION
3955 ": error looking up file handle %p", handle);
3956 SetLastError (ERROR_INVALID_HANDLE);
3960 if (!(file_handle->fileaccess & GENERIC_READ) &&
3961 !(file_handle->fileaccess & GENERIC_WRITE) &&
3962 !(file_handle->fileaccess & GENERIC_ALL)) {
3964 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);
3966 SetLastError (ERROR_ACCESS_DENIED);
3970 #ifdef HAVE_LARGE_FILE_SUPPORT
3971 offset = ((gint64)offset_high << 32) | offset_low;
3972 length = ((gint64)length_high << 32) | length_low;
3975 g_message (G_GNUC_PRETTY_FUNCTION
3976 ": Locking handle %p fd %d, offset %lld, length %lld",
3977 handle, file_private_handle->fd, offset, length);
3980 offset = offset_low;
3981 length = length_low;
3984 g_message (G_GNUC_PRETTY_FUNCTION
3985 ": Locking handle %p fd %d, offset %ld, length %ld",
3986 handle, file_private_handle->fd, offset, length);
3990 return(_wapi_lock_file_region (file_private_handle->fd, offset,
3994 gboolean UnlockFile (gpointer fd_handle, guint32 offset_low,
3995 guint32 offset_high, guint32 length_low,
3996 guint32 length_high)
3998 struct _WapiHandle_file *file_handle;
3999 struct _WapiHandlePrivate_file *file_private_handle;
4001 off_t offset, length;
4002 gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4004 if (handle == NULL) {
4005 SetLastError (ERROR_INVALID_HANDLE);
4009 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4010 (gpointer *)&file_handle,
4011 (gpointer *)&file_private_handle);
4013 g_warning (G_GNUC_PRETTY_FUNCTION
4014 ": error looking up file handle %p", handle);
4015 SetLastError (ERROR_INVALID_HANDLE);
4019 if (!(file_handle->fileaccess & GENERIC_READ) &&
4020 !(file_handle->fileaccess & GENERIC_WRITE) &&
4021 !(file_handle->fileaccess & GENERIC_ALL)) {
4023 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);
4025 SetLastError (ERROR_ACCESS_DENIED);
4029 #ifdef HAVE_LARGE_FILE_SUPPORT
4030 offset = ((gint64)offset_high << 32) | offset_low;
4031 length = ((gint64)length_high << 32) | length_low;
4034 g_message (G_GNUC_PRETTY_FUNCTION
4035 ": Unlocking handle %p fd %d, offset %lld, length %lld",
4036 handle, file_private_handle->fd, offset, length);
4039 offset = offset_low;
4040 length = length_low;
4043 g_message (G_GNUC_PRETTY_FUNCTION
4044 ": Unlocking handle %p fd %d, offset %ld, length %ld",
4045 handle, file_private_handle->fd, offset, length);
4049 return(_wapi_unlock_file_region (file_private_handle->fd, offset,