Added more tests for Exception Handling Statements
[mono.git] / mono / io-layer / io.c
1 /*
2  * io.c:  File, console and find handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <dirent.h>
19 #include <fnmatch.h>
20 #include <stdio.h>
21 #include <utime.h>
22
23 #ifndef PLATFORM_WIN32
24 #ifdef HAVE_AIO_H
25 #include <aio.h>
26 #define USE_AIO 1
27 #elif defined(HAVE_SYS_AIO_H)
28 #include <sys/aio.h>
29 #define USE_AIO 1
30 #else
31 #undef USE_AIO
32 #endif
33 #endif
34
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>
42
43 #undef DEBUG
44
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);
47
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);
69
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 */
74         NULL,                   /* signal */
75         NULL,                   /* own */
76         NULL,                   /* is_owned */
77 };
78
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);
88
89 /* Console is mostly the same as file, except it can block waiting for
90  * input or output
91  */
92 struct _WapiHandleOps _wapi_console_ops = {
93         console_close_shared,   /* close_shared */
94         console_close_private,  /* close_private */
95         NULL,                   /* signal */
96         NULL,                   /* own */
97         NULL,                   /* is_owned */
98 };
99
100 /* Find handle has no ops.
101  */
102 struct _WapiHandleOps _wapi_find_ops = {
103         NULL,                   /* close_shared */
104         NULL,                   /* close_private */
105         NULL,                   /* signal */
106         NULL,                   /* own */
107         NULL,                   /* is_owned */
108 };
109
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);
118
119 /* Pipe handles
120  */
121 struct _WapiHandleOps _wapi_pipe_ops = {
122         pipe_close_shared,      /* close_shared */
123         pipe_close_private,     /* close_private */
124         NULL,                   /* signal */
125         NULL,                   /* own */
126         NULL,                   /* is_owned */
127 };
128
129 static struct {
130         /* File, console and pipe handles */
131         WapiFileType (*getfiletype)(void);
132         
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);
141         
142         /* File handles */
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},
156         /* file */
157         {file_getfiletype,
158          file_read, file_write,
159          file_flush, file_seek,
160          file_setendoffile,
161          file_getfilesize,
162          file_getfiletime,
163          file_setfiletime},
164         /* console */
165         {console_getfiletype,
166          console_read,
167          console_write,
168          NULL, NULL, NULL, NULL, NULL, NULL},
169         /* thread */
170         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
171         /* sem */
172         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
173         /* mutex */
174         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
175         /* event */
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},
179         /* find */
180         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
181         /* process */
182         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
183         /* pipe */
184         {pipe_getfiletype,
185          pipe_read,
186          pipe_write,
187          NULL, NULL, NULL, NULL, NULL, NULL},
188 };
189
190
191 static mono_once_t io_ops_once=MONO_ONCE_INIT;
192
193 static void io_ops_init (void)
194 {
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); */
199 }
200
201 /* Some utility functions.
202  */
203
204 static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
205 {
206         guint32 attrs = 0;
207
208         /* FIXME: this could definitely be better */
209
210         if (S_ISDIR (buf->st_mode))
211                 attrs |= FILE_ATTRIBUTE_DIRECTORY;
212         else
213                 attrs |= FILE_ATTRIBUTE_ARCHIVE;
214         
215         if (!(buf->st_mode & S_IWUSR))
216                 attrs |= FILE_ATTRIBUTE_READONLY;
217         
218         return attrs;
219 }
220
221 static void
222 _wapi_set_last_error_from_errno (void)
223 {
224         SetLastError (_wapi_get_win32_file_error (errno));
225 }
226
227 /* Handle ops.
228  */
229 static void file_close_shared (gpointer handle)
230 {
231         struct _WapiHandle_file *file_handle;
232         gboolean ok;
233         
234         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
235                                 (gpointer *)&file_handle, NULL);
236         if(ok==FALSE) {
237                 g_warning (G_GNUC_PRETTY_FUNCTION
238                            ": error looking up file handle %p", handle);
239                 return;
240         }
241         
242 #ifdef DEBUG
243         g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p", handle);
244 #endif
245         
246         if(file_handle->filename!=0) {
247                 _wapi_handle_scratch_delete (file_handle->filename);
248                 file_handle->filename=0;
249         }
250         if(file_handle->security_attributes!=0) {
251                 _wapi_handle_scratch_delete (file_handle->security_attributes);
252                 file_handle->security_attributes=0;
253         }
254 }
255
256 static void file_close_private (gpointer handle)
257 {
258         struct _WapiHandlePrivate_file *file_private_handle;
259         gboolean ok;
260         
261         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
262                                 (gpointer *)&file_private_handle);
263         if(ok==FALSE) {
264                 g_warning (G_GNUC_PRETTY_FUNCTION
265                            ": error looking up file handle %p", handle);
266                 return;
267         }
268         
269 #ifdef DEBUG
270         g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
271                   handle, file_private_handle->fd);
272 #endif
273         
274         /* Blank out the mapping, to make catching errors easier */
275         _wapi_handle_fd_offset_store (file_private_handle->fd, NULL);
276         
277         close(file_private_handle->fd);
278 }
279
280 static WapiFileType file_getfiletype(void)
281 {
282         return(FILE_TYPE_DISK);
283 }
284
285 #ifdef USE_AIO
286 typedef struct {
287         struct aiocb *aio;
288         WapiOverlapped *overlapped;
289         WapiOverlappedCB callback;
290 } notifier_data_t;
291
292 #define SIGPTR(a) a.SIGVAL_PTR
293
294 static void
295 async_notifier (union sigval sig)
296 {
297         notifier_data_t *ndata = SIGPTR (sig);
298         guint32 error;
299         guint32 numbytes;
300
301         error = aio_return (ndata->aio);
302         if (error < 0) {
303                 error = _wapi_get_win32_file_error (error);
304                 numbytes = 0;
305         } else {
306                 numbytes = error;
307                 error = 0;
308         }
309
310         ndata->callback (error, numbytes, ndata->overlapped);
311         g_free (ndata->aio);
312         g_free (ndata);
313 }
314
315 #endif /* USE_AIO */
316
317 static gboolean file_read(gpointer handle, gpointer buffer,
318                           guint32 numbytes, guint32 *bytesread,
319                           WapiOverlapped *overlapped)
320 {
321         struct _WapiHandle_file *file_handle;
322         struct _WapiHandlePrivate_file *file_private_handle;
323         gboolean ok;
324         int ret;
325         
326         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
327                                 (gpointer *)&file_handle,
328                                 (gpointer *)&file_private_handle);
329         if(ok==FALSE) {
330                 g_warning (G_GNUC_PRETTY_FUNCTION
331                            ": error looking up file handle %p", handle);
332                 return(FALSE);
333         }
334         
335         if(bytesread!=NULL) {
336                 *bytesread=0;
337         }
338         
339         if(!(file_handle->fileaccess&GENERIC_READ) &&
340            !(file_handle->fileaccess&GENERIC_ALL)) {
341 #ifdef DEBUG
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);
343 #endif
344
345                 return(FALSE);
346         }
347
348         if (file_private_handle->async == FALSE) {
349                 do {
350                         ret=read(file_private_handle->fd, buffer, numbytes);
351                 }
352                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
353                         
354                 if(ret==-1) {
355                         gint err = errno;
356
357 #ifdef DEBUG
358                         g_message(G_GNUC_PRETTY_FUNCTION
359                                   ": read of handle %p fd %d error: %s", handle,
360                                   file_private_handle->fd, strerror(err));
361 #endif
362                         SetLastError (_wapi_get_win32_file_error (err));
363                         return(FALSE);
364                 }
365                 
366                 if(bytesread!=NULL) {
367                         *bytesread=ret;
368                 }
369                 
370                 return(TRUE);
371         }
372
373 #ifndef USE_AIO
374         SetLastError (ERROR_NOT_SUPPORTED);
375         return FALSE;
376 #else
377         if (overlapped == NULL || file_private_handle->callback == NULL) {
378                 SetLastError (ERROR_INVALID_PARAMETER);
379                 return FALSE;
380         }
381
382         {
383         int fd = file_private_handle->fd;
384         struct aiocb *aio;
385         int result;
386         notifier_data_t *ndata;
387
388         ndata = g_new0 (notifier_data_t, 1);
389         aio = g_new0 (struct aiocb, 1);
390         ndata->overlapped = overlapped;
391         ndata->aio = aio;
392         ndata->callback = file_private_handle->callback;
393
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;
402
403         result = aio_read (aio);
404         if (result == -1) {
405                 _wapi_set_last_error_from_errno ();
406                 return FALSE;
407         }
408
409         result = aio_error (aio);
410 #ifdef DEBUG
411         g_print ("aio_error (read) returned %d for %d\n", result, fd);
412 #endif
413         if (result == 0) {
414                 numbytes = aio_return (aio);
415 #ifdef DEBUG
416                 g_print ("numbytes %d for %d\n", numbytes, fd);
417 #endif
418         } else {
419                 errno = result;
420                 _wapi_set_last_error_from_errno ();
421                 return FALSE;
422         }
423
424         if (bytesread)
425                 *bytesread = numbytes;
426
427         return TRUE;
428         }
429 #endif
430 }
431
432 static gboolean file_write(gpointer handle, gconstpointer buffer,
433                            guint32 numbytes, guint32 *byteswritten,
434                            WapiOverlapped *overlapped G_GNUC_UNUSED)
435 {
436         struct _WapiHandle_file *file_handle;
437         struct _WapiHandlePrivate_file *file_private_handle;
438         gboolean ok;
439         int ret;
440         
441         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
442                                 (gpointer *)&file_handle,
443                                 (gpointer *)&file_private_handle);
444         if(ok==FALSE) {
445                 g_warning (G_GNUC_PRETTY_FUNCTION
446                            ": error looking up file handle %p", handle);
447                 return(FALSE);
448         }
449         
450         if(byteswritten!=NULL) {
451                 *byteswritten=0;
452         }
453         
454         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
455            !(file_handle->fileaccess&GENERIC_ALL)) {
456 #ifdef DEBUG
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);
458 #endif
459
460                 return(FALSE);
461         }
462         
463         if (file_private_handle->async == FALSE) {
464                 off_t current_pos;
465                 
466                 /* Need to lock the region we're about to write to,
467                  * because we only do advisory locking on POSIX
468                  * systems
469                  */
470                 current_pos = lseek (file_private_handle->fd, (off_t)0,
471                                      SEEK_CUR);
472                 if (current_pos == -1) {
473 #ifdef DEBUG
474                         g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d lseek failed: %s", handle, file_private_handle->fd, strerror (errno));
475 #endif
476                         _wapi_set_last_error_from_errno ();
477                         return(FALSE);
478                 }
479                 
480                 if (_wapi_lock_file_region (file_private_handle->fd,
481                                             current_pos, numbytes) == FALSE) {
482                         /* The error has already been set */
483                         return(FALSE);
484                 }
485                 
486                 do {
487                         ret=write(file_private_handle->fd, buffer, numbytes);
488                 }
489                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
490
491                 _wapi_unlock_file_region (file_private_handle->fd, current_pos,
492                                           numbytes);
493
494                 if(ret==-1) {
495 #ifdef DEBUG
496                         g_message(G_GNUC_PRETTY_FUNCTION
497                                   ": write of handle %p fd %d error: %s", handle,
498                                   file_private_handle->fd, strerror(errno));
499 #endif
500
501                         return(FALSE);
502                 }
503                 if(byteswritten!=NULL) {
504                         *byteswritten=ret;
505                 }
506                 return(TRUE);
507         }
508
509 #ifndef USE_AIO
510         SetLastError (ERROR_NOT_SUPPORTED);
511         return FALSE;
512 #else
513         if (overlapped == NULL || file_private_handle->callback == NULL) {
514                 SetLastError (ERROR_INVALID_PARAMETER);
515                 return FALSE;
516         }
517
518         {
519         int fd = file_private_handle->fd;
520         struct aiocb *aio;
521         int result;
522         notifier_data_t *ndata;
523
524         ndata = g_new0 (notifier_data_t, 1);
525         aio = g_new0 (struct aiocb, 1);
526         ndata->overlapped = overlapped;
527         ndata->aio = aio;
528         ndata->callback = file_private_handle->callback;
529
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;
538
539         result = aio_write (aio);
540         if (result == -1) {
541                 _wapi_set_last_error_from_errno ();
542                 return FALSE;
543         }
544
545         result = aio_error (aio);
546 #ifdef DEBUG
547         g_print ("aio_error (write) returned %d for %d\n", result, fd);
548 #endif
549         if (result == 0) {
550                 numbytes = aio_return (aio);
551 #ifdef DEBUG
552         g_print ("numbytes %d for %d\n", numbytes, fd);
553 #endif
554         } else {
555                 errno = result;
556                 _wapi_set_last_error_from_errno ();
557                 return FALSE;
558         }
559
560         if (byteswritten)
561                 *byteswritten = numbytes;
562
563         return TRUE;
564         }
565 #endif
566 }
567
568 static gboolean file_flush(gpointer handle)
569 {
570         struct _WapiHandle_file *file_handle;
571         struct _WapiHandlePrivate_file *file_private_handle;
572         gboolean ok;
573         int ret;
574         
575         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
576                                 (gpointer *)&file_handle,
577                                 (gpointer *)&file_private_handle);
578         if(ok==FALSE) {
579                 g_warning (G_GNUC_PRETTY_FUNCTION
580                            ": error looking up file handle %p", handle);
581                 return(FALSE);
582         }
583
584         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
585            !(file_handle->fileaccess&GENERIC_ALL)) {
586 #ifdef DEBUG
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);
588 #endif
589
590                 return(FALSE);
591         }
592
593         ret=fsync(file_private_handle->fd);
594         if (ret==-1) {
595 #ifdef DEBUG
596                 g_message(G_GNUC_PRETTY_FUNCTION
597                           ": write of handle %p fd %d error: %s", handle,
598                           file_private_handle->fd, strerror(errno));
599 #endif
600
601                 return(FALSE);
602         }
603         
604         return(TRUE);
605 }
606
607 static guint32 file_seek(gpointer handle, gint32 movedistance,
608                          gint32 *highmovedistance, WapiSeekMethod method)
609 {
610         struct _WapiHandle_file *file_handle;
611         struct _WapiHandlePrivate_file *file_private_handle;
612         gboolean ok;
613         off_t offset, newpos;
614         int whence;
615         guint32 ret;
616         
617         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
618                                 (gpointer *)&file_handle,
619                                 (gpointer *)&file_private_handle);
620         if(ok==FALSE) {
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);
625         }
626         
627         if(!(file_handle->fileaccess&GENERIC_READ) &&
628            !(file_handle->fileaccess&GENERIC_WRITE) &&
629            !(file_handle->fileaccess&GENERIC_ALL)) {
630 #ifdef DEBUG
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);
632 #endif
633
634                 SetLastError (ERROR_ACCESS_DENIED);
635                 return(INVALID_SET_FILE_POINTER);
636         }
637
638         switch(method) {
639         case FILE_BEGIN:
640                 whence=SEEK_SET;
641                 break;
642         case FILE_CURRENT:
643                 whence=SEEK_CUR;
644                 break;
645         case FILE_END:
646                 whence=SEEK_END;
647                 break;
648         default:
649 #ifdef DEBUG
650                 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
651                           method);
652 #endif
653
654                 SetLastError (ERROR_INVALID_PARAMETER);
655                 return(INVALID_SET_FILE_POINTER);
656         }
657
658 #ifdef HAVE_LARGE_FILE_SUPPORT
659         if(highmovedistance==NULL) {
660                 offset=movedistance;
661 #ifdef DEBUG
662                 g_message(G_GNUC_PRETTY_FUNCTION
663                           ": setting offset to %lld (low %d)", offset,
664                           movedistance);
665 #endif
666         } else {
667                 offset=((gint64) *highmovedistance << 32) | (unsigned long)movedistance;
668                 
669 #ifdef DEBUG
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);
671 #endif
672         }
673 #else
674         offset=movedistance;
675 #endif
676
677 #ifdef DEBUG
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);
682 #else
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);
686 #endif
687 #endif
688
689         newpos=lseek(file_private_handle->fd, offset, whence);
690         if(newpos==-1) {
691 #ifdef DEBUG
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));
695 #endif
696
697                 _wapi_set_last_error_from_errno ();
698                 return(INVALID_SET_FILE_POINTER);
699         }
700
701 #ifdef DEBUG
702 #ifdef HAVE_LARGE_FILE_SUPPORT
703         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
704 #else
705         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
706 #endif
707 #endif
708
709 #ifdef HAVE_LARGE_FILE_SUPPORT
710         ret=newpos & 0xFFFFFFFF;
711         if(highmovedistance!=NULL) {
712                 *highmovedistance=newpos>>32;
713         }
714 #else
715         ret=newpos;
716         if(highmovedistance!=NULL) {
717                 /* Accurate, but potentially dodgy :-) */
718                 *highmovedistance=0;
719         }
720 #endif
721
722 #ifdef DEBUG
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);
727 #endif
728
729         return(ret);
730 }
731
732 static gboolean file_setendoffile(gpointer handle)
733 {
734         struct _WapiHandle_file *file_handle;
735         struct _WapiHandlePrivate_file *file_private_handle;
736         gboolean ok;
737         struct stat statbuf;
738         off_t size, pos;
739         int ret;
740         
741         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
742                                 (gpointer *)&file_handle,
743                                 (gpointer *)&file_private_handle);
744         if(ok==FALSE) {
745                 g_warning (G_GNUC_PRETTY_FUNCTION
746                            ": error looking up file handle %p", handle);
747                 return(FALSE);
748         }
749         
750         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
751            !(file_handle->fileaccess&GENERIC_ALL)) {
752 #ifdef DEBUG
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);
754 #endif
755
756                 return(FALSE);
757         }
758
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.
763          */
764         
765         ret=fstat(file_private_handle->fd, &statbuf);
766         if(ret==-1) {
767 #ifdef DEBUG
768                 g_message(G_GNUC_PRETTY_FUNCTION
769                           ": handle %p fd %d fstat failed: %s", handle,
770                           file_private_handle->fd, strerror(errno));
771 #endif
772
773                 return(FALSE);
774         }
775         size=statbuf.st_size;
776
777         pos=lseek(file_private_handle->fd, (off_t)0, SEEK_CUR);
778         if(pos==-1) {
779 #ifdef DEBUG
780                 g_message(G_GNUC_PRETTY_FUNCTION
781                           ": handle %p fd %d lseek failed: %s", handle,
782                           file_private_handle->fd, strerror(errno));
783 #endif
784
785                 return(FALSE);
786         }
787         
788         if(pos>size) {
789                 /* extend */
790                 do {
791                         ret=write(file_private_handle->fd, "", 1);
792                 }
793                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
794
795                 if(ret==-1) {
796 #ifdef DEBUG
797                         g_message(G_GNUC_PRETTY_FUNCTION
798                                   ": handle %p fd %d extend write failed: %s",
799                                   handle, file_private_handle->fd,
800                                   strerror(errno));
801 #endif
802
803                         return(FALSE);
804                 }
805         }
806
807         /* always truncate, because the extend write() adds an extra
808          * byte to the end of the file
809          */
810         do {
811                 ret=ftruncate(file_private_handle->fd, pos);
812         }
813         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
814                         
815         if(ret==-1) {
816 #ifdef DEBUG
817                 g_message(G_GNUC_PRETTY_FUNCTION
818                           ": handle %p fd %d ftruncate failed: %s", handle,
819                           file_private_handle->fd, strerror(errno));
820 #endif
821                 
822                 return(FALSE);
823         }
824                 
825         return(TRUE);
826 }
827
828 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
829 {
830         struct _WapiHandle_file *file_handle;
831         struct _WapiHandlePrivate_file *file_private_handle;
832         gboolean ok;
833         struct stat statbuf;
834         guint32 size;
835         int ret;
836         
837         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
838                                 (gpointer *)&file_handle,
839                                 (gpointer *)&file_private_handle);
840         if(ok==FALSE) {
841                 g_warning (G_GNUC_PRETTY_FUNCTION
842                            ": error looking up file handle %p", handle);
843                 return(INVALID_FILE_SIZE);
844         }
845         
846         if(!(file_handle->fileaccess&GENERIC_READ) &&
847            !(file_handle->fileaccess&GENERIC_WRITE) &&
848            !(file_handle->fileaccess&GENERIC_ALL)) {
849 #ifdef DEBUG
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);
851 #endif
852
853                 return(INVALID_FILE_SIZE);
854         }
855
856         ret=fstat(file_private_handle->fd, &statbuf);
857         if(ret==-1) {
858 #ifdef DEBUG
859                 g_message(G_GNUC_PRETTY_FUNCTION
860                           ": handle %p fd %d fstat failed: %s", handle,
861                           file_private_handle->fd, strerror(errno));
862 #endif
863
864                 return(INVALID_FILE_SIZE);
865         }
866         
867 #ifdef HAVE_LARGE_FILE_SUPPORT
868         size=statbuf.st_size & 0xFFFFFFFF;
869         if(highsize!=NULL) {
870                 *highsize=statbuf.st_size>>32;
871         }
872 #else
873         if(highsize!=NULL) {
874                 /* Accurate, but potentially dodgy :-) */
875                 *highsize=0;
876         }
877         size=statbuf.st_size;
878 #endif
879
880 #ifdef DEBUG
881         g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
882                   *highsize);
883 #endif
884         
885         return(size);
886 }
887
888 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
889                                  WapiFileTime *last_access,
890                                  WapiFileTime *last_write)
891 {
892         struct _WapiHandle_file *file_handle;
893         struct _WapiHandlePrivate_file *file_private_handle;
894         gboolean ok;
895         struct stat statbuf;
896         guint64 create_ticks, access_ticks, write_ticks;
897         int ret;
898         
899         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
900                                 (gpointer *)&file_handle,
901                                 (gpointer *)&file_private_handle);
902         if(ok==FALSE) {
903                 g_warning (G_GNUC_PRETTY_FUNCTION
904                            ": error looking up file handle %p", handle);
905                 return(FALSE);
906         }
907         
908         if(!(file_handle->fileaccess&GENERIC_READ) &&
909            !(file_handle->fileaccess&GENERIC_ALL)) {
910 #ifdef DEBUG
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);
912 #endif
913
914                 return(FALSE);
915         }
916         
917         ret=fstat(file_private_handle->fd, &statbuf);
918         if(ret==-1) {
919 #ifdef DEBUG
920                 g_message(G_GNUC_PRETTY_FUNCTION
921                           ": handle %p fd %d fstat failed: %s", handle,
922                           file_private_handle->fd, strerror(errno));
923 #endif
924
925                 return(FALSE);
926         }
927
928 #ifdef DEBUG
929         g_message(G_GNUC_PRETTY_FUNCTION
930                   ": atime: %ld ctime: %ld mtime: %ld",
931                   statbuf.st_atime, statbuf.st_ctime,
932                   statbuf.st_mtime);
933 #endif
934
935         /* Try and guess a meaningful create time by using the older
936          * of atime or ctime
937          */
938         /* The magic constant comes from msdn documentation
939          * "Converting a time_t Value to a File Time"
940          */
941         if(statbuf.st_atime < statbuf.st_ctime) {
942                 create_ticks=((guint64)statbuf.st_atime*10000000)
943                         + 116444736000000000ULL;
944         } else {
945                 create_ticks=((guint64)statbuf.st_ctime*10000000)
946                         + 116444736000000000ULL;
947         }
948         
949         access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
950         write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
951         
952 #ifdef DEBUG
953                 g_message(G_GNUC_PRETTY_FUNCTION
954                           ": aticks: %llu cticks: %llu wticks: %llu",
955                           access_ticks, create_ticks, write_ticks);
956 #endif
957
958         if(create_time!=NULL) {
959                 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
960                 create_time->dwHighDateTime = create_ticks >> 32;
961         }
962         
963         if(last_access!=NULL) {
964                 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
965                 last_access->dwHighDateTime = access_ticks >> 32;
966         }
967         
968         if(last_write!=NULL) {
969                 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
970                 last_write->dwHighDateTime = write_ticks >> 32;
971         }
972
973         return(TRUE);
974 }
975
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)
980 {
981         struct _WapiHandle_file *file_handle;
982         struct _WapiHandlePrivate_file *file_private_handle;
983         gboolean ok;
984         guchar *name;
985         struct utimbuf utbuf;
986         struct stat statbuf;
987         guint64 access_ticks, write_ticks;
988         int ret;
989         
990         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
991                                 (gpointer *)&file_handle,
992                                 (gpointer *)&file_private_handle);
993         if(ok==FALSE) {
994                 g_warning (G_GNUC_PRETTY_FUNCTION
995                            ": error looking up file handle %p", handle);
996                 SetLastError (ERROR_INVALID_HANDLE);
997                 return(FALSE);
998         }
999         
1000         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
1001            !(file_handle->fileaccess&GENERIC_ALL)) {
1002 #ifdef DEBUG
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);
1004 #endif
1005
1006                 SetLastError (ERROR_ACCESS_DENIED);
1007                 return(FALSE);
1008         }
1009
1010         if(file_handle->filename==0) {
1011 #ifdef DEBUG
1012                 g_message(G_GNUC_PRETTY_FUNCTION
1013                           ": handle %p fd %d unknown filename", handle,
1014                           file_private_handle->fd);
1015 #endif
1016
1017                 SetLastError (ERROR_INVALID_HANDLE);
1018                 return(FALSE);
1019         }
1020         
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
1023          */
1024         ret=fstat(file_private_handle->fd, &statbuf);
1025         if(ret==-1) {
1026 #ifdef DEBUG
1027                 g_message(G_GNUC_PRETTY_FUNCTION
1028                           ": handle %p fd %d fstat failed: %s", handle,
1029                           file_private_handle->fd, strerror(errno));
1030 #endif
1031
1032                 SetLastError (ERROR_INVALID_PARAMETER);
1033                 return(FALSE);
1034         }
1035
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.
1041                  */
1042                 if (access_ticks < 116444736000000000ULL) {
1043 #ifdef DEBUG
1044                         g_message (G_GNUC_PRETTY_FUNCTION
1045                                    ": attempt to set access time too early");
1046 #endif
1047                         SetLastError (ERROR_INVALID_PARAMETER);
1048                         return(FALSE);
1049                 }
1050                 
1051                 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1052         } else {
1053                 utbuf.actime=statbuf.st_atime;
1054         }
1055
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.
1061                  */
1062                 if (write_ticks < 116444736000000000ULL) {
1063 #ifdef DEBUG
1064                         g_message (G_GNUC_PRETTY_FUNCTION
1065                                    ": attempt to set write time too early");
1066 #endif
1067                         SetLastError (ERROR_INVALID_PARAMETER);
1068                         return(FALSE);
1069                 }
1070                 
1071                 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1072         } else {
1073                 utbuf.modtime=statbuf.st_mtime;
1074         }
1075
1076 #ifdef DEBUG
1077         g_message(G_GNUC_PRETTY_FUNCTION
1078                   ": setting handle %p access %ld write %ld", handle,
1079                   utbuf.actime, utbuf.modtime);
1080 #endif
1081
1082         name=_wapi_handle_scratch_lookup (file_handle->filename);
1083
1084         ret=utime(name, &utbuf);
1085         if(ret==-1) {
1086 #ifdef DEBUG
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));
1090
1091 #endif
1092                 g_free (name);
1093                 SetLastError (ERROR_INVALID_PARAMETER);
1094                 return(FALSE);
1095         }
1096
1097         g_free (name);
1098         
1099         return(TRUE);
1100 }
1101
1102 static void console_close_shared (gpointer handle)
1103 {
1104         struct _WapiHandle_file *console_handle;
1105         gboolean ok;
1106         
1107         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1108                                 (gpointer *)&console_handle, NULL);
1109         if(ok==FALSE) {
1110                 g_warning (G_GNUC_PRETTY_FUNCTION
1111                            ": error looking up console handle %p", handle);
1112                 return;
1113         }
1114         
1115 #ifdef DEBUG
1116         g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
1117 #endif
1118         
1119         if(console_handle->filename!=0) {
1120                 _wapi_handle_scratch_delete (console_handle->filename);
1121                 console_handle->filename=0;
1122         }
1123         if(console_handle->security_attributes!=0) {
1124                 _wapi_handle_scratch_delete (console_handle->security_attributes);
1125                 console_handle->security_attributes=0;
1126         }
1127 }
1128
1129 static void console_close_private (gpointer handle)
1130 {
1131         struct _WapiHandlePrivate_file *console_private_handle;
1132         gboolean ok;
1133         
1134         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
1135                                 (gpointer *)&console_private_handle);
1136         if(ok==FALSE) {
1137                 g_warning (G_GNUC_PRETTY_FUNCTION
1138                            ": error looking up console handle %p", handle);
1139                 return;
1140         }
1141         
1142 #ifdef DEBUG
1143         g_message(G_GNUC_PRETTY_FUNCTION
1144                   ": closing console handle %p with fd %d", handle,
1145                   console_private_handle->fd);
1146 #endif
1147         
1148         /* Blank out the mapping, to make catching errors easier */
1149         _wapi_handle_fd_offset_store (console_private_handle->fd, NULL);
1150         
1151         close(console_private_handle->fd);
1152 }
1153
1154 static WapiFileType console_getfiletype(void)
1155 {
1156         return(FILE_TYPE_CHAR);
1157 }
1158
1159 static gboolean console_read(gpointer handle, gpointer buffer,
1160                              guint32 numbytes, guint32 *bytesread,
1161                              WapiOverlapped *overlapped G_GNUC_UNUSED)
1162 {
1163         struct _WapiHandle_file *console_handle;
1164         struct _WapiHandlePrivate_file *console_private_handle;
1165         gboolean ok;
1166         int ret;
1167         
1168         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1169                                 (gpointer *)&console_handle,
1170                                 (gpointer *)&console_private_handle);
1171         if(ok==FALSE) {
1172                 g_warning (G_GNUC_PRETTY_FUNCTION
1173                            ": error looking up console handle %p", handle);
1174                 return(FALSE);
1175         }
1176         
1177         if(bytesread!=NULL) {
1178                 *bytesread=0;
1179         }
1180         
1181         if(!(console_handle->fileaccess&GENERIC_READ) &&
1182            !(console_handle->fileaccess&GENERIC_ALL)) {
1183 #ifdef DEBUG
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);
1185 #endif
1186
1187                 return(FALSE);
1188         }
1189         
1190         do {
1191                 ret=read(console_private_handle->fd, buffer, numbytes);
1192         }
1193         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1194
1195         if(ret==-1) {
1196 #ifdef DEBUG
1197                 g_message(G_GNUC_PRETTY_FUNCTION
1198                           ": read of handle %p fd %d error: %s", handle,
1199                           console_private_handle->fd, strerror(errno));
1200 #endif
1201
1202                 return(FALSE);
1203         }
1204         
1205         if(bytesread!=NULL) {
1206                 *bytesread=ret;
1207         }
1208         
1209         return(TRUE);
1210 }
1211
1212 static gboolean console_write(gpointer handle, gconstpointer buffer,
1213                               guint32 numbytes, guint32 *byteswritten,
1214                               WapiOverlapped *overlapped G_GNUC_UNUSED)
1215 {
1216         struct _WapiHandle_file *console_handle;
1217         struct _WapiHandlePrivate_file *console_private_handle;
1218         gboolean ok;
1219         int ret;
1220         
1221         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1222                                 (gpointer *)&console_handle,
1223                                 (gpointer *)&console_private_handle);
1224         if(ok==FALSE) {
1225                 g_warning (G_GNUC_PRETTY_FUNCTION
1226                            ": error looking up console handle %p", handle);
1227                 return(FALSE);
1228         }
1229         
1230         if(byteswritten!=NULL) {
1231                 *byteswritten=0;
1232         }
1233         
1234         if(!(console_handle->fileaccess&GENERIC_WRITE) &&
1235            !(console_handle->fileaccess&GENERIC_ALL)) {
1236 #ifdef DEBUG
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);
1238 #endif
1239
1240                 return(FALSE);
1241         }
1242         
1243         do {
1244                 ret=write(console_private_handle->fd, buffer, numbytes);
1245         }
1246         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1247
1248         if(ret==-1) {
1249 #ifdef DEBUG
1250                 g_message(G_GNUC_PRETTY_FUNCTION
1251                           ": write of handle %p fd %d error: %s", handle,
1252                           console_private_handle->fd, strerror(errno));
1253 #endif
1254
1255                 return(FALSE);
1256         }
1257         if(byteswritten!=NULL) {
1258                 *byteswritten=ret;
1259         }
1260         
1261         return(TRUE);
1262 }
1263
1264 static void pipe_close_shared (gpointer handle)
1265 {
1266         struct _WapiHandle_file *pipe_handle;
1267         gboolean ok;
1268         
1269         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1270                                 (gpointer *)&pipe_handle, NULL);
1271         if(ok==FALSE) {
1272                 g_warning (G_GNUC_PRETTY_FUNCTION
1273                            ": error looking up pipe handle %p", handle);
1274                 return;
1275         }
1276         
1277 #ifdef DEBUG
1278         g_message(G_GNUC_PRETTY_FUNCTION ": closing pipe handle %p", handle);
1279 #endif
1280         
1281         if(pipe_handle->filename!=0) {
1282                 _wapi_handle_scratch_delete (pipe_handle->filename);
1283                 pipe_handle->filename=0;
1284         }
1285         if(pipe_handle->security_attributes!=0) {
1286                 _wapi_handle_scratch_delete (pipe_handle->security_attributes);
1287                 pipe_handle->security_attributes=0;
1288         }
1289 }
1290
1291 static void pipe_close_private (gpointer handle)
1292 {
1293         struct _WapiHandlePrivate_file *pipe_private_handle;
1294         gboolean ok;
1295         
1296         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
1297                                 (gpointer *)&pipe_private_handle);
1298         if(ok==FALSE) {
1299                 g_warning (G_GNUC_PRETTY_FUNCTION
1300                            ": error looking up pipe handle %p", handle);
1301                 return;
1302         }
1303         
1304 #ifdef DEBUG
1305         g_message(G_GNUC_PRETTY_FUNCTION
1306                   ": closing pipe handle %p with fd %d", handle,
1307                   pipe_private_handle->fd);
1308 #endif
1309         
1310         /* Blank out the mapping, to make catching errors easier */
1311         _wapi_handle_fd_offset_store (pipe_private_handle->fd, NULL);
1312         
1313         close(pipe_private_handle->fd);
1314 }
1315
1316 static WapiFileType pipe_getfiletype(void)
1317 {
1318         return(FILE_TYPE_PIPE);
1319 }
1320
1321 static gboolean pipe_read (gpointer handle, gpointer buffer,
1322                            guint32 numbytes, guint32 *bytesread,
1323                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1324 {
1325         struct _WapiHandle_file *pipe_handle;
1326         struct _WapiHandlePrivate_file *pipe_private_handle;
1327         gboolean ok;
1328         int ret;
1329         
1330         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1331                                 (gpointer *)&pipe_handle,
1332                                 (gpointer *)&pipe_private_handle);
1333         if(ok==FALSE) {
1334                 g_warning (G_GNUC_PRETTY_FUNCTION
1335                            ": error looking up pipe handle %p", handle);
1336                 return(FALSE);
1337         }
1338         
1339         if(bytesread!=NULL) {
1340                 *bytesread=0;
1341         }
1342         
1343         if(!(pipe_handle->fileaccess&GENERIC_READ) &&
1344            !(pipe_handle->fileaccess&GENERIC_ALL)) {
1345 #ifdef DEBUG
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);
1347 #endif
1348
1349                 return(FALSE);
1350         }
1351         
1352 #ifdef DEBUG
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);
1356 #endif
1357
1358         do {
1359                 ret=read(pipe_private_handle->fd, buffer, numbytes);
1360         }
1361         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1362                 
1363         if(ret==-1) {
1364 #ifdef DEBUG
1365                 g_message(G_GNUC_PRETTY_FUNCTION
1366                           ": read of handle %p fd %d error: %s", handle,
1367                           pipe_private_handle->fd, strerror(errno));
1368 #endif
1369
1370                 return(FALSE);
1371         }
1372         
1373 #ifdef DEBUG
1374         g_message (G_GNUC_PRETTY_FUNCTION ": read %d bytes from pipe", ret);
1375 #endif
1376
1377         if(bytesread!=NULL) {
1378                 *bytesread=ret;
1379         }
1380         
1381         return(TRUE);
1382 }
1383
1384 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1385                            guint32 numbytes, guint32 *byteswritten,
1386                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1387 {
1388         struct _WapiHandle_file *pipe_handle;
1389         struct _WapiHandlePrivate_file *pipe_private_handle;
1390         gboolean ok;
1391         int ret;
1392         
1393         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1394                                 (gpointer *)&pipe_handle,
1395                                 (gpointer *)&pipe_private_handle);
1396         if(ok==FALSE) {
1397                 g_warning (G_GNUC_PRETTY_FUNCTION
1398                            ": error looking up pipe handle %p", handle);
1399                 return(FALSE);
1400         }
1401         
1402         if(byteswritten!=NULL) {
1403                 *byteswritten=0;
1404         }
1405         
1406         if(!(pipe_handle->fileaccess&GENERIC_WRITE) &&
1407            !(pipe_handle->fileaccess&GENERIC_ALL)) {
1408 #ifdef DEBUG
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);
1410 #endif
1411
1412                 return(FALSE);
1413         }
1414         
1415 #ifdef DEBUG
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);
1419 #endif
1420
1421         do {
1422                 ret=write(pipe_private_handle->fd, buffer, numbytes);
1423         }
1424         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1425
1426         if(ret==-1) {
1427 #ifdef DEBUG
1428                 g_message(G_GNUC_PRETTY_FUNCTION
1429                           ": write of handle %p fd %d error: %s", handle,
1430                           pipe_private_handle->fd, strerror(errno));
1431 #endif
1432
1433                 return(FALSE);
1434         }
1435         if(byteswritten!=NULL) {
1436                 *byteswritten=ret;
1437         }
1438         
1439         return(TRUE);
1440 }
1441
1442 static int convert_flags(guint32 fileaccess, guint32 createmode)
1443 {
1444         int flags=0;
1445         
1446         switch(fileaccess) {
1447         case GENERIC_READ:
1448                 flags=O_RDONLY;
1449                 break;
1450         case GENERIC_WRITE:
1451                 flags=O_WRONLY;
1452                 break;
1453         case GENERIC_READ|GENERIC_WRITE:
1454                 flags=O_RDWR;
1455                 break;
1456         default:
1457 #ifdef DEBUG
1458                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
1459                           fileaccess);
1460 #endif
1461                 break;
1462         }
1463
1464         switch(createmode) {
1465         case CREATE_NEW:
1466                 flags|=O_CREAT|O_EXCL;
1467                 break;
1468         case CREATE_ALWAYS:
1469                 flags|=O_CREAT|O_TRUNC;
1470                 break;
1471         case OPEN_EXISTING:
1472                 break;
1473         case OPEN_ALWAYS:
1474                 flags|=O_CREAT;
1475                 break;
1476         case TRUNCATE_EXISTING:
1477                 flags|=O_TRUNC;
1478                 break;
1479         default:
1480 #ifdef DEBUG
1481                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
1482                           createmode);
1483 #endif
1484                 break;
1485         }
1486         
1487         return(flags);
1488 }
1489
1490 static guint32 convert_from_flags(int flags)
1491 {
1492         guint32 fileaccess=0;
1493         
1494 #ifndef O_ACCMODE
1495 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1496 #endif
1497
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;
1504         } else {
1505 #ifdef DEBUG
1506                 g_message(G_GNUC_PRETTY_FUNCTION
1507                           ": Can't figure out flags 0x%x", flags);
1508 #endif
1509         }
1510
1511         /* Maybe sort out create mode too */
1512
1513         return(fileaccess);
1514 }
1515
1516 static mode_t convert_perms(guint32 sharemode)
1517 {
1518         mode_t perms=0600;
1519         
1520         if(sharemode&FILE_SHARE_READ) {
1521                 perms|=044;
1522         }
1523         if(sharemode&FILE_SHARE_WRITE) {
1524                 perms|=022;
1525         }
1526
1527         return(perms);
1528 }
1529
1530
1531 /**
1532  * CreateFile:
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.
1549  *
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().
1553  *
1554  * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1555  */
1556 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1557                     guint32 sharemode, WapiSecurityAttributes *security,
1558                     guint32 createmode, guint32 attrs,
1559                     gpointer template G_GNUC_UNUSED)
1560 {
1561         struct _WapiHandle_file *file_handle;
1562         struct _WapiHandlePrivate_file *file_private_handle;
1563         gpointer handle;
1564         gboolean ok;
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.
1570          */
1571         mode_t perms=0644;
1572         gchar *filename;
1573         int fd, ret;
1574         int thr_ret;
1575         gpointer cf_ret = INVALID_HANDLE_VALUE;
1576         struct stat statbuf;
1577         gboolean file_already_shared;
1578         guint32 file_existing_share, file_existing_access;
1579         
1580         mono_once (&io_ops_once, io_ops_init);
1581
1582         if(name==NULL) {
1583 #ifdef DEBUG
1584                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1585 #endif
1586
1587                 return(INVALID_HANDLE_VALUE);
1588         }
1589
1590         filename=mono_unicode_to_external (name);
1591         if(filename==NULL) {
1592 #ifdef DEBUG
1593                 g_message(G_GNUC_PRETTY_FUNCTION
1594                           ": unicode conversion returned NULL");
1595 #endif
1596
1597                 return(INVALID_HANDLE_VALUE);
1598         }
1599         
1600 #ifdef DEBUG
1601         g_message (G_GNUC_PRETTY_FUNCTION ": Opening %s with share 0x%x and access 0x%x", filename, sharemode, fileaccess);
1602 #endif
1603         
1604         fd = open(filename, flags, perms);
1605     
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.
1613          */
1614         if (fd == -1 && errno == EISDIR)
1615         {
1616                 /* Try again but don't try to make it writable */
1617                 fd = open(filename, flags  & ~(O_RDWR|O_WRONLY), perms);
1618         }
1619         
1620         if (fd == -1) {
1621 #ifdef DEBUG
1622                 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
1623                           filename, strerror(errno));
1624 #endif
1625                 _wapi_set_last_error_from_errno ();
1626                 g_free (filename);
1627
1628                 return(INVALID_HANDLE_VALUE);
1629         }
1630
1631         if (fd >= _wapi_fd_offset_table_size) {
1632 #ifdef DEBUG
1633                 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
1634 #endif
1635
1636                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1637                 
1638                 close (fd);
1639                 g_free (filename);
1640                 
1641                 return(INVALID_HANDLE_VALUE);
1642         }
1643
1644         ret = fstat (fd, &statbuf);
1645         if (ret == -1) {
1646 #ifdef DEBUG
1647                 g_message (G_GNUC_PRETTY_FUNCTION ": fstat error of file %s: %s", filename, strerror (errno));
1648 #endif
1649                 _wapi_set_last_error_from_errno ();
1650                 g_free (filename);
1651                 close (fd);
1652                 
1653                 return(INVALID_HANDLE_VALUE);
1654         }
1655
1656         file_already_shared = _wapi_handle_get_or_set_share (statbuf.st_dev, statbuf.st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access);
1657         
1658         if (file_already_shared) {
1659                 if (file_existing_share == 0) {
1660                         /* Quick and easy, no possibility to share */
1661 #ifdef DEBUG
1662                         g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing = NONE", fileaccess);
1663 #endif
1664                         SetLastError (ERROR_SHARING_VIOLATION);
1665                         g_free (filename);
1666                         close (fd);
1667                 
1668                         return(INVALID_HANDLE_VALUE);
1669                 }
1670
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 */
1676 #ifdef DEBUG
1677                         g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", fileaccess, file_existing_share);
1678 #endif
1679                         SetLastError (ERROR_SHARING_VIOLATION);
1680                         g_free (filename);
1681                         close (fd);
1682                 
1683                         return(INVALID_HANDLE_VALUE);
1684                 }
1685
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 */
1691 #ifdef DEBUG
1692                         g_message (G_GNUC_PRETTY_FUNCTION ": Access mode prevents open: requested share: 0x%x, file has access: 0x%x", sharemode, file_existing_access);
1693 #endif
1694                         SetLastError (ERROR_SHARING_VIOLATION);
1695                         g_free (filename);
1696                         close (fd);
1697                 
1698                         return(INVALID_HANDLE_VALUE);
1699                 }
1700         } else {
1701 #ifdef DEBUG
1702                 g_message (G_GNUC_PRETTY_FUNCTION ": New file!");
1703 #endif
1704         }
1705         
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");
1710                 g_free (filename);
1711                 close (fd);
1712                 
1713                 return(INVALID_HANDLE_VALUE);
1714         }
1715
1716         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
1717                               handle);
1718         thr_ret = _wapi_handle_lock_handle (handle);
1719         g_assert (thr_ret == 0);
1720         
1721         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1722                                 (gpointer *)&file_handle,
1723                                 (gpointer *)&file_private_handle);
1724         if(ok==FALSE) {
1725                 g_warning (G_GNUC_PRETTY_FUNCTION
1726                            ": error looking up file handle %p", handle);
1727                 close (fd);
1728                 goto cleanup;
1729         }
1730
1731         _wapi_handle_fd_offset_store (fd, handle);
1732         cf_ret = GINT_TO_POINTER (fd);
1733
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,
1738                                                           strlen (filename));
1739         if(security!=NULL) {
1740                 file_handle->security_attributes=_wapi_handle_scratch_store (
1741                         security, sizeof(WapiSecurityAttributes));
1742         }
1743         
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;
1749         
1750 #ifdef DEBUG
1751         g_message(G_GNUC_PRETTY_FUNCTION
1752                   ": returning handle %p with fd %d", handle,
1753                   file_private_handle->fd);
1754 #endif
1755
1756 cleanup:
1757         thr_ret = _wapi_handle_unlock_handle (handle);
1758         g_assert (thr_ret == 0);
1759         pthread_cleanup_pop (0);
1760         
1761         g_free (filename);
1762         
1763         return(cf_ret);
1764 }
1765
1766 /**
1767  * DeleteFile:
1768  * @name: a pointer to a NULL-terminated unicode string, that names
1769  * the file to be deleted.
1770  *
1771  * Deletes file @name.
1772  *
1773  * Return value: %TRUE on success, %FALSE otherwise.
1774  */
1775 gboolean DeleteFile(const gunichar2 *name)
1776 {
1777         gchar *filename;
1778         int ret;
1779         
1780         if(name==NULL) {
1781 #ifdef DEBUG
1782                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1783 #endif
1784
1785                 return(FALSE);
1786         }
1787
1788         filename=mono_unicode_to_external(name);
1789         if(filename==NULL) {
1790 #ifdef DEBUG
1791                 g_message(G_GNUC_PRETTY_FUNCTION
1792                           ": unicode conversion returned NULL");
1793 #endif
1794
1795                 return(FALSE);
1796         }
1797         
1798         ret=unlink(filename);
1799         
1800         g_free(filename);
1801
1802         if(ret==0) {
1803                 return(TRUE);
1804         }
1805
1806         _wapi_set_last_error_from_errno ();
1807         return(FALSE);
1808 }
1809
1810 /**
1811  * MoveFile:
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.
1816  *
1817  * Renames file @name to @dest_name
1818  *
1819  * Return value: %TRUE on success, %FALSE otherwise.
1820  */
1821 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1822 {
1823         gchar *utf8_name, *utf8_dest_name;
1824         int result;
1825
1826         utf8_name = mono_unicode_to_external (name);
1827         if (utf8_name == NULL) {
1828 #ifdef DEBUG
1829                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1830 #endif
1831                 
1832                 return FALSE;
1833         }
1834
1835         utf8_dest_name = mono_unicode_to_external (dest_name);
1836         if (utf8_dest_name == NULL) {
1837 #ifdef DEBUG
1838                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1839 #endif
1840
1841                 g_free (utf8_name);
1842                 return FALSE;
1843         }
1844
1845         result = rename (utf8_name, utf8_dest_name);
1846         g_free (utf8_name);
1847         g_free (utf8_dest_name);
1848
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) {
1852                         return(FALSE);
1853                 }
1854                 
1855                 return(DeleteFile (name));
1856         }
1857
1858         if (result == 0) {
1859                 return TRUE;
1860         }
1861         
1862         switch (errno) {
1863         case EEXIST:
1864                 SetLastError (ERROR_ALREADY_EXISTS);
1865                 break;
1866         
1867         default:
1868                 _wapi_set_last_error_from_errno ();
1869                 break;
1870         }
1871
1872         return FALSE;
1873 }
1874
1875 /**
1876  * CopyFile:
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.
1882  *
1883  * Copies file @name to @dest_name
1884  *
1885  * Return value: %TRUE on success, %FALSE otherwise.
1886  */
1887 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
1888                    gboolean fail_if_exists)
1889 {
1890         gchar *utf8_src, *utf8_dest;
1891         int src_fd, dest_fd;
1892         int buf_size;
1893         char *buf;
1894         int remain, n;
1895         struct stat st;
1896         
1897         utf8_src = mono_unicode_to_external (name);
1898         if (utf8_src == NULL) {
1899 #ifdef DEBUG
1900                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of source returned NULL");
1901 #endif
1902
1903                 SetLastError (ERROR_INVALID_PARAMETER);
1904                 return(FALSE);
1905         }
1906         
1907         utf8_dest = mono_unicode_to_external (dest_name);
1908         if (utf8_dest == NULL) {
1909 #ifdef DEBUG
1910                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of dest returned NULL");
1911 #endif
1912
1913                 SetLastError (ERROR_INVALID_PARAMETER);
1914
1915                 g_free (utf8_src);
1916                 
1917                 return(FALSE);
1918         }
1919         
1920         src_fd = open (utf8_src, O_RDONLY);
1921         if (src_fd < 0) {
1922                 _wapi_set_last_error_from_errno ();
1923
1924                 g_free (utf8_src);
1925                 g_free (utf8_dest);
1926                 
1927                 return(FALSE);
1928         }
1929
1930         if (fstat (src_fd, &st) < 0) {
1931                 _wapi_set_last_error_from_errno ();
1932
1933                 g_free (utf8_src);
1934                 g_free (utf8_dest);
1935                 close (src_fd);
1936                 
1937                 return(FALSE);
1938         }
1939         
1940         if (fail_if_exists) {
1941                 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
1942         } else {
1943                 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
1944                 if (dest_fd < 0) {
1945                         /* O_TRUNC might cause a fail if the file
1946                          * doesn't exist
1947                          */
1948                         dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
1949                                         st.st_mode);
1950                 }
1951         }
1952         if (dest_fd < 0) {
1953                 _wapi_set_last_error_from_errno ();
1954
1955                 g_free (utf8_src);
1956                 g_free (utf8_dest);
1957                 close (src_fd);
1958                 
1959                 return(FALSE);
1960         }
1961         
1962         buf_size = st.st_blksize;
1963         buf = (char *) alloca (buf_size);
1964         
1965         for (;;) {
1966                 remain = read (src_fd, buf, buf_size);
1967                 
1968                 if (remain < 0) {
1969                         if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
1970                                 continue;
1971                         }
1972                         
1973                         _wapi_set_last_error_from_errno ();
1974
1975                         g_free (utf8_src);
1976                         g_free (utf8_dest);
1977                         close (src_fd);
1978                         close (dest_fd);
1979                         
1980                         return(FALSE);
1981                 }
1982                 
1983                 if (remain == 0) {
1984                         break;
1985                 }
1986
1987                 while (remain > 0) {
1988                         if ((n = write (dest_fd, buf, remain)) < 0) {
1989                                 if (errno == EINTR && !_wapi_thread_cur_apc_pending())
1990                                         continue;
1991
1992                                 _wapi_set_last_error_from_errno ();
1993 #ifdef DEBUG
1994                                 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
1995 #endif
1996
1997                                 g_free (utf8_src);
1998                                 g_free (utf8_dest);
1999                                 close (src_fd);
2000                                 close (dest_fd);
2001
2002                                 return (FALSE);
2003                         }
2004
2005                         remain -= n;
2006                 }
2007         }
2008
2009         g_free (utf8_src);
2010         g_free (utf8_dest);
2011         close (src_fd);
2012         close (dest_fd);
2013
2014         return(TRUE);
2015 }
2016
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;
2021
2022 static gpointer stdhandle_create (int fd, const guchar *name)
2023 {
2024         struct _WapiHandle_file *file_handle;
2025         struct _WapiHandlePrivate_file *file_private_handle;
2026         gboolean ok;
2027         gpointer handle, ret = INVALID_HANDLE_VALUE;
2028         int flags;
2029         int thr_ret;
2030         
2031 #ifdef DEBUG
2032         g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
2033                   name);
2034 #endif
2035         
2036         /* Check if fd is valid */
2037         do {
2038                 flags=fcntl(fd, F_GETFL);
2039         }
2040         while (flags==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
2041
2042         if(flags==-1) {
2043                 /* Invalid fd.  Not really much point checking for EBADF
2044                  * specifically
2045                  */
2046 #ifdef DEBUG
2047                 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
2048                           fd, strerror(errno));
2049 #endif
2050
2051                 return(INVALID_HANDLE_VALUE);
2052         }
2053
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);
2059         }
2060
2061         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2062                               handle);
2063         thr_ret = _wapi_handle_lock_handle (handle);
2064         g_assert (thr_ret == 0);
2065         
2066         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2067                                 (gpointer *)&file_handle,
2068                                 (gpointer *)&file_private_handle);
2069         if(ok==FALSE) {
2070                 g_warning (G_GNUC_PRETTY_FUNCTION
2071                            ": error looking up console handle %p", handle);
2072                 goto cleanup;
2073         }
2074
2075         /* We know this is fd 0, 1 or 2 */
2076         _wapi_handle_fd_offset_store (fd, handle);
2077         ret = GINT_TO_POINTER (fd);
2078         
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;
2087         
2088 #ifdef DEBUG
2089         g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
2090                   handle, file_private_handle->fd);
2091 #endif
2092
2093 cleanup:
2094         thr_ret = _wapi_handle_unlock_handle (handle);
2095         g_assert (thr_ret == 0);
2096         pthread_cleanup_pop (0);
2097
2098         return(ret);
2099 }
2100
2101 static void stdhandle_init (void)
2102 {
2103         stdin_handle=stdhandle_create (0, "<stdin>");
2104         stdout_handle=stdhandle_create (1, "<stdout>");
2105         stderr_handle=stdhandle_create (2, "<stderr>");
2106 }
2107
2108 /**
2109  * GetStdHandle:
2110  * @stdhandle: specifies the file descriptor
2111  *
2112  * Returns a handle for stdin, stdout, or stderr.  Always returns the
2113  * same handle for the same @stdhandle.
2114  *
2115  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2116  */
2117
2118 gpointer GetStdHandle(WapiStdHandle stdhandle)
2119 {
2120         gpointer handle;
2121         
2122         mono_once (&io_ops_once, io_ops_init);
2123         mono_once (&stdhandle_once, stdhandle_init);
2124         
2125         switch(stdhandle) {
2126         case STD_INPUT_HANDLE:
2127                 handle=stdin_handle;
2128                 break;
2129
2130         case STD_OUTPUT_HANDLE:
2131                 handle=stdout_handle;
2132                 break;
2133
2134         case STD_ERROR_HANDLE:
2135                 handle=stderr_handle;
2136                 break;
2137
2138         default:
2139 #ifdef DEBUG
2140                 g_message(G_GNUC_PRETTY_FUNCTION
2141                           ": unknown standard handle type");
2142 #endif
2143
2144                 return(INVALID_HANDLE_VALUE);
2145         }
2146
2147         if (handle == INVALID_HANDLE_VALUE) {
2148                 SetLastError (ERROR_NO_MORE_FILES);
2149                 return(INVALID_HANDLE_VALUE);
2150         }
2151         
2152         /* Add a reference to this handle */
2153         _wapi_handle_ref (_wapi_handle_fd_offset_to_handle (handle));
2154         
2155         return(handle);
2156 }
2157
2158 /**
2159  * ReadFile:
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
2166  * file.
2167  * @overlapped: points to a required %WapiOverlapped structure if
2168  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2169  * otherwise.
2170  *
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.
2176
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.
2180  *
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
2183  * error.
2184  */
2185 gboolean ReadFile(gpointer fd_handle, gpointer buffer, guint32 numbytes,
2186                   guint32 *bytesread, WapiOverlapped *overlapped)
2187 {
2188         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2189         WapiHandleType type;
2190
2191         if (handle == NULL) {
2192                 SetLastError (ERROR_INVALID_HANDLE);
2193                 return(FALSE);
2194         }
2195         
2196         type = _wapi_handle_type (handle);
2197         
2198         if(io_ops[type].readfile==NULL) {
2199                 SetLastError (ERROR_INVALID_HANDLE);
2200                 return(FALSE);
2201         }
2202         
2203         return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2204                                       overlapped));
2205 }
2206
2207 /**
2208  * WriteFile:
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
2218  * otherwise.
2219  *
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.
2225  *
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.
2229  *
2230  * Return value: %TRUE if the write succeeds, %FALSE on error.
2231  */
2232 gboolean WriteFile(gpointer fd_handle, gconstpointer buffer, guint32 numbytes,
2233                    guint32 *byteswritten, WapiOverlapped *overlapped)
2234 {
2235         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2236         WapiHandleType type;
2237
2238         if (handle == NULL) {
2239                 SetLastError (ERROR_INVALID_HANDLE);
2240                 return(FALSE);
2241         }
2242         
2243         type = _wapi_handle_type (handle);
2244         
2245         if(io_ops[type].writefile==NULL) {
2246                 SetLastError (ERROR_INVALID_HANDLE);
2247                 return(FALSE);
2248         }
2249         
2250         return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2251                                        overlapped));
2252 }
2253
2254 /**
2255  * FlushFileBuffers:
2256  * @handle: Handle to open file.  The handle must have
2257  * %GENERIC_WRITE access.
2258  *
2259  * Flushes buffers of the file and causes all unwritten data to
2260  * be written.
2261  *
2262  * Return value: %TRUE on success, %FALSE otherwise.
2263  */
2264 gboolean FlushFileBuffers(gpointer fd_handle)
2265 {
2266         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2267         WapiHandleType type;
2268
2269         if (handle == NULL) {
2270                 SetLastError (ERROR_INVALID_HANDLE);
2271                 return(FALSE);
2272         }
2273         
2274         type = _wapi_handle_type (handle);
2275         
2276         if(io_ops[type].flushfile==NULL) {
2277                 SetLastError (ERROR_INVALID_HANDLE);
2278                 return(FALSE);
2279         }
2280         
2281         return(io_ops[type].flushfile (handle));
2282 }
2283
2284 /**
2285  * SetEndOfFile:
2286  * @handle: The file handle to set.  The handle must have
2287  * %GENERIC_WRITE access.
2288  *
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.
2291  *
2292  * Return value: %TRUE on success, %FALSE otherwise.
2293  */
2294 gboolean SetEndOfFile(gpointer fd_handle)
2295 {
2296         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2297         WapiHandleType type;
2298
2299         if (handle == NULL) {
2300                 SetLastError (ERROR_INVALID_HANDLE);
2301                 return(FALSE);
2302         }
2303         
2304         type = _wapi_handle_type (handle);
2305         
2306         if(io_ops[type].setendoffile==NULL) {
2307                 SetLastError (ERROR_INVALID_HANDLE);
2308                 return(FALSE);
2309         }
2310         
2311         return(io_ops[type].setendoffile (handle));
2312 }
2313
2314 /**
2315  * SetFilePointer:
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
2322  * %NULL.
2323  * @method: The starting point for the file pointer move.
2324  *
2325  * Sets the file pointer of an open file.
2326  *
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.
2334  *
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.
2338  *
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.
2342  */
2343 guint32 SetFilePointer(gpointer fd_handle, gint32 movedistance,
2344                        gint32 *highmovedistance, WapiSeekMethod method)
2345 {
2346         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2347         WapiHandleType type;
2348
2349         if (handle == NULL) {
2350                 SetLastError (ERROR_INVALID_HANDLE);
2351                 return(INVALID_SET_FILE_POINTER);
2352         }
2353         
2354         type = _wapi_handle_type (handle);
2355         
2356         if(io_ops[type].seek==NULL) {
2357                 SetLastError (ERROR_INVALID_HANDLE);
2358                 return(INVALID_SET_FILE_POINTER);
2359         }
2360         
2361         return(io_ops[type].seek (handle, movedistance, highmovedistance,
2362                                   method));
2363 }
2364
2365 /**
2366  * GetFileType:
2367  * @handle: The file handle to test.
2368  *
2369  * Finds the type of file @handle.
2370  *
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.
2375  */
2376 WapiFileType GetFileType(gpointer fd_handle)
2377 {
2378         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2379         WapiHandleType type;
2380
2381         if (handle == NULL) {
2382                 SetLastError (ERROR_INVALID_HANDLE);
2383                 return(FILE_TYPE_UNKNOWN);
2384         }
2385         
2386         type = _wapi_handle_type (handle);
2387         
2388         if(io_ops[type].getfiletype==NULL) {
2389                 SetLastError (ERROR_INVALID_HANDLE);
2390                 return(FILE_TYPE_UNKNOWN);
2391         }
2392         
2393         return(io_ops[type].getfiletype ());
2394 }
2395
2396 /**
2397  * GetFileSize:
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
2401  * stored here.
2402  *
2403  * Retrieves the size of the file @handle.
2404  *
2405  * If the library is compiled without large file support, @highsize
2406  * has its value set to zero on a successful return.
2407  *
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.
2411  */
2412 guint32 GetFileSize(gpointer fd_handle, guint32 *highsize)
2413 {
2414         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2415         WapiHandleType type;
2416
2417         if (handle == NULL) {
2418                 SetLastError (ERROR_INVALID_HANDLE);
2419                 return(INVALID_FILE_SIZE);
2420         }
2421         
2422         type = _wapi_handle_type (handle);
2423         
2424         if(io_ops[type].getfilesize==NULL) {
2425                 SetLastError (ERROR_INVALID_HANDLE);
2426                 return(INVALID_FILE_SIZE);
2427         }
2428         
2429         return(io_ops[type].getfilesize (handle, highsize));
2430 }
2431
2432 /**
2433  * GetFileTime:
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
2438  * %NULL.
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
2441  * %NULL.
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
2444  * be %NULL.
2445  *
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
2449  * GMT.
2450  *
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
2453  * other times.
2454  *
2455  * Return value: %TRUE on success, %FALSE otherwise.
2456  */
2457 gboolean GetFileTime(gpointer fd_handle, WapiFileTime *create_time,
2458                      WapiFileTime *last_access, WapiFileTime *last_write)
2459 {
2460         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2461         WapiHandleType type;
2462
2463         if (handle == NULL) {
2464                 SetLastError (ERROR_INVALID_HANDLE);
2465                 return(FALSE);
2466         }
2467         
2468         type = _wapi_handle_type (handle);
2469         
2470         if(io_ops[type].getfiletime==NULL) {
2471                 SetLastError (ERROR_INVALID_HANDLE);
2472                 return(FALSE);
2473         }
2474         
2475         return(io_ops[type].getfiletime (handle, create_time, last_access,
2476                                          last_write));
2477 }
2478
2479 /**
2480  * SetFileTime:
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
2485  * %NULL.
2486  * @last_access: Points to a %WapiFileTime structure that contains the
2487  * number of ticks since the epoch when the file was last accessed.
2488  * May be %NULL.
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.
2491  * May be %NULL.
2492  *
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
2496  * GMT.
2497  *
2498  * Create time isn't recorded on POSIX file systems, and is ignored.
2499  *
2500  * Return value: %TRUE on success, %FALSE otherwise.
2501  */
2502 gboolean SetFileTime(gpointer fd_handle, const WapiFileTime *create_time,
2503                      const WapiFileTime *last_access,
2504                      const WapiFileTime *last_write)
2505 {
2506         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2507         WapiHandleType type;
2508
2509         if (handle == NULL) {
2510                 SetLastError (ERROR_INVALID_HANDLE);
2511                 return(FALSE);
2512         }
2513         
2514         type = _wapi_handle_type (handle);
2515         
2516         if(io_ops[type].setfiletime==NULL) {
2517                 SetLastError (ERROR_INVALID_HANDLE);
2518                 return(FALSE);
2519         }
2520         
2521         return(io_ops[type].setfiletime (handle, create_time, last_access,
2522                                          last_write));
2523 }
2524
2525 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
2526  * January 1 1601 GMT
2527  */
2528
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
2534
2535 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2536
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},
2540 };
2541
2542 /**
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
2547  * broken-out time.
2548  *
2549  * Converts a tick count into broken-out time values.
2550  *
2551  * Return value: %TRUE on success, %FALSE otherwise.
2552  */
2553 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2554                               WapiSystemTime *system_time)
2555 {
2556         gint64 file_ticks, totaldays, rem, y;
2557         const guint16 *ip;
2558         
2559         if(system_time==NULL) {
2560 #ifdef DEBUG
2561                 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2562 #endif
2563
2564                 return(FALSE);
2565         }
2566         
2567         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2568                 file_time->dwLowDateTime;
2569         
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
2573          */
2574         if(file_ticks<0) {
2575 #ifdef DEBUG
2576                 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2577 #endif
2578
2579                 return(FALSE);
2580         }
2581
2582         totaldays=(file_ticks / TICKS_PER_DAY);
2583         rem = file_ticks % TICKS_PER_DAY;
2584 #ifdef DEBUG
2585         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2586                   totaldays, rem);
2587 #endif
2588
2589         system_time->wHour=rem/TICKS_PER_HOUR;
2590         rem %= TICKS_PER_HOUR;
2591 #ifdef DEBUG
2592         g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2593                   system_time->wHour, rem);
2594 #endif
2595         
2596         system_time->wMinute = rem / TICKS_PER_MINUTE;
2597         rem %= TICKS_PER_MINUTE;
2598 #ifdef DEBUG
2599         g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2600                   system_time->wMinute, rem);
2601 #endif
2602         
2603         system_time->wSecond = rem / TICKS_PER_SECOND;
2604         rem %= TICKS_PER_SECOND;
2605 #ifdef DEBUG
2606         g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2607                   system_time->wSecond, rem);
2608 #endif
2609         
2610         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2611 #ifdef DEBUG
2612         g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2613                   system_time->wMilliseconds);
2614 #endif
2615
2616         /* January 1, 1601 was a Monday, according to Emacs calendar */
2617         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2618 #ifdef DEBUG
2619         g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2620                   system_time->wDayOfWeek);
2621 #endif
2622         
2623         /* This algorithm to find year and month given days from epoch
2624          * from glibc
2625          */
2626         y=1601;
2627         
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))
2630
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);
2634 #ifdef DEBUG
2635                 g_message(G_GNUC_PRETTY_FUNCTION
2636                           ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2637                           y);
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));
2641 #endif
2642                 
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));
2647 #ifdef DEBUG
2648                 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2649                           totaldays);
2650 #endif
2651                 y = yg;
2652 #ifdef DEBUG
2653                 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2654 #endif
2655         }
2656         
2657         system_time->wYear = y;
2658 #ifdef DEBUG
2659         g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2660 #endif
2661
2662         ip = mon_yday[isleap(y)];
2663         
2664         for(y=11; totaldays < ip[y]; --y) {
2665                 continue;
2666         }
2667         totaldays-=ip[y];
2668 #ifdef DEBUG
2669         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2670 #endif
2671         
2672         system_time->wMonth = y + 1;
2673 #ifdef DEBUG
2674         g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2675 #endif
2676
2677         system_time->wDay = totaldays + 1;
2678 #ifdef DEBUG
2679         g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2680 #endif
2681         
2682         return(TRUE);
2683 }
2684
2685 static gint
2686 file_compare (gconstpointer a, gconstpointer b)
2687 {
2688         gchar *astr = *(gchar **) a;
2689         gchar *bstr = *(gchar **) b;
2690
2691         return strcmp (astr, bstr);
2692 }
2693
2694 static gint
2695 get_errno_from_g_file_error (gint error)
2696 {
2697         switch (error) {
2698 #ifdef EACCESS
2699         case G_FILE_ERROR_ACCES:
2700                 error = EACCES;
2701                 break;
2702 #endif
2703 #ifdef ENAMETOOLONG
2704         case G_FILE_ERROR_NAMETOOLONG:
2705                 error = ENAMETOOLONG;
2706                 break;
2707 #endif
2708 #ifdef ENOENT
2709         case G_FILE_ERROR_NOENT:
2710                 error = ENOENT;
2711                 break;
2712 #endif
2713 #ifdef ENOTDIR
2714         case G_FILE_ERROR_NOTDIR:
2715                 error = ENOTDIR;
2716                 break;
2717 #endif
2718 #ifdef ENXIO
2719         case G_FILE_ERROR_NXIO:
2720                 error = ENXIO;
2721                 break;
2722 #endif
2723 #ifdef ENODEV
2724         case G_FILE_ERROR_NODEV:
2725                 error = ENODEV;
2726                 break;
2727 #endif
2728 #ifdef EROFS
2729         case G_FILE_ERROR_ROFS:
2730                 error = EROFS;
2731                 break;
2732 #endif
2733 #ifdef ETXTBSY
2734         case G_FILE_ERROR_TXTBSY:
2735                 error = ETXTBSY;
2736                 break;
2737 #endif
2738 #ifdef EFAULT
2739         case G_FILE_ERROR_FAULT:
2740                 error = EFAULT;
2741                 break;
2742 #endif
2743 #ifdef ELOOP
2744         case G_FILE_ERROR_LOOP:
2745                 error = ELOOP;
2746                 break;
2747 #endif
2748 #ifdef ENOSPC
2749         case G_FILE_ERROR_NOSPC:
2750                 error = ENOSPC;
2751                 break;
2752 #endif
2753 #ifdef ENOMEM
2754         case G_FILE_ERROR_NOMEM:
2755                 error = ENOMEM;
2756                 break;
2757 #endif
2758 #ifdef EMFILE
2759         case G_FILE_ERROR_MFILE:
2760                 error = EMFILE;
2761                 break;
2762 #endif
2763 #ifdef ENFILE
2764         case G_FILE_ERROR_NFILE:
2765                 error = ENFILE;
2766                 break;
2767 #endif
2768 #ifdef EBADF
2769         case G_FILE_ERROR_BADF:
2770                 error = EBADF;
2771                 break;
2772 #endif
2773 #ifdef EINVAL
2774         case G_FILE_ERROR_INVAL:
2775                 error = EINVAL;
2776                 break;
2777 #endif
2778 #ifdef EPIPE
2779         case G_FILE_ERROR_PIPE:
2780                 error = EPIPE;
2781                 break;
2782 #endif
2783 #ifdef EAGAIN
2784         case G_FILE_ERROR_AGAIN:
2785                 error = EAGAIN;
2786                 break;
2787 #endif
2788 #ifdef EINTR
2789         case G_FILE_ERROR_INTR:
2790                 error = EINTR;
2791                 break;
2792 #endif
2793 #ifdef EWIO
2794         case G_FILE_ERROR_IO:
2795                 error = EIO;
2796                 break;
2797 #endif
2798 #ifdef EPERM
2799         case G_FILE_ERROR_PERM:
2800                 error = EPERM;
2801                 break;
2802 #endif
2803         case G_FILE_ERROR_FAILED:
2804                 error = ERROR_INVALID_PARAMETER;
2805                 break;
2806         }
2807
2808         return error;
2809 }
2810
2811 /* scandir using glib */
2812 static gint
2813 mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
2814 {
2815         GError *error = NULL;
2816         GDir *dir;
2817         GPtrArray *names;
2818         const gchar *name;
2819         gint result;
2820         GPatternSpec *patspec;
2821
2822         dir = g_dir_open (dirname, 0, &error);
2823         if (dir == NULL) {
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))
2829                         errnum = EACCES;
2830
2831                 errno = errnum;
2832                 return -1;
2833         }
2834
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));
2840         }
2841         
2842         g_pattern_spec_free (patspec);
2843         g_dir_close (dir);
2844         result = names->len;
2845         if (result > 0) {
2846                 g_ptr_array_sort (names, file_compare);
2847                 g_ptr_array_set_size (names, result + 1);
2848
2849                 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
2850         } else {
2851                 g_ptr_array_free (names, TRUE);
2852         }
2853
2854         return result;
2855 }
2856
2857 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
2858 {
2859         struct _WapiHandlePrivate_find *find_handle;
2860         gpointer handle, find_ret = INVALID_HANDLE_VALUE;
2861         gboolean ok;
2862         gchar *utf8_pattern = NULL, *dir_part, *entry_part;
2863         int result;
2864         int thr_ret;
2865         gboolean unref = FALSE;
2866         
2867         if (pattern == NULL) {
2868 #ifdef DEBUG
2869                 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
2870 #endif
2871
2872                 SetLastError (ERROR_PATH_NOT_FOUND);
2873                 return(INVALID_HANDLE_VALUE);
2874         }
2875
2876         utf8_pattern = mono_unicode_to_external (pattern);
2877         if (utf8_pattern == NULL) {
2878 #ifdef DEBUG
2879                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2880 #endif
2881                 
2882                 SetLastError (ERROR_INVALID_NAME);
2883                 return(INVALID_HANDLE_VALUE);
2884         }
2885
2886 #ifdef DEBUG
2887         g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
2888                 utf8_pattern);
2889 #endif
2890         
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);
2894
2895 #if 0
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...
2899          */
2900         if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
2901                 SetLastError (ERROR_INVALID_NAME);
2902                 g_free (dir_part);
2903                 g_free (entry_part);
2904                 g_free (utf8_pattern);
2905                 return(INVALID_HANDLE_VALUE);
2906         }
2907 #endif
2908         
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");
2912                 g_free (dir_part);
2913                 g_free (entry_part);
2914                 g_free (utf8_pattern);
2915                 
2916                 return(INVALID_HANDLE_VALUE);
2917         }
2918
2919         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2920                               handle);
2921         thr_ret = _wapi_handle_lock_handle (handle);
2922         g_assert (thr_ret == 0);
2923         
2924         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
2925                                 (gpointer *)&find_handle);
2926         if(ok==FALSE) {
2927                 g_warning (G_GNUC_PRETTY_FUNCTION
2928                            ": error looking up find handle %p", handle);
2929                 g_free (dir_part);
2930                 dir_part = NULL;
2931                 g_free (entry_part);
2932                 entry_part = NULL;
2933                 g_free (utf8_pattern);
2934                 utf8_pattern = NULL;
2935                 goto cleanup;
2936         }
2937
2938         /* The pattern can specify a directory or a set of files.
2939          *
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.
2949          *
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.
2956          */
2957
2958         find_handle->namelist = NULL;
2959         result = mono_io_scandir (dir_part, entry_part, &find_handle->namelist);
2960         
2961         if (result < 0) {
2962 #ifdef DEBUG
2963                 gint errnum = errno;
2964 #endif
2965                 _wapi_set_last_error_from_errno ();
2966 #ifdef DEBUG
2967                 g_message (G_GNUC_PRETTY_FUNCTION ": scandir error: %s", g_strerror (errnum));
2968 #endif
2969                 g_free (utf8_pattern);
2970                 g_free (entry_part);
2971                 g_free (dir_part);
2972                 unref = TRUE;
2973                 goto cleanup;
2974         }
2975
2976         g_free (utf8_pattern);
2977         g_free (entry_part);
2978         
2979 #ifdef DEBUG
2980         g_message (G_GNUC_PRETTY_FUNCTION ": Got %d matches", result);
2981 #endif
2982
2983         find_handle->dir_part = dir_part;
2984         find_handle->num = result;
2985         find_handle->count = 0;
2986
2987         find_ret = handle;
2988
2989 cleanup:
2990         thr_ret = _wapi_handle_unlock_handle (handle);
2991         g_assert (thr_ret == 0);
2992         pthread_cleanup_pop (0);
2993
2994         /* FindNextFile has to be called after unlocking the handle,
2995          * because it wants to lock the handle itself
2996          */
2997         if (find_ret != INVALID_HANDLE_VALUE &&
2998             !FindNextFile (handle, find_data)) {
2999                 FindClose (handle);
3000                 SetLastError (ERROR_NO_MORE_FILES);
3001                 find_ret = INVALID_HANDLE_VALUE;
3002         }
3003
3004         /* Must not call _wapi_handle_unref() with the handle already
3005          * locked
3006          */
3007         if (unref) {
3008                 _wapi_handle_unref (handle);
3009         }
3010         
3011         return (find_ret);
3012 }
3013
3014 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
3015 {
3016         struct _WapiHandlePrivate_find *find_handle;
3017         gboolean ok;
3018         struct stat buf;
3019         gchar *filename;
3020         gchar *utf8_filename, *utf8_basename;
3021         gunichar2 *utf16_basename;
3022         time_t create_time;
3023         glong bytes;
3024         int thr_ret;
3025         gboolean ret = FALSE;
3026         
3027         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3028                                 (gpointer *)&find_handle);
3029         if(ok==FALSE) {
3030                 g_warning (G_GNUC_PRETTY_FUNCTION
3031                            ": error looking up find handle %p", handle);
3032                 SetLastError (ERROR_INVALID_HANDLE);
3033                 return(FALSE);
3034         }
3035
3036         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3037                               handle);
3038         thr_ret = _wapi_handle_lock_handle (handle);
3039         g_assert (thr_ret == 0);
3040         
3041 retry:
3042         if (find_handle->count >= find_handle->num) {
3043                 SetLastError (ERROR_NO_MORE_FILES);
3044                 goto cleanup;
3045         }
3046
3047         /* stat next match */
3048
3049         filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3050         if (lstat (filename, &buf) != 0) {
3051 #ifdef DEBUG
3052                 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
3053 #endif
3054
3055                 g_free (filename);
3056                 goto retry;
3057         }
3058
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
3062          * it isn't there.)
3063          */
3064         if(S_ISLNK (buf.st_mode)) {
3065                 if(stat (filename, &buf) != 0) {
3066                         g_free (filename);
3067                         goto retry;
3068                 }
3069         }
3070         
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
3075                  * ignore it.
3076                  */
3077                 g_free (filename);
3078                 goto retry;
3079         }
3080         g_free (filename);
3081         
3082 #ifdef DEBUG
3083         g_message (G_GNUC_PRETTY_FUNCTION ": Found [%s]", utf8_filename);
3084 #endif
3085         
3086         /* fill data block */
3087
3088         if (buf.st_mtime < buf.st_ctime)
3089                 create_time = buf.st_mtime;
3090         else
3091                 create_time = buf.st_ctime;
3092         
3093         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3094
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);
3098
3099         if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3100                 find_data->nFileSizeHigh = 0;
3101                 find_data->nFileSizeLow = 0;
3102         }
3103         else {
3104                 find_data->nFileSizeHigh = buf.st_size >> 32;
3105                 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3106         }
3107
3108         find_data->dwReserved0 = 0;
3109         find_data->dwReserved1 = 0;
3110
3111         utf8_basename = g_path_get_basename (utf8_filename);
3112         utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3113                                           NULL);
3114         if(utf16_basename==NULL) {
3115                 g_free (utf8_basename);
3116                 g_free (utf8_filename);
3117                 goto retry;
3118         }
3119         ret = TRUE;
3120         
3121         /* utf16 is 2 * utf8 */
3122         bytes *= 2;
3123
3124         memset (find_data->cFileName, '\0', (MAX_PATH*2));
3125
3126         /* Truncating a utf16 string like this might leave the last
3127          * char incomplete
3128          */
3129         memcpy (find_data->cFileName, utf16_basename,
3130                 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3131
3132         find_data->cAlternateFileName [0] = 0;  /* not used */
3133
3134         g_free (utf8_basename);
3135         g_free (utf8_filename);
3136         g_free (utf16_basename);
3137
3138 cleanup:
3139         thr_ret = _wapi_handle_unlock_handle (handle);
3140         g_assert (thr_ret == 0);
3141         pthread_cleanup_pop (0);
3142         
3143         return(ret);
3144 }
3145
3146 /**
3147  * FindClose:
3148  * @wapi_handle: the find handle to close.
3149  *
3150  * Closes find handle @wapi_handle
3151  *
3152  * Return value: %TRUE on success, %FALSE otherwise.
3153  */
3154 gboolean FindClose (gpointer handle)
3155 {
3156         struct _WapiHandlePrivate_find *find_handle;
3157         gboolean ok;
3158         int thr_ret;
3159
3160         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3161                                 (gpointer *)&find_handle);
3162         if(ok==FALSE) {
3163                 g_warning (G_GNUC_PRETTY_FUNCTION
3164                            ": error looking up find handle %p", handle);
3165                 SetLastError (ERROR_INVALID_HANDLE);
3166                 return(FALSE);
3167         }
3168
3169         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3170                               handle);
3171         thr_ret = _wapi_handle_lock_handle (handle);
3172         g_assert (thr_ret == 0);
3173         
3174         g_strfreev (find_handle->namelist);
3175         g_free (find_handle->dir_part);
3176
3177         thr_ret = _wapi_handle_unlock_handle (handle);
3178         g_assert (thr_ret == 0);
3179         pthread_cleanup_pop (0);
3180         
3181         _wapi_handle_unref (handle);
3182         
3183         return(TRUE);
3184 }
3185
3186 /**
3187  * CreateDirectory:
3188  * @name: a pointer to a NULL-terminated unicode string, that names
3189  * the directory to be created.
3190  * @security: ignored for now
3191  *
3192  * Creates directory @name
3193  *
3194  * Return value: %TRUE on success, %FALSE otherwise.
3195  */
3196 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
3197 {
3198         gchar *utf8_name;
3199         int result;
3200         struct stat buf;
3201         guint32 attrs;
3202         
3203         utf8_name = mono_unicode_to_external (name);
3204         if (utf8_name == NULL) {
3205 #ifdef DEBUG
3206                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3207 #endif
3208         
3209                 return FALSE;
3210         }
3211
3212         result = mkdir (utf8_name, 0777);
3213
3214         if (result == 0) {
3215                 g_free (utf8_name);
3216                 return TRUE;
3217         }
3218
3219         if (errno == EEXIST) {
3220                 result = stat (utf8_name, &buf);
3221                 if (result == -1) {
3222                         _wapi_set_last_error_from_errno ();
3223                         g_free (utf8_name);
3224                         return FALSE;
3225                 }
3226
3227                 g_free (utf8_name);
3228                 attrs = _wapi_stat_to_file_attributes (&buf);
3229                 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
3230                         return TRUE;
3231
3232                 errno = EEXIST;
3233                 _wapi_set_last_error_from_errno ();
3234                 return FALSE;
3235         }
3236
3237         _wapi_set_last_error_from_errno ();
3238         g_free (utf8_name);
3239         return FALSE;
3240 }
3241
3242 /**
3243  * RemoveDirectory:
3244  * @name: a pointer to a NULL-terminated unicode string, that names
3245  * the directory to be removed.
3246  *
3247  * Removes directory @name
3248  *
3249  * Return value: %TRUE on success, %FALSE otherwise.
3250  */
3251 gboolean RemoveDirectory (const gunichar2 *name)
3252 {
3253         gchar *utf8_name;
3254         int result;
3255
3256         utf8_name = mono_unicode_to_external (name);
3257         if (utf8_name == NULL) {
3258 #ifdef DEBUG
3259                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3260 #endif
3261                 
3262                 return FALSE;
3263         }
3264
3265         result = rmdir (utf8_name);
3266         g_free (utf8_name);
3267
3268         if (result == 0)
3269                 return TRUE;
3270         
3271         _wapi_set_last_error_from_errno ();
3272         return FALSE;
3273 }
3274
3275 /**
3276  * GetFileAttributes:
3277  * @name: a pointer to a NULL-terminated unicode filename.
3278  *
3279  * Gets the attributes for @name;
3280  *
3281  * Return value: %INVALID_FILE_ATTRIBUTES on failure
3282  */
3283 guint32 GetFileAttributes (const gunichar2 *name)
3284 {
3285         gchar *utf8_name;
3286         struct stat buf;
3287         int result;
3288         
3289         utf8_name = mono_unicode_to_external (name);
3290         if (utf8_name == NULL) {
3291 #ifdef DEBUG
3292                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3293 #endif
3294
3295                 SetLastError (ERROR_INVALID_PARAMETER);
3296                 return (INVALID_FILE_ATTRIBUTES);
3297         }
3298
3299         result = stat (utf8_name, &buf);
3300
3301         if (result != 0) {
3302                 _wapi_set_last_error_from_errno ();
3303                 g_free (utf8_name);
3304                 return (INVALID_FILE_ATTRIBUTES);
3305         }
3306         
3307         g_free (utf8_name);
3308         return _wapi_stat_to_file_attributes (&buf);
3309 }
3310
3311 /**
3312  * GetFileAttributesEx:
3313  * @name: a pointer to a NULL-terminated unicode filename.
3314  * @level: must be GetFileExInfoStandard
3315  * @info: pointer to a WapiFileAttributesData structure
3316  *
3317  * Gets attributes, size and filetimes for @name;
3318  *
3319  * Return value: %TRUE on success, %FALSE on failure
3320  */
3321 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3322 {
3323         gchar *utf8_name;
3324         WapiFileAttributesData *data;
3325
3326         struct stat buf;
3327         time_t create_time;
3328         int result;
3329         
3330         if (level != GetFileExInfoStandard) {
3331 #ifdef DEBUG
3332                 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
3333 #endif
3334
3335                 return FALSE;
3336         }
3337
3338         utf8_name = mono_unicode_to_external (name);
3339         if (utf8_name == NULL) {
3340 #ifdef DEBUG
3341                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3342 #endif
3343
3344                 SetLastError (ERROR_INVALID_PARAMETER);
3345                 return FALSE;
3346         }
3347
3348         result = stat (utf8_name, &buf);
3349         g_free (utf8_name);
3350
3351         if (result != 0) {
3352                 SetLastError (ERROR_FILE_NOT_FOUND);
3353                 return FALSE;
3354         }
3355
3356         /* fill data block */
3357
3358         data = (WapiFileAttributesData *)info;
3359
3360         if (buf.st_mtime < buf.st_ctime)
3361                 create_time = buf.st_mtime;
3362         else
3363                 create_time = buf.st_ctime;
3364         
3365         data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3366
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);
3370
3371         if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3372                 data->nFileSizeHigh = 0;
3373                 data->nFileSizeLow = 0;
3374         }
3375         else {
3376                 data->nFileSizeHigh = buf.st_size >> 32;
3377                 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3378         }
3379
3380         return TRUE;
3381 }
3382
3383 /**
3384  * SetFileAttributes
3385  * @name: name of file
3386  * @attrs: attributes to set
3387  *
3388  * Changes the attributes on a named file.
3389  *
3390  * Return value: %TRUE on success, %FALSE on failure.
3391  */
3392 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3393 {
3394         /* FIXME: think of something clever to do on unix */
3395         gchar *utf8_name;
3396         struct stat buf;
3397         int result;
3398
3399         /*
3400          * Currently we only handle one *internal* case, with a value that is
3401          * not standard: 0x80000000, which means `set executable bit'
3402          */
3403
3404         utf8_name = mono_unicode_to_external (name);
3405         result = stat (utf8_name, &buf);
3406         if (result != 0) {
3407                 g_free (utf8_name);
3408                 SetLastError (ERROR_FILE_NOT_FOUND);
3409                 return FALSE;
3410         }
3411
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.
3415          */
3416         if (attrs & FILE_ATTRIBUTE_READONLY) {
3417                 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3418         } else {
3419                 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
3420         }
3421
3422         /* Ignore the other attributes for now */
3423
3424         if (attrs & 0x80000000){
3425                 mode_t exec_mask = 0;
3426
3427                 if ((buf.st_mode & S_IRUSR) != 0)
3428                         exec_mask |= S_IXUSR;
3429
3430                 if ((buf.st_mode & S_IRGRP) != 0)
3431                         exec_mask |= S_IXGRP;
3432
3433                 if ((buf.st_mode & S_IROTH) != 0)
3434                         exec_mask |= S_IXOTH;
3435
3436                 result = chmod (utf8_name, buf.st_mode | exec_mask);
3437         }
3438         /* Don't bother to reset executable (might need to change this
3439          * policy)
3440          */
3441         
3442         g_free (utf8_name);
3443
3444         return(TRUE);
3445 }
3446
3447 /**
3448  * GetCurrentDirectory
3449  * @length: size of the buffer
3450  * @buffer: pointer to buffer that recieves path
3451  *
3452  * Retrieves the current directory for the current process.
3453  *
3454  * Return value: number of characters in buffer on success, zero on failure
3455  */
3456 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3457 {
3458         gchar *path;
3459         gunichar2 *utf16_path;
3460         glong count;
3461         gsize bytes;
3462         
3463         path = g_get_current_dir ();
3464         if (path == NULL)
3465                 return 0;
3466
3467         utf16_path=mono_unicode_from_external (path, &bytes);
3468         
3469         /* if buffer too small, return number of characters required.
3470          * this is plain dumb.
3471          */
3472         
3473         count = (bytes/2)+1;
3474         if (count > length) {
3475                 g_free(path);
3476                 g_free (utf16_path);
3477                 
3478                 return (count);
3479         }
3480
3481         /* Add the terminator */
3482         memset (buffer, '\0', bytes+2);
3483         memcpy (buffer, utf16_path, bytes);
3484         
3485         g_free (utf16_path);
3486         g_free (path);
3487
3488         return count;
3489 }
3490
3491 /**
3492  * SetCurrentDirectory
3493  * @path: path to new directory
3494  *
3495  * Changes the directory path for the current process.
3496  *
3497  * Return value: %TRUE on success, %FALSE on failure.
3498  */
3499 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3500 {
3501         gchar *utf8_path;
3502         gboolean result;
3503
3504         utf8_path = mono_unicode_to_external (path);
3505         if (chdir (utf8_path) != 0) {
3506                 _wapi_set_last_error_from_errno ();
3507                 result = FALSE;
3508         }
3509         else
3510                 result = TRUE;
3511
3512         g_free (utf8_path);
3513         return result;
3514 }
3515
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
3518  */
3519 int _wapi_file_handle_to_fd (gpointer fd_handle)
3520 {
3521         struct _WapiHandlePrivate_file *file_private_handle;
3522         gboolean ok;
3523         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3524         
3525 #ifdef DEBUG
3526         g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
3527 #endif
3528
3529         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
3530                                 (gpointer *)&file_private_handle);
3531         if(ok==FALSE) {
3532                 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
3533                                         (gpointer *)&file_private_handle);
3534                 if(ok==FALSE) {
3535                         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
3536                                                 (gpointer *)&file_private_handle);
3537                         if(ok==FALSE) {
3538 #ifdef DEBUG
3539                                 g_message (G_GNUC_PRETTY_FUNCTION
3540                                            ": returning -1");
3541 #endif
3542                                 return(-1);
3543                         }
3544                 }
3545         }
3546         
3547 #ifdef DEBUG
3548         g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
3549                    file_private_handle->fd);
3550 #endif
3551         
3552         g_assert (file_private_handle->fd == GPOINTER_TO_INT (fd_handle));
3553         
3554         return(file_private_handle->fd);
3555 }
3556
3557 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3558                      WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3559 {
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;
3566         gboolean ok;
3567         int filedes[2];
3568         int ret;
3569         int thr_ret;
3570         gboolean unref_read = FALSE, unref_write = FALSE;
3571         gboolean cp_ret = FALSE;
3572         
3573         mono_once (&io_ops_once, io_ops_init);
3574         
3575 #ifdef DEBUG
3576         g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
3577 #endif
3578
3579         ret=pipe (filedes);
3580         if(ret==-1) {
3581 #ifdef DEBUG
3582                 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
3583                            strerror (errno));
3584 #endif
3585                 
3586                 _wapi_set_last_error_from_errno ();
3587                 return(FALSE);
3588         }
3589
3590         if (filedes[0] >= _wapi_fd_offset_table_size ||
3591             filedes[1] >= _wapi_fd_offset_table_size) {
3592 #ifdef DEBUG
3593                 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
3594 #endif
3595
3596                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3597                 
3598                 close (filedes[0]);
3599                 close (filedes[1]);
3600                 
3601                 return(FALSE);
3602         }
3603         
3604         /* filedes[0] is open for reading, filedes[1] for writing */
3605
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");
3610                 close (filedes[0]);
3611                 close (filedes[1]);
3612                 return(FALSE);
3613         }
3614         
3615         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3616                               read_handle);
3617         thr_ret = _wapi_handle_lock_handle (read_handle);
3618         g_assert (thr_ret == 0);
3619
3620         ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
3621                                 (gpointer *)&pipe_read_handle,
3622                                 (gpointer *)&pipe_read_private_handle);
3623         if(ok==FALSE) {
3624                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3625                 close (filedes[0]);
3626                 close (filedes[1]);
3627                 goto cleanup;
3628         }
3629         
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");
3634                 unref_read = TRUE;
3635                 
3636                 close (filedes[0]);
3637                 close (filedes[1]);
3638                 goto cleanup;
3639         }
3640         
3641         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3642                               write_handle);
3643         thr_ret = _wapi_handle_lock_handle (write_handle);
3644         g_assert (thr_ret == 0);
3645
3646         ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
3647                                 (gpointer *)&pipe_write_handle,
3648                                 (gpointer *)&pipe_write_private_handle);
3649         if(ok==FALSE) {
3650                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3651                 unref_read = TRUE;
3652                 unref_write = TRUE;
3653                 
3654                 close (filedes[0]);
3655                 close (filedes[1]);
3656                 goto write_cleanup;
3657         }
3658         cp_ret = TRUE;
3659         
3660         pipe_read_private_handle->fd=filedes[0];
3661         pipe_read_private_handle->assigned=TRUE;
3662         pipe_read_handle->fileaccess=GENERIC_READ;
3663         
3664         _wapi_handle_fd_offset_store (filedes[0], read_handle);
3665         *readpipe=GINT_TO_POINTER (filedes[0]);
3666
3667         pipe_write_private_handle->fd=filedes[1];
3668         pipe_write_private_handle->assigned=TRUE;
3669         pipe_write_handle->fileaccess=GENERIC_WRITE;
3670         
3671         _wapi_handle_fd_offset_store (filedes[1], write_handle);
3672         *writepipe=GINT_TO_POINTER (filedes[1]);
3673
3674 #ifdef DEBUG
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]);
3676 #endif
3677
3678 write_cleanup:
3679         thr_ret =_wapi_handle_unlock_handle (write_handle);
3680         g_assert (thr_ret == 0);
3681         pthread_cleanup_pop (0);
3682
3683         if (unref_write) {
3684                 _wapi_handle_unref (write_handle);
3685         }
3686
3687 cleanup:
3688         thr_ret =_wapi_handle_unlock_handle (read_handle);
3689         g_assert (thr_ret == 0);
3690         pthread_cleanup_pop (0);
3691
3692         /* Must not call _wapi_handle_unref() with the handle already
3693          * locked
3694          */
3695         if (unref_read) {
3696                 _wapi_handle_unref (read_handle);
3697         }
3698         
3699         return(cp_ret);
3700 }
3701
3702 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3703 {
3704         gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3705         gunichar2 *tmpdir16=NULL;
3706         glong dirlen;
3707         gsize bytes;
3708         guint32 ret;
3709         
3710         if(tmpdir[strlen (tmpdir)]!='/') {
3711                 g_free (tmpdir);
3712                 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3713         }
3714         
3715         tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3716         if(tmpdir16==NULL) {
3717                 g_free (tmpdir);
3718                 return(0);
3719         } else {
3720                 dirlen=(bytes/2);
3721                 
3722                 if(dirlen+1>len) {
3723 #ifdef DEBUG
3724                         g_message (G_GNUC_PRETTY_FUNCTION
3725                                    ": Size %d smaller than needed (%ld)", len,
3726                                    dirlen+1);
3727 #endif
3728                 
3729                         ret=dirlen+1;
3730                 } else {
3731                         /* Add the terminator */
3732                         memset (buf, '\0', bytes+2);
3733                         memcpy (buf, tmpdir16, bytes);
3734                 
3735                         ret=dirlen;
3736                 }
3737         }
3738
3739         if(tmpdir16!=NULL) {
3740                 g_free (tmpdir16);
3741         }
3742         g_free (tmpdir);
3743         
3744         return(ret);
3745 }
3746
3747 gboolean
3748 _wapi_io_add_callback (gpointer fd_handle,
3749                        WapiOverlappedCB callback,
3750                        guint64 flags G_GNUC_UNUSED)
3751 {
3752         struct _WapiHandle_file *file_handle;
3753         struct _WapiHandlePrivate_file *file_private_handle;
3754         gboolean ok;
3755         int thr_ret;
3756         gboolean ret = FALSE;
3757         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3758         
3759         if (handle == NULL) {
3760                 SetLastError (ERROR_INVALID_HANDLE);
3761                 return(FALSE);
3762         }
3763         
3764         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3765                                   (gpointer *) &file_handle,
3766                                   (gpointer *) &file_private_handle);
3767
3768         if (ok == FALSE) {
3769                 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
3770                                           (gpointer *) &file_handle,
3771                                           (gpointer *) &file_private_handle);
3772
3773         }
3774
3775         if (ok == FALSE || file_private_handle->async == FALSE) {
3776                 SetLastError (ERROR_INVALID_HANDLE);
3777                 return FALSE;
3778         }
3779
3780         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3781                               handle);
3782         thr_ret = _wapi_handle_lock_handle (handle);
3783         g_assert (thr_ret == 0);
3784         
3785         if (file_private_handle->callback != NULL) {
3786                 SetLastError (ERROR_INVALID_PARAMETER);
3787                 goto cleanup;
3788         }
3789         ret = TRUE;
3790         
3791         file_private_handle->callback = callback;
3792
3793 cleanup:
3794         thr_ret = _wapi_handle_unlock_handle (handle);
3795         g_assert (thr_ret == 0);
3796         pthread_cleanup_pop (0);
3797
3798         return(ret);
3799 }
3800
3801 gint32
3802 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
3803 {
3804         FILE *fp;
3805         gunichar2 *ptr, *dir;
3806         glong length, total = 0;
3807         gchar buffer [512];
3808         gchar **splitted;
3809
3810         memset (buf, 0, sizeof (gunichar2) * (len + 1)); 
3811         buf [0] = '/';
3812         buf [1] = 0;
3813         buf [2] = 0;
3814
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");
3819         if (fp == NULL) {
3820                 fp = fopen ("/etc/mnttab", "rt");
3821                 if (fp == NULL)
3822                         return 1;
3823         }
3824
3825         ptr = buf;
3826         while (fgets (buffer, 512, fp) != NULL) {
3827                 if (*buffer != '/')
3828                         continue;
3829
3830                 splitted = g_strsplit (buffer, " ", 0);
3831                 if (!*splitted || !*(splitted + 1))
3832                         continue;
3833
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 */
3838                 }
3839
3840                 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
3841                 g_free (dir);
3842                 total += length + 1;
3843         }
3844
3845         fclose (fp);
3846         return total;
3847 /* Commented out, does not work with my mtab!!! - Gonz */
3848 #ifdef NOTENABLED /* HAVE_MNTENT_H */
3849 {
3850         FILE *fp;
3851         struct mntent *mnt;
3852         gunichar2 *ptr, *dir;
3853         glong len, total = 0;
3854         
3855
3856         fp = setmntent ("/etc/mtab", "rt");
3857         if (fp == NULL) {
3858                 fp = setmntent ("/etc/mnttab", "rt");
3859                 if (fp == NULL)
3860                         return;
3861         }
3862
3863         ptr = buf;
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 */
3869                 }
3870
3871                 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
3872                 g_free (dir);
3873                 total += len + 1;
3874         }
3875
3876         endmntent (fp);
3877         return total;
3878 }
3879 #endif
3880 }
3881
3882 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
3883 {
3884         struct flock lock_data;
3885         int ret;
3886
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;
3891         
3892         do {
3893                 ret = fcntl (fd, F_SETLK, &lock_data);
3894         }
3895         while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
3896         
3897 #ifdef DEBUG
3898         g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
3899 #endif
3900
3901         if (ret == -1) {
3902                 SetLastError (ERROR_LOCK_VIOLATION);
3903                 return(FALSE);
3904         }
3905
3906         return(TRUE);
3907 }
3908
3909 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
3910 {
3911         struct flock lock_data;
3912         int ret;
3913
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;
3918         
3919         do {
3920                 ret = fcntl (fd, F_SETLK, &lock_data);
3921         }
3922         while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
3923         
3924 #ifdef DEBUG
3925         g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
3926 #endif
3927         
3928         if (ret == -1) {
3929                 SetLastError (ERROR_LOCK_VIOLATION);
3930                 return(FALSE);
3931         }
3932
3933         return(TRUE);
3934 }
3935
3936 gboolean LockFile (gpointer fd_handle, guint32 offset_low, guint32 offset_high,
3937                    guint32 length_low, guint32 length_high)
3938 {
3939         struct _WapiHandle_file *file_handle;
3940         struct _WapiHandlePrivate_file *file_private_handle;
3941         gboolean ok;
3942         off_t offset, length;
3943         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3944
3945         if (handle == NULL) {
3946                 SetLastError (ERROR_INVALID_HANDLE);
3947                 return(FALSE);
3948         }
3949         
3950         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3951                                   (gpointer *)&file_handle,
3952                                   (gpointer *)&file_private_handle);
3953         if (ok == FALSE) {
3954                 g_warning (G_GNUC_PRETTY_FUNCTION
3955                            ": error looking up file handle %p", handle);
3956                 SetLastError (ERROR_INVALID_HANDLE);
3957                 return(FALSE);
3958         }
3959
3960         if (!(file_handle->fileaccess & GENERIC_READ) &&
3961             !(file_handle->fileaccess & GENERIC_WRITE) &&
3962             !(file_handle->fileaccess & GENERIC_ALL)) {
3963 #ifdef DEBUG
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);
3965 #endif
3966                 SetLastError (ERROR_ACCESS_DENIED);
3967                 return(FALSE);
3968         }
3969
3970 #ifdef HAVE_LARGE_FILE_SUPPORT
3971         offset = ((gint64)offset_high << 32) | offset_low;
3972         length = ((gint64)length_high << 32) | length_low;
3973
3974 #ifdef DEBUG
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);
3978 #endif
3979 #else
3980         offset = offset_low;
3981         length = length_low;
3982
3983 #ifdef DEBUG
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);
3987 #endif
3988 #endif
3989
3990         return(_wapi_lock_file_region (file_private_handle->fd, offset,
3991                                        length));
3992 }
3993
3994 gboolean UnlockFile (gpointer fd_handle, guint32 offset_low,
3995                      guint32 offset_high, guint32 length_low,
3996                      guint32 length_high)
3997 {
3998         struct _WapiHandle_file *file_handle;
3999         struct _WapiHandlePrivate_file *file_private_handle;
4000         gboolean ok;
4001         off_t offset, length;
4002         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4003
4004         if (handle == NULL) {
4005                 SetLastError (ERROR_INVALID_HANDLE);
4006                 return(FALSE);
4007         }
4008         
4009         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4010                                   (gpointer *)&file_handle,
4011                                   (gpointer *)&file_private_handle);
4012         if (ok == FALSE) {
4013                 g_warning (G_GNUC_PRETTY_FUNCTION
4014                            ": error looking up file handle %p", handle);
4015                 SetLastError (ERROR_INVALID_HANDLE);
4016                 return(FALSE);
4017         }
4018
4019         if (!(file_handle->fileaccess & GENERIC_READ) &&
4020             !(file_handle->fileaccess & GENERIC_WRITE) &&
4021             !(file_handle->fileaccess & GENERIC_ALL)) {
4022 #ifdef DEBUG
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);
4024 #endif
4025                 SetLastError (ERROR_ACCESS_DENIED);
4026                 return(FALSE);
4027         }
4028
4029 #ifdef HAVE_LARGE_FILE_SUPPORT
4030         offset = ((gint64)offset_high << 32) | offset_low;
4031         length = ((gint64)length_high << 32) | length_low;
4032
4033 #ifdef DEBUG
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);
4037 #endif
4038 #else
4039         offset = offset_low;
4040         length = length_low;
4041
4042 #ifdef DEBUG
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);
4046 #endif
4047 #endif
4048
4049         return(_wapi_unlock_file_region (file_private_handle->fd, offset,
4050                                          length));
4051 }