2005-02-05 Zoltan Varga <vargaz@freemail.hu>
[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 const 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                 SetLastError (ERROR_INVALID_HANDLE);
240                 return;
241         }
242         
243 #ifdef DEBUG
244         g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p", handle);
245 #endif
246         
247         if(file_handle->filename!=0) {
248                 _wapi_handle_scratch_delete (file_handle->filename);
249                 file_handle->filename=0;
250         }
251         if(file_handle->security_attributes!=0) {
252                 _wapi_handle_scratch_delete (file_handle->security_attributes);
253                 file_handle->security_attributes=0;
254         }
255 }
256
257 static void file_close_private (gpointer handle)
258 {
259         struct _WapiHandlePrivate_file *file_private_handle;
260         gboolean ok;
261         
262         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
263                                 (gpointer *)&file_private_handle);
264         if(ok==FALSE) {
265                 g_warning (G_GNUC_PRETTY_FUNCTION
266                            ": error looking up file handle %p", handle);
267                 SetLastError (ERROR_INVALID_HANDLE);
268                 return;
269         }
270         
271         if (file_private_handle->fd_mapped.assigned) {
272 #ifdef DEBUG
273                 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
274                           handle, file_private_handle->fd_mapped.fd);
275 #endif
276         
277                 /* Blank out the mapping, to make catching errors easier */
278                 _wapi_handle_fd_offset_store (file_private_handle->fd_mapped.fd, NULL);
279         
280                 close(file_private_handle->fd_mapped.fd);
281         }
282 }
283
284 static WapiFileType file_getfiletype(void)
285 {
286         return(FILE_TYPE_DISK);
287 }
288
289 #ifdef USE_AIO
290 typedef struct {
291         struct aiocb *aio;
292         WapiOverlapped *overlapped;
293         WapiOverlappedCB callback;
294 } notifier_data_t;
295
296 #define SIGPTR(a) a.SIGVAL_PTR
297
298 static void
299 async_notifier (union sigval sig)
300 {
301         notifier_data_t *ndata = SIGPTR (sig);
302         guint32 error;
303         guint32 numbytes;
304
305         error = aio_return (ndata->aio);
306         if (error < 0) {
307                 error = _wapi_get_win32_file_error (error);
308                 numbytes = 0;
309         } else {
310                 numbytes = error;
311                 error = 0;
312         }
313
314         ndata->callback (error, numbytes, ndata->overlapped);
315         g_free (ndata->aio);
316         g_free (ndata);
317 }
318
319 #endif /* USE_AIO */
320
321 static gboolean file_read(gpointer handle, gpointer buffer,
322                           guint32 numbytes, guint32 *bytesread,
323                           WapiOverlapped *overlapped)
324 {
325         struct _WapiHandle_file *file_handle;
326         struct _WapiHandlePrivate_file *file_private_handle;
327         gboolean ok;
328         int ret;
329         
330         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
331                                 (gpointer *)&file_handle,
332                                 (gpointer *)&file_private_handle);
333         if(ok==FALSE) {
334                 g_warning (G_GNUC_PRETTY_FUNCTION
335                            ": error looking up file handle %p", handle);
336                 SetLastError (ERROR_INVALID_HANDLE);
337                 return(FALSE);
338         }
339
340         if (file_private_handle->fd_mapped.assigned == FALSE) {
341                 SetLastError (ERROR_INVALID_HANDLE);
342                 return(FALSE);
343         }
344         
345         if(bytesread!=NULL) {
346                 *bytesread=0;
347         }
348         
349         if(!(file_handle->fileaccess&GENERIC_READ) &&
350            !(file_handle->fileaccess&GENERIC_ALL)) {
351 #ifdef DEBUG
352                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
353 #endif
354
355                 SetLastError (ERROR_ACCESS_DENIED);
356                 return(FALSE);
357         }
358
359         if (file_private_handle->async == FALSE) {
360                 do {
361                         ret=read(file_private_handle->fd_mapped.fd, buffer,
362                                  numbytes);
363                 }
364                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
365                         
366                 if(ret==-1) {
367                         gint err = errno;
368
369 #ifdef DEBUG
370                         g_message(G_GNUC_PRETTY_FUNCTION
371                                   ": read of handle %p fd %d error: %s", handle,
372                                   file_private_handle->fd_mapped.fd,
373                                   strerror(err));
374 #endif
375                         SetLastError (_wapi_get_win32_file_error (err));
376                         return(FALSE);
377                 }
378                 
379                 if(bytesread!=NULL) {
380                         *bytesread=ret;
381                 }
382                 
383                 return(TRUE);
384         }
385
386 #ifndef USE_AIO
387         SetLastError (ERROR_NOT_SUPPORTED);
388         return FALSE;
389 #else
390         if (overlapped == NULL || file_private_handle->callback == NULL) {
391                 SetLastError (ERROR_INVALID_PARAMETER);
392                 return FALSE;
393         }
394
395         {
396         int fd = file_private_handle->fd_mapped.fd;
397         struct aiocb *aio;
398         int result;
399         notifier_data_t *ndata;
400
401         ndata = g_new0 (notifier_data_t, 1);
402         aio = g_new0 (struct aiocb, 1);
403         ndata->overlapped = overlapped;
404         ndata->aio = aio;
405         ndata->callback = file_private_handle->callback;
406
407         aio->aio_fildes = fd;
408         aio->aio_lio_opcode = LIO_READ;
409         aio->aio_nbytes = numbytes;
410         aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
411         aio->aio_buf = buffer;
412         aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
413         aio->aio_sigevent.sigev_notify_function = async_notifier;
414         SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
415
416         result = aio_read (aio);
417         if (result == -1) {
418                 _wapi_set_last_error_from_errno ();
419                 return FALSE;
420         }
421
422         result = aio_error (aio);
423 #ifdef DEBUG
424         g_print ("aio_error (read) returned %d for %d\n", result, fd);
425 #endif
426         if (result == 0) {
427                 numbytes = aio_return (aio);
428 #ifdef DEBUG
429                 g_print ("numbytes %d for %d\n", numbytes, fd);
430 #endif
431         } else {
432                 errno = result;
433                 _wapi_set_last_error_from_errno ();
434                 return FALSE;
435         }
436
437         if (bytesread)
438                 *bytesread = numbytes;
439
440         return TRUE;
441         }
442 #endif
443 }
444
445 static gboolean file_write(gpointer handle, gconstpointer buffer,
446                            guint32 numbytes, guint32 *byteswritten,
447                            WapiOverlapped *overlapped G_GNUC_UNUSED)
448 {
449         struct _WapiHandle_file *file_handle;
450         struct _WapiHandlePrivate_file *file_private_handle;
451         gboolean ok;
452         int ret;
453         
454         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
455                                 (gpointer *)&file_handle,
456                                 (gpointer *)&file_private_handle);
457         if(ok==FALSE) {
458                 g_warning (G_GNUC_PRETTY_FUNCTION
459                            ": error looking up file handle %p", handle);
460                 SetLastError (ERROR_INVALID_HANDLE);
461                 return(FALSE);
462         }
463
464         if (file_private_handle->fd_mapped.assigned == FALSE) {
465                 SetLastError (ERROR_INVALID_HANDLE);
466                 return(FALSE);
467         }
468         
469         if(byteswritten!=NULL) {
470                 *byteswritten=0;
471         }
472         
473         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
474            !(file_handle->fileaccess&GENERIC_ALL)) {
475 #ifdef DEBUG
476                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
477 #endif
478
479                 SetLastError (ERROR_ACCESS_DENIED);
480                 return(FALSE);
481         }
482         
483         if (file_private_handle->async == FALSE) {
484                 off_t current_pos;
485                 
486                 /* Need to lock the region we're about to write to,
487                  * because we only do advisory locking on POSIX
488                  * systems
489                  */
490                 current_pos = lseek (file_private_handle->fd_mapped.fd,
491                                      (off_t)0, SEEK_CUR);
492                 if (current_pos == -1) {
493 #ifdef DEBUG
494                         g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d lseek failed: %s", handle, file_private_handle->fd_mapped.fd, strerror (errno));
495 #endif
496                         _wapi_set_last_error_from_errno ();
497                         return(FALSE);
498                 }
499                 
500                 if (_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
501                                             current_pos, numbytes) == FALSE) {
502                         /* The error has already been set */
503                         return(FALSE);
504                 }
505                 
506                 do {
507                         ret=write(file_private_handle->fd_mapped.fd, buffer,
508                                   numbytes);
509                 }
510                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
511
512                 _wapi_unlock_file_region (file_private_handle->fd_mapped.fd,
513                                           current_pos, numbytes);
514
515                 if(ret==-1) {
516 #ifdef DEBUG
517                         g_message(G_GNUC_PRETTY_FUNCTION
518                                   ": write of handle %p fd %d error: %s", handle,
519                                   file_private_handle->fd_mapped.fd,
520                                   strerror(errno));
521 #endif
522
523                         _wapi_set_last_error_from_errno ();
524                         return(FALSE);
525                 }
526                 if(byteswritten!=NULL) {
527                         *byteswritten=ret;
528                 }
529                 return(TRUE);
530         }
531
532 #ifndef USE_AIO
533         SetLastError (ERROR_NOT_SUPPORTED);
534         return FALSE;
535 #else
536         if (overlapped == NULL || file_private_handle->callback == NULL) {
537                 SetLastError (ERROR_INVALID_PARAMETER);
538                 return FALSE;
539         }
540
541         {
542         int fd = file_private_handle->fd_mapped.fd;
543         struct aiocb *aio;
544         int result;
545         notifier_data_t *ndata;
546
547         ndata = g_new0 (notifier_data_t, 1);
548         aio = g_new0 (struct aiocb, 1);
549         ndata->overlapped = overlapped;
550         ndata->aio = aio;
551         ndata->callback = file_private_handle->callback;
552
553         aio->aio_fildes = fd;
554         aio->aio_lio_opcode = LIO_WRITE;
555         aio->aio_nbytes = numbytes;
556         aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
557         aio->aio_buf = (gpointer) buffer;
558         aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
559         aio->aio_sigevent.sigev_notify_function = async_notifier;
560         SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
561
562         result = aio_write (aio);
563         if (result == -1) {
564                 _wapi_set_last_error_from_errno ();
565                 return FALSE;
566         }
567
568         result = aio_error (aio);
569 #ifdef DEBUG
570         g_print ("aio_error (write) returned %d for %d\n", result, fd);
571 #endif
572         if (result == 0) {
573                 numbytes = aio_return (aio);
574 #ifdef DEBUG
575         g_print ("numbytes %d for %d\n", numbytes, fd);
576 #endif
577         } else {
578                 errno = result;
579                 _wapi_set_last_error_from_errno ();
580                 return FALSE;
581         }
582
583         if (byteswritten)
584                 *byteswritten = numbytes;
585
586         return TRUE;
587         }
588 #endif
589 }
590
591 static gboolean file_flush(gpointer handle)
592 {
593         struct _WapiHandle_file *file_handle;
594         struct _WapiHandlePrivate_file *file_private_handle;
595         gboolean ok;
596         int ret;
597         
598         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
599                                 (gpointer *)&file_handle,
600                                 (gpointer *)&file_private_handle);
601         if(ok==FALSE) {
602                 g_warning (G_GNUC_PRETTY_FUNCTION
603                            ": error looking up file handle %p", handle);
604                 SetLastError (ERROR_INVALID_HANDLE);
605                 return(FALSE);
606         }
607
608         if (file_private_handle->fd_mapped.assigned == FALSE) {
609                 SetLastError (ERROR_INVALID_HANDLE);
610                 return(FALSE);
611         }
612
613         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
614            !(file_handle->fileaccess&GENERIC_ALL)) {
615 #ifdef DEBUG
616                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
617 #endif
618
619                 SetLastError (ERROR_ACCESS_DENIED);
620                 return(FALSE);
621         }
622
623         ret=fsync(file_private_handle->fd_mapped.fd);
624         if (ret==-1) {
625 #ifdef DEBUG
626                 g_message(G_GNUC_PRETTY_FUNCTION
627                           ": fsync of handle %p fd %d error: %s", handle,
628                           file_private_handle->fd_mapped.fd, strerror(errno));
629 #endif
630
631                 _wapi_set_last_error_from_errno ();
632                 return(FALSE);
633         }
634         
635         return(TRUE);
636 }
637
638 static guint32 file_seek(gpointer handle, gint32 movedistance,
639                          gint32 *highmovedistance, WapiSeekMethod method)
640 {
641         struct _WapiHandle_file *file_handle;
642         struct _WapiHandlePrivate_file *file_private_handle;
643         gboolean ok;
644         off_t offset, newpos;
645         int whence;
646         guint32 ret;
647         
648         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
649                                 (gpointer *)&file_handle,
650                                 (gpointer *)&file_private_handle);
651         if(ok==FALSE) {
652                 g_warning (G_GNUC_PRETTY_FUNCTION
653                            ": error looking up file handle %p", handle);
654                 SetLastError (ERROR_INVALID_HANDLE);
655                 return(INVALID_SET_FILE_POINTER);
656         }
657
658         if (file_private_handle->fd_mapped.assigned == FALSE) {
659                 SetLastError (ERROR_INVALID_HANDLE);
660                 return(FALSE);
661         }
662         
663         if(!(file_handle->fileaccess&GENERIC_READ) &&
664            !(file_handle->fileaccess&GENERIC_WRITE) &&
665            !(file_handle->fileaccess&GENERIC_ALL)) {
666 #ifdef DEBUG
667                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
668 #endif
669
670                 SetLastError (ERROR_ACCESS_DENIED);
671                 return(INVALID_SET_FILE_POINTER);
672         }
673
674         switch(method) {
675         case FILE_BEGIN:
676                 whence=SEEK_SET;
677                 break;
678         case FILE_CURRENT:
679                 whence=SEEK_CUR;
680                 break;
681         case FILE_END:
682                 whence=SEEK_END;
683                 break;
684         default:
685 #ifdef DEBUG
686                 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
687                           method);
688 #endif
689
690                 SetLastError (ERROR_INVALID_PARAMETER);
691                 return(INVALID_SET_FILE_POINTER);
692         }
693
694 #ifdef HAVE_LARGE_FILE_SUPPORT
695         if(highmovedistance==NULL) {
696                 offset=movedistance;
697 #ifdef DEBUG
698                 g_message(G_GNUC_PRETTY_FUNCTION
699                           ": setting offset to %lld (low %d)", offset,
700                           movedistance);
701 #endif
702         } else {
703                 offset=((gint64) *highmovedistance << 32) | (unsigned long)movedistance;
704                 
705 #ifdef DEBUG
706                 g_message(G_GNUC_PRETTY_FUNCTION ": setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
707 #endif
708         }
709 #else
710         offset=movedistance;
711 #endif
712
713 #ifdef DEBUG
714 #ifdef HAVE_LARGE_FILE_SUPPORT
715         g_message(G_GNUC_PRETTY_FUNCTION
716                   ": moving handle %p fd %d by %lld bytes from %d", handle,
717                   file_private_handle->fd_mapped.fd, offset, whence);
718 #else
719         g_message(G_GNUC_PRETTY_FUNCTION
720                   ": moving handle %p fd %d by %ld bytes from %d", handle,
721                   file_private_handle->fd_mapped.fd, offset, whence);
722 #endif
723 #endif
724
725         newpos=lseek(file_private_handle->fd_mapped.fd, offset, whence);
726         if(newpos==-1) {
727 #ifdef DEBUG
728                 g_message(G_GNUC_PRETTY_FUNCTION
729                           ": lseek on handle %p fd %d returned error %s",
730                           handle, file_private_handle->fd_mapped.fd,
731                           strerror(errno));
732 #endif
733
734                 _wapi_set_last_error_from_errno ();
735                 return(INVALID_SET_FILE_POINTER);
736         }
737
738 #ifdef DEBUG
739 #ifdef HAVE_LARGE_FILE_SUPPORT
740         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
741 #else
742         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
743 #endif
744 #endif
745
746 #ifdef HAVE_LARGE_FILE_SUPPORT
747         ret=newpos & 0xFFFFFFFF;
748         if(highmovedistance!=NULL) {
749                 *highmovedistance=newpos>>32;
750         }
751 #else
752         ret=newpos;
753         if(highmovedistance!=NULL) {
754                 /* Accurate, but potentially dodgy :-) */
755                 *highmovedistance=0;
756         }
757 #endif
758
759 #ifdef DEBUG
760         g_message(G_GNUC_PRETTY_FUNCTION
761                   ": move of handle %p fd %d returning %d/%d", handle,
762                   file_private_handle->fd_mapped.fd, ret,
763                   highmovedistance==NULL?0:*highmovedistance);
764 #endif
765
766         return(ret);
767 }
768
769 static gboolean file_setendoffile(gpointer handle)
770 {
771         struct _WapiHandle_file *file_handle;
772         struct _WapiHandlePrivate_file *file_private_handle;
773         gboolean ok;
774         struct stat statbuf;
775         off_t size, pos;
776         int ret;
777         
778         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
779                                 (gpointer *)&file_handle,
780                                 (gpointer *)&file_private_handle);
781         if(ok==FALSE) {
782                 g_warning (G_GNUC_PRETTY_FUNCTION
783                            ": error looking up file handle %p", handle);
784                 SetLastError (ERROR_INVALID_HANDLE);
785                 return(FALSE);
786         }
787
788         if (file_private_handle->fd_mapped.assigned == FALSE) {
789                 SetLastError (ERROR_INVALID_HANDLE);
790                 return(FALSE);
791         }
792         
793         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
794            !(file_handle->fileaccess&GENERIC_ALL)) {
795 #ifdef DEBUG
796                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
797 #endif
798
799                 SetLastError (ERROR_ACCESS_DENIED);
800                 return(FALSE);
801         }
802
803         /* Find the current file position, and the file length.  If
804          * the file position is greater than the length, write to
805          * extend the file with a hole.  If the file position is less
806          * than the length, truncate the file.
807          */
808         
809         ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
810         if(ret==-1) {
811 #ifdef DEBUG
812                 g_message(G_GNUC_PRETTY_FUNCTION
813                           ": handle %p fd %d fstat failed: %s", handle,
814                           file_private_handle->fd_mapped.fd, strerror(errno));
815 #endif
816
817                 _wapi_set_last_error_from_errno ();
818                 return(FALSE);
819         }
820         size=statbuf.st_size;
821
822         pos=lseek(file_private_handle->fd_mapped.fd, (off_t)0, SEEK_CUR);
823         if(pos==-1) {
824 #ifdef DEBUG
825                 g_message(G_GNUC_PRETTY_FUNCTION
826                           ": handle %p fd %d lseek failed: %s", handle,
827                           file_private_handle->fd_mapped.fd, strerror(errno));
828 #endif
829
830                 _wapi_set_last_error_from_errno ();
831                 return(FALSE);
832         }
833         
834         if(pos>size) {
835                 /* extend */
836                 do {
837                         ret=write(file_private_handle->fd_mapped.fd, "", 1);
838                 }
839                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
840
841                 if(ret==-1) {
842 #ifdef DEBUG
843                         g_message(G_GNUC_PRETTY_FUNCTION
844                                   ": handle %p fd %d extend write failed: %s",
845                                   handle, file_private_handle->fd_mapped.fd,
846                                   strerror(errno));
847 #endif
848
849                         _wapi_set_last_error_from_errno ();
850                         return(FALSE);
851                 }
852         }
853
854         /* always truncate, because the extend write() adds an extra
855          * byte to the end of the file
856          */
857         do {
858                 ret=ftruncate(file_private_handle->fd_mapped.fd, pos);
859         }
860         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
861                         
862         if(ret==-1) {
863 #ifdef DEBUG
864                 g_message(G_GNUC_PRETTY_FUNCTION
865                           ": handle %p fd %d ftruncate failed: %s", handle,
866                           file_private_handle->fd_mapped.fd, strerror(errno));
867 #endif
868                 
869                 _wapi_set_last_error_from_errno ();
870                 return(FALSE);
871         }
872                 
873         return(TRUE);
874 }
875
876 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
877 {
878         struct _WapiHandle_file *file_handle;
879         struct _WapiHandlePrivate_file *file_private_handle;
880         gboolean ok;
881         struct stat statbuf;
882         guint32 size;
883         int ret;
884         
885         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
886                                 (gpointer *)&file_handle,
887                                 (gpointer *)&file_private_handle);
888         if(ok==FALSE) {
889                 g_warning (G_GNUC_PRETTY_FUNCTION
890                            ": error looking up file handle %p", handle);
891                 SetLastError (ERROR_INVALID_HANDLE);
892                 return(INVALID_FILE_SIZE);
893         }
894
895         if (file_private_handle->fd_mapped.assigned == FALSE) {
896                 SetLastError (ERROR_INVALID_HANDLE);
897                 return(FALSE);
898         }
899         
900         if(!(file_handle->fileaccess&GENERIC_READ) &&
901            !(file_handle->fileaccess&GENERIC_WRITE) &&
902            !(file_handle->fileaccess&GENERIC_ALL)) {
903 #ifdef DEBUG
904                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
905 #endif
906
907                 SetLastError (ERROR_ACCESS_DENIED);
908                 return(INVALID_FILE_SIZE);
909         }
910
911         ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
912         if(ret==-1) {
913 #ifdef DEBUG
914                 g_message(G_GNUC_PRETTY_FUNCTION
915                           ": handle %p fd %d fstat failed: %s", handle,
916                           file_private_handle->fd_mapped.fd, strerror(errno));
917 #endif
918
919                 _wapi_set_last_error_from_errno ();
920                 return(INVALID_FILE_SIZE);
921         }
922         
923 #ifdef HAVE_LARGE_FILE_SUPPORT
924         size=statbuf.st_size & 0xFFFFFFFF;
925         if(highsize!=NULL) {
926                 *highsize=statbuf.st_size>>32;
927         }
928 #else
929         if(highsize!=NULL) {
930                 /* Accurate, but potentially dodgy :-) */
931                 *highsize=0;
932         }
933         size=statbuf.st_size;
934 #endif
935
936 #ifdef DEBUG
937         g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
938                   *highsize);
939 #endif
940         
941         return(size);
942 }
943
944 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
945                                  WapiFileTime *last_access,
946                                  WapiFileTime *last_write)
947 {
948         struct _WapiHandle_file *file_handle;
949         struct _WapiHandlePrivate_file *file_private_handle;
950         gboolean ok;
951         struct stat statbuf;
952         guint64 create_ticks, access_ticks, write_ticks;
953         int ret;
954         
955         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
956                                 (gpointer *)&file_handle,
957                                 (gpointer *)&file_private_handle);
958         if(ok==FALSE) {
959                 g_warning (G_GNUC_PRETTY_FUNCTION
960                            ": error looking up file handle %p", handle);
961                 SetLastError (ERROR_INVALID_HANDLE);
962                 return(FALSE);
963         }
964
965         if (file_private_handle->fd_mapped.assigned == FALSE) {
966                 SetLastError (ERROR_INVALID_HANDLE);
967                 return(FALSE);
968         }
969         
970         if(!(file_handle->fileaccess&GENERIC_READ) &&
971            !(file_handle->fileaccess&GENERIC_ALL)) {
972 #ifdef DEBUG
973                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
974 #endif
975
976                 SetLastError (ERROR_ACCESS_DENIED);
977                 return(FALSE);
978         }
979         
980         ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
981         if(ret==-1) {
982 #ifdef DEBUG
983                 g_message(G_GNUC_PRETTY_FUNCTION
984                           ": handle %p fd %d fstat failed: %s", handle,
985                           file_private_handle->fd_mapped.fd, strerror(errno));
986 #endif
987
988                 _wapi_set_last_error_from_errno ();
989                 return(FALSE);
990         }
991
992 #ifdef DEBUG
993         g_message(G_GNUC_PRETTY_FUNCTION
994                   ": atime: %ld ctime: %ld mtime: %ld",
995                   statbuf.st_atime, statbuf.st_ctime,
996                   statbuf.st_mtime);
997 #endif
998
999         /* Try and guess a meaningful create time by using the older
1000          * of atime or ctime
1001          */
1002         /* The magic constant comes from msdn documentation
1003          * "Converting a time_t Value to a File Time"
1004          */
1005         if(statbuf.st_atime < statbuf.st_ctime) {
1006                 create_ticks=((guint64)statbuf.st_atime*10000000)
1007                         + 116444736000000000ULL;
1008         } else {
1009                 create_ticks=((guint64)statbuf.st_ctime*10000000)
1010                         + 116444736000000000ULL;
1011         }
1012         
1013         access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
1014         write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
1015         
1016 #ifdef DEBUG
1017                 g_message(G_GNUC_PRETTY_FUNCTION
1018                           ": aticks: %llu cticks: %llu wticks: %llu",
1019                           access_ticks, create_ticks, write_ticks);
1020 #endif
1021
1022         if(create_time!=NULL) {
1023                 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
1024                 create_time->dwHighDateTime = create_ticks >> 32;
1025         }
1026         
1027         if(last_access!=NULL) {
1028                 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
1029                 last_access->dwHighDateTime = access_ticks >> 32;
1030         }
1031         
1032         if(last_write!=NULL) {
1033                 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
1034                 last_write->dwHighDateTime = write_ticks >> 32;
1035         }
1036
1037         return(TRUE);
1038 }
1039
1040 static gboolean file_setfiletime(gpointer handle,
1041                                  const WapiFileTime *create_time G_GNUC_UNUSED,
1042                                  const WapiFileTime *last_access,
1043                                  const WapiFileTime *last_write)
1044 {
1045         struct _WapiHandle_file *file_handle;
1046         struct _WapiHandlePrivate_file *file_private_handle;
1047         gboolean ok;
1048         guchar *name;
1049         struct utimbuf utbuf;
1050         struct stat statbuf;
1051         guint64 access_ticks, write_ticks;
1052         int ret;
1053         
1054         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1055                                 (gpointer *)&file_handle,
1056                                 (gpointer *)&file_private_handle);
1057         if(ok==FALSE) {
1058                 g_warning (G_GNUC_PRETTY_FUNCTION
1059                            ": error looking up file handle %p", handle);
1060                 SetLastError (ERROR_INVALID_HANDLE);
1061                 return(FALSE);
1062         }
1063
1064         if (file_private_handle->fd_mapped.assigned == FALSE) {
1065                 SetLastError (ERROR_INVALID_HANDLE);
1066                 return(FALSE);
1067         }
1068         
1069         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
1070            !(file_handle->fileaccess&GENERIC_ALL)) {
1071 #ifdef DEBUG
1072                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
1073 #endif
1074
1075                 SetLastError (ERROR_ACCESS_DENIED);
1076                 return(FALSE);
1077         }
1078
1079         if(file_handle->filename==0) {
1080 #ifdef DEBUG
1081                 g_message(G_GNUC_PRETTY_FUNCTION
1082                           ": handle %p fd %d unknown filename", handle,
1083                           file_private_handle->fd_mapped.fd);
1084 #endif
1085
1086                 SetLastError (ERROR_INVALID_HANDLE);
1087                 return(FALSE);
1088         }
1089         
1090         /* Get the current times, so we can put the same times back in
1091          * the event that one of the FileTime structs is NULL
1092          */
1093         ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
1094         if(ret==-1) {
1095 #ifdef DEBUG
1096                 g_message(G_GNUC_PRETTY_FUNCTION
1097                           ": handle %p fd %d fstat failed: %s", handle,
1098                           file_private_handle->fd_mapped.fd, strerror(errno));
1099 #endif
1100
1101                 SetLastError (ERROR_INVALID_PARAMETER);
1102                 return(FALSE);
1103         }
1104
1105         if(last_access!=NULL) {
1106                 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
1107                         last_access->dwLowDateTime;
1108                 /* This is (time_t)0.  We can actually go to INT_MIN,
1109                  * but this will do for now.
1110                  */
1111                 if (access_ticks < 116444736000000000ULL) {
1112 #ifdef DEBUG
1113                         g_message (G_GNUC_PRETTY_FUNCTION
1114                                    ": attempt to set access time too early");
1115 #endif
1116                         SetLastError (ERROR_INVALID_PARAMETER);
1117                         return(FALSE);
1118                 }
1119                 
1120                 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1121         } else {
1122                 utbuf.actime=statbuf.st_atime;
1123         }
1124
1125         if(last_write!=NULL) {
1126                 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
1127                         last_write->dwLowDateTime;
1128                 /* This is (time_t)0.  We can actually go to INT_MIN,
1129                  * but this will do for now.
1130                  */
1131                 if (write_ticks < 116444736000000000ULL) {
1132 #ifdef DEBUG
1133                         g_message (G_GNUC_PRETTY_FUNCTION
1134                                    ": attempt to set write time too early");
1135 #endif
1136                         SetLastError (ERROR_INVALID_PARAMETER);
1137                         return(FALSE);
1138                 }
1139                 
1140                 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1141         } else {
1142                 utbuf.modtime=statbuf.st_mtime;
1143         }
1144
1145 #ifdef DEBUG
1146         g_message(G_GNUC_PRETTY_FUNCTION
1147                   ": setting handle %p access %ld write %ld", handle,
1148                   utbuf.actime, utbuf.modtime);
1149 #endif
1150
1151         name=_wapi_handle_scratch_lookup (file_handle->filename);
1152
1153         ret=utime(name, &utbuf);
1154         if(ret==-1) {
1155 #ifdef DEBUG
1156                 g_message(G_GNUC_PRETTY_FUNCTION
1157                           ": handle %p [%s] fd %d utime failed: %s", handle,
1158                           name, file_private_handle->fd_mapped.fd,
1159                           strerror(errno));
1160
1161 #endif
1162                 g_free (name);
1163                 SetLastError (ERROR_INVALID_PARAMETER);
1164                 return(FALSE);
1165         }
1166
1167         g_free (name);
1168         
1169         return(TRUE);
1170 }
1171
1172 static void console_close_shared (gpointer handle)
1173 {
1174         struct _WapiHandle_file *console_handle;
1175         gboolean ok;
1176         
1177         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1178                                 (gpointer *)&console_handle, NULL);
1179         if(ok==FALSE) {
1180                 g_warning (G_GNUC_PRETTY_FUNCTION
1181                            ": error looking up console handle %p", handle);
1182                 SetLastError (ERROR_INVALID_HANDLE);
1183                 return;
1184         }
1185         
1186 #ifdef DEBUG
1187         g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
1188 #endif
1189         
1190         if(console_handle->filename!=0) {
1191                 _wapi_handle_scratch_delete (console_handle->filename);
1192                 console_handle->filename=0;
1193         }
1194         if(console_handle->security_attributes!=0) {
1195                 _wapi_handle_scratch_delete (console_handle->security_attributes);
1196                 console_handle->security_attributes=0;
1197         }
1198 }
1199
1200 static void console_close_private (gpointer handle)
1201 {
1202         struct _WapiHandlePrivate_file *console_private_handle;
1203         gboolean ok;
1204         
1205         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
1206                                 (gpointer *)&console_private_handle);
1207         if(ok==FALSE) {
1208                 g_warning (G_GNUC_PRETTY_FUNCTION
1209                            ": error looking up console handle %p", handle);
1210                 SetLastError (ERROR_INVALID_HANDLE);
1211                 return;
1212         }
1213         
1214         if (console_private_handle->fd_mapped.assigned == TRUE) {
1215 #ifdef DEBUG
1216                 g_message(G_GNUC_PRETTY_FUNCTION
1217                           ": closing console handle %p with fd %d", handle,
1218                           console_private_handle->fd_mapped.fd);
1219 #endif
1220         
1221                 /* Blank out the mapping, to make catching errors easier */
1222                 _wapi_handle_fd_offset_store (console_private_handle->fd_mapped.fd, NULL);
1223         
1224                 close(console_private_handle->fd_mapped.fd);
1225         }
1226 }
1227
1228 static WapiFileType console_getfiletype(void)
1229 {
1230         return(FILE_TYPE_CHAR);
1231 }
1232
1233 static gboolean console_read(gpointer handle, gpointer buffer,
1234                              guint32 numbytes, guint32 *bytesread,
1235                              WapiOverlapped *overlapped G_GNUC_UNUSED)
1236 {
1237         struct _WapiHandle_file *console_handle;
1238         struct _WapiHandlePrivate_file *console_private_handle;
1239         gboolean ok;
1240         int ret;
1241         
1242         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1243                                 (gpointer *)&console_handle,
1244                                 (gpointer *)&console_private_handle);
1245         if(ok==FALSE) {
1246                 g_warning (G_GNUC_PRETTY_FUNCTION
1247                            ": error looking up console handle %p", handle);
1248                 SetLastError (ERROR_INVALID_HANDLE);
1249                 return(FALSE);
1250         }
1251
1252         if (console_private_handle->fd_mapped.assigned == FALSE) {
1253                 SetLastError (ERROR_INVALID_HANDLE);
1254                 return(FALSE);
1255         }
1256         
1257         if(bytesread!=NULL) {
1258                 *bytesread=0;
1259         }
1260         
1261         if(!(console_handle->fileaccess&GENERIC_READ) &&
1262            !(console_handle->fileaccess&GENERIC_ALL)) {
1263 #ifdef DEBUG
1264                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, console_private_handle->fd_mapped.fd, console_handle->fileaccess);
1265 #endif
1266
1267                 SetLastError (ERROR_ACCESS_DENIED);
1268                 return(FALSE);
1269         }
1270         
1271         do {
1272                 ret=read(console_private_handle->fd_mapped.fd, buffer,
1273                          numbytes);
1274         }
1275         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1276
1277         if(ret==-1) {
1278 #ifdef DEBUG
1279                 g_message(G_GNUC_PRETTY_FUNCTION
1280                           ": read of handle %p fd %d error: %s", handle,
1281                           console_private_handle->fd_mapped.fd,
1282                           strerror(errno));
1283 #endif
1284
1285                 _wapi_set_last_error_from_errno ();
1286                 return(FALSE);
1287         }
1288         
1289         if(bytesread!=NULL) {
1290                 *bytesread=ret;
1291         }
1292         
1293         return(TRUE);
1294 }
1295
1296 static gboolean console_write(gpointer handle, gconstpointer buffer,
1297                               guint32 numbytes, guint32 *byteswritten,
1298                               WapiOverlapped *overlapped G_GNUC_UNUSED)
1299 {
1300         struct _WapiHandle_file *console_handle;
1301         struct _WapiHandlePrivate_file *console_private_handle;
1302         gboolean ok;
1303         int ret;
1304         
1305         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1306                                 (gpointer *)&console_handle,
1307                                 (gpointer *)&console_private_handle);
1308         if(ok==FALSE) {
1309                 g_warning (G_GNUC_PRETTY_FUNCTION
1310                            ": error looking up console handle %p", handle);
1311                 SetLastError (ERROR_INVALID_HANDLE);
1312                 return(FALSE);
1313         }
1314
1315         if (console_private_handle->fd_mapped.assigned == FALSE) {
1316                 SetLastError (ERROR_INVALID_HANDLE);
1317                 return(FALSE);
1318         }
1319         
1320         if(byteswritten!=NULL) {
1321                 *byteswritten=0;
1322         }
1323         
1324         if(!(console_handle->fileaccess&GENERIC_WRITE) &&
1325            !(console_handle->fileaccess&GENERIC_ALL)) {
1326 #ifdef DEBUG
1327                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd_mapped.fd, console_handle->fileaccess);
1328 #endif
1329
1330                 SetLastError (ERROR_ACCESS_DENIED);
1331                 return(FALSE);
1332         }
1333         
1334         do {
1335                 ret=write(console_private_handle->fd_mapped.fd, buffer,
1336                           numbytes);
1337         }
1338         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1339
1340         if(ret==-1) {
1341 #ifdef DEBUG
1342                 g_message(G_GNUC_PRETTY_FUNCTION
1343                           ": write of handle %p fd %d error: %s", handle,
1344                           console_private_handle->fd_mapped.fd,
1345                           strerror(errno));
1346 #endif
1347
1348                 _wapi_set_last_error_from_errno ();
1349                 return(FALSE);
1350         }
1351         if(byteswritten!=NULL) {
1352                 *byteswritten=ret;
1353         }
1354         
1355         return(TRUE);
1356 }
1357
1358 static void pipe_close_shared (gpointer handle)
1359 {
1360         struct _WapiHandle_file *pipe_handle;
1361         gboolean ok;
1362         
1363         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1364                                 (gpointer *)&pipe_handle, NULL);
1365         if(ok==FALSE) {
1366                 g_warning (G_GNUC_PRETTY_FUNCTION
1367                            ": error looking up pipe handle %p", handle);
1368                 SetLastError (ERROR_INVALID_HANDLE);
1369                 return;
1370         }
1371         
1372 #ifdef DEBUG
1373         g_message(G_GNUC_PRETTY_FUNCTION ": closing pipe handle %p", handle);
1374 #endif
1375         
1376         if(pipe_handle->filename!=0) {
1377                 _wapi_handle_scratch_delete (pipe_handle->filename);
1378                 pipe_handle->filename=0;
1379         }
1380         if(pipe_handle->security_attributes!=0) {
1381                 _wapi_handle_scratch_delete (pipe_handle->security_attributes);
1382                 pipe_handle->security_attributes=0;
1383         }
1384 }
1385
1386 static void pipe_close_private (gpointer handle)
1387 {
1388         struct _WapiHandlePrivate_file *pipe_private_handle;
1389         gboolean ok;
1390         
1391         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
1392                                 (gpointer *)&pipe_private_handle);
1393         if(ok==FALSE) {
1394                 g_warning (G_GNUC_PRETTY_FUNCTION
1395                            ": error looking up pipe handle %p", handle);
1396                 SetLastError (ERROR_INVALID_HANDLE);
1397                 return;
1398         }
1399         
1400         if (pipe_private_handle->fd_mapped.assigned == TRUE) {
1401 #ifdef DEBUG
1402                 g_message(G_GNUC_PRETTY_FUNCTION
1403                           ": closing pipe handle %p with fd %d", handle,
1404                           pipe_private_handle->fd_mapped.fd);
1405 #endif
1406         
1407                 /* Blank out the mapping, to make catching errors easier */
1408                 _wapi_handle_fd_offset_store (pipe_private_handle->fd_mapped.fd, NULL);
1409         
1410                 close(pipe_private_handle->fd_mapped.fd);
1411         }
1412 }
1413
1414 static WapiFileType pipe_getfiletype(void)
1415 {
1416         return(FILE_TYPE_PIPE);
1417 }
1418
1419 static gboolean pipe_read (gpointer handle, gpointer buffer,
1420                            guint32 numbytes, guint32 *bytesread,
1421                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1422 {
1423         struct _WapiHandle_file *pipe_handle;
1424         struct _WapiHandlePrivate_file *pipe_private_handle;
1425         gboolean ok;
1426         int ret;
1427         
1428         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1429                                 (gpointer *)&pipe_handle,
1430                                 (gpointer *)&pipe_private_handle);
1431         if(ok==FALSE) {
1432                 g_warning (G_GNUC_PRETTY_FUNCTION
1433                            ": error looking up pipe handle %p", handle);
1434                 SetLastError (ERROR_INVALID_HANDLE);
1435                 return(FALSE);
1436         }
1437
1438         if (pipe_private_handle->fd_mapped.assigned == FALSE) {
1439                 SetLastError (ERROR_INVALID_HANDLE);
1440                 return(FALSE);
1441         }
1442
1443         if(bytesread!=NULL) {
1444                 *bytesread=0;
1445         }
1446         
1447         if(!(pipe_handle->fileaccess&GENERIC_READ) &&
1448            !(pipe_handle->fileaccess&GENERIC_ALL)) {
1449 #ifdef DEBUG
1450                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, pipe_private_handle->fd_mapped.fd, pipe_handle->fileaccess);
1451 #endif
1452
1453                 SetLastError (ERROR_ACCESS_DENIED);
1454                 return(FALSE);
1455         }
1456         
1457 #ifdef DEBUG
1458         g_message (G_GNUC_PRETTY_FUNCTION
1459                    ": reading up to %d bytes from pipe %p (fd %d)", numbytes,
1460                    handle, pipe_private_handle->fd_mapped.fd);
1461 #endif
1462
1463         do {
1464                 ret=read(pipe_private_handle->fd_mapped.fd, buffer, numbytes);
1465         }
1466         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1467                 
1468         if(ret==-1) {
1469 #ifdef DEBUG
1470                 g_message(G_GNUC_PRETTY_FUNCTION
1471                           ": read of handle %p fd %d error: %s", handle,
1472                           pipe_private_handle->fd_mapped.fd, strerror(errno));
1473 #endif
1474
1475                 _wapi_set_last_error_from_errno ();
1476                 return(FALSE);
1477         }
1478         
1479 #ifdef DEBUG
1480         g_message (G_GNUC_PRETTY_FUNCTION ": read %d bytes from pipe", ret);
1481 #endif
1482
1483         if(bytesread!=NULL) {
1484                 *bytesread=ret;
1485         }
1486         
1487         return(TRUE);
1488 }
1489
1490 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1491                            guint32 numbytes, guint32 *byteswritten,
1492                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1493 {
1494         struct _WapiHandle_file *pipe_handle;
1495         struct _WapiHandlePrivate_file *pipe_private_handle;
1496         gboolean ok;
1497         int ret;
1498         
1499         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1500                                 (gpointer *)&pipe_handle,
1501                                 (gpointer *)&pipe_private_handle);
1502         if(ok==FALSE) {
1503                 g_warning (G_GNUC_PRETTY_FUNCTION
1504                            ": error looking up pipe handle %p", handle);
1505                 SetLastError (ERROR_INVALID_HANDLE);
1506                 return(FALSE);
1507         }
1508
1509         if (pipe_private_handle->fd_mapped.assigned == FALSE) {
1510                 SetLastError (ERROR_INVALID_HANDLE);
1511                 return(FALSE);
1512         }
1513         
1514         if(byteswritten!=NULL) {
1515                 *byteswritten=0;
1516         }
1517         
1518         if(!(pipe_handle->fileaccess&GENERIC_WRITE) &&
1519            !(pipe_handle->fileaccess&GENERIC_ALL)) {
1520 #ifdef DEBUG
1521                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, pipe_private_handle->fd_mapped.fd, pipe_handle->fileaccess);
1522 #endif
1523
1524                 SetLastError (ERROR_ACCESS_DENIED);
1525                 return(FALSE);
1526         }
1527         
1528 #ifdef DEBUG
1529         g_message (G_GNUC_PRETTY_FUNCTION
1530                    ": writing up to %d bytes to pipe %p (fd %d)", numbytes,
1531                    handle, pipe_private_handle->fd_mapped.fd);
1532 #endif
1533
1534         do {
1535                 ret=write(pipe_private_handle->fd_mapped.fd, buffer, numbytes);
1536         }
1537         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1538
1539         if(ret==-1) {
1540 #ifdef DEBUG
1541                 g_message(G_GNUC_PRETTY_FUNCTION
1542                           ": write of handle %p fd %d error: %s", handle,
1543                           pipe_private_handle->fd_mapped.fd, strerror(errno));
1544 #endif
1545
1546                 _wapi_set_last_error_from_errno ();
1547                 return(FALSE);
1548         }
1549         if(byteswritten!=NULL) {
1550                 *byteswritten=ret;
1551         }
1552         
1553         return(TRUE);
1554 }
1555
1556 static int convert_flags(guint32 fileaccess, guint32 createmode)
1557 {
1558         int flags=0;
1559         
1560         switch(fileaccess) {
1561         case GENERIC_READ:
1562                 flags=O_RDONLY;
1563                 break;
1564         case GENERIC_WRITE:
1565                 flags=O_WRONLY;
1566                 break;
1567         case GENERIC_READ|GENERIC_WRITE:
1568                 flags=O_RDWR;
1569                 break;
1570         default:
1571 #ifdef DEBUG
1572                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
1573                           fileaccess);
1574 #endif
1575                 break;
1576         }
1577
1578         switch(createmode) {
1579         case CREATE_NEW:
1580                 flags|=O_CREAT|O_EXCL;
1581                 break;
1582         case CREATE_ALWAYS:
1583                 flags|=O_CREAT|O_TRUNC;
1584                 break;
1585         case OPEN_EXISTING:
1586                 break;
1587         case OPEN_ALWAYS:
1588                 flags|=O_CREAT;
1589                 break;
1590         case TRUNCATE_EXISTING:
1591                 flags|=O_TRUNC;
1592                 break;
1593         default:
1594 #ifdef DEBUG
1595                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
1596                           createmode);
1597 #endif
1598                 break;
1599         }
1600         
1601         return(flags);
1602 }
1603
1604 static guint32 convert_from_flags(int flags)
1605 {
1606         guint32 fileaccess=0;
1607         
1608 #ifndef O_ACCMODE
1609 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1610 #endif
1611
1612         if((flags & O_ACCMODE) == O_RDONLY) {
1613                 fileaccess=GENERIC_READ;
1614         } else if ((flags & O_ACCMODE) == O_WRONLY) {
1615                 fileaccess=GENERIC_WRITE;
1616         } else if ((flags & O_ACCMODE) == O_RDWR) {
1617                 fileaccess=GENERIC_READ|GENERIC_WRITE;
1618         } else {
1619 #ifdef DEBUG
1620                 g_message(G_GNUC_PRETTY_FUNCTION
1621                           ": Can't figure out flags 0x%x", flags);
1622 #endif
1623         }
1624
1625         /* Maybe sort out create mode too */
1626
1627         return(fileaccess);
1628 }
1629
1630 static mode_t convert_perms(guint32 sharemode)
1631 {
1632         mode_t perms=0600;
1633         
1634         if(sharemode&FILE_SHARE_READ) {
1635                 perms|=044;
1636         }
1637         if(sharemode&FILE_SHARE_WRITE) {
1638                 perms|=022;
1639         }
1640
1641         return(perms);
1642 }
1643
1644
1645 /**
1646  * CreateFile:
1647  * @name: a pointer to a NULL-terminated unicode string, that names
1648  * the file or other object to create.
1649  * @fileaccess: specifies the file access mode
1650  * @sharemode: whether the file should be shared.  This parameter is
1651  * currently ignored.
1652  * @security: Ignored for now.
1653  * @createmode: specifies whether to create a new file, whether to
1654  * overwrite an existing file, whether to truncate the file, etc.
1655  * @attrs: specifies file attributes and flags.  On win32 attributes
1656  * are characteristics of the file, not the handle, and are ignored
1657  * when an existing file is opened.  Flags give the library hints on
1658  * how to process a file to optimise performance.
1659  * @template: the handle of an open %GENERIC_READ file that specifies
1660  * attributes to apply to a newly created file, ignoring @attrs.
1661  * Normally this parameter is NULL.  This parameter is ignored when an
1662  * existing file is opened.
1663  *
1664  * Creates a new file handle.  This only applies to normal files:
1665  * pipes are handled by CreatePipe(), and console handles are created
1666  * with GetStdHandle().
1667  *
1668  * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1669  */
1670 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1671                     guint32 sharemode, WapiSecurityAttributes *security,
1672                     guint32 createmode, guint32 attrs,
1673                     gpointer template G_GNUC_UNUSED)
1674 {
1675         struct _WapiHandle_file *file_handle;
1676         struct _WapiHandlePrivate_file *file_private_handle;
1677         gpointer handle;
1678         gboolean ok;
1679         int flags=convert_flags(fileaccess, createmode);
1680         /*mode_t perms=convert_perms(sharemode);*/
1681         /* we don't use sharemode, because that relates to sharing of the file
1682          * when the file is open and is already handled by other code, perms instead
1683          * are the on-disk permissions and this is a sane default.
1684          */
1685         mode_t perms=0644;
1686         gchar *filename;
1687         int fd, ret;
1688         int thr_ret;
1689         gpointer cf_ret = INVALID_HANDLE_VALUE;
1690         struct stat statbuf;
1691         gboolean file_already_shared;
1692         guint32 file_existing_share, file_existing_access;
1693         
1694         mono_once (&io_ops_once, io_ops_init);
1695
1696         if(name==NULL) {
1697 #ifdef DEBUG
1698                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1699 #endif
1700
1701                 SetLastError (ERROR_INVALID_NAME);
1702                 return(INVALID_HANDLE_VALUE);
1703         }
1704
1705         filename=mono_unicode_to_external (name);
1706         if(filename==NULL) {
1707 #ifdef DEBUG
1708                 g_message(G_GNUC_PRETTY_FUNCTION
1709                           ": unicode conversion returned NULL");
1710 #endif
1711
1712                 SetLastError (ERROR_INVALID_NAME);
1713                 return(INVALID_HANDLE_VALUE);
1714         }
1715         
1716 #ifdef DEBUG
1717         g_message (G_GNUC_PRETTY_FUNCTION ": Opening %s with share 0x%x and access 0x%x", filename, sharemode, fileaccess);
1718 #endif
1719         
1720         fd = open(filename, flags, perms);
1721     
1722         /* If we were trying to open a directory with write permissions
1723          * (e.g. O_WRONLY or O_RDWR), this call will fail with
1724          * EISDIR. However, this is a bit bogus because calls to
1725          * manipulate the directory (e.g. SetFileTime) will still work on
1726          * the directory because they use other API calls
1727          * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1728          * to open the directory again without write permission.
1729          */
1730         if (fd == -1 && errno == EISDIR)
1731         {
1732                 /* Try again but don't try to make it writable */
1733                 fd = open(filename, flags  & ~(O_RDWR|O_WRONLY), perms);
1734         }
1735         
1736         if (fd == -1) {
1737 #ifdef DEBUG
1738                 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
1739                           filename, strerror(errno));
1740 #endif
1741                 _wapi_set_last_error_from_errno ();
1742                 g_free (filename);
1743
1744                 return(INVALID_HANDLE_VALUE);
1745         }
1746
1747         if (fd >= _wapi_fd_offset_table_size) {
1748 #ifdef DEBUG
1749                 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
1750 #endif
1751
1752                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1753                 
1754                 close (fd);
1755                 g_free (filename);
1756                 
1757                 return(INVALID_HANDLE_VALUE);
1758         }
1759
1760         ret = fstat (fd, &statbuf);
1761         if (ret == -1) {
1762 #ifdef DEBUG
1763                 g_message (G_GNUC_PRETTY_FUNCTION ": fstat error of file %s: %s", filename, strerror (errno));
1764 #endif
1765                 _wapi_set_last_error_from_errno ();
1766                 g_free (filename);
1767                 close (fd);
1768                 
1769                 return(INVALID_HANDLE_VALUE);
1770         }
1771
1772         file_already_shared = _wapi_handle_get_or_set_share (statbuf.st_dev, statbuf.st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access);
1773         
1774         if (file_already_shared) {
1775                 if (file_existing_share == 0) {
1776                         /* Quick and easy, no possibility to share */
1777 #ifdef DEBUG
1778                         g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing = NONE", fileaccess);
1779 #endif
1780                         SetLastError (ERROR_SHARING_VIOLATION);
1781                         g_free (filename);
1782                         close (fd);
1783                 
1784                         return(INVALID_HANDLE_VALUE);
1785                 }
1786
1787                 if (((file_existing_share == FILE_SHARE_READ) &&
1788                      (fileaccess != GENERIC_READ)) ||
1789                     ((file_existing_share == FILE_SHARE_WRITE) &&
1790                      (fileaccess != GENERIC_WRITE))) {
1791                         /* New access mode doesn't match up */
1792 #ifdef DEBUG
1793                         g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", fileaccess, file_existing_share);
1794 #endif
1795                         SetLastError (ERROR_SHARING_VIOLATION);
1796                         g_free (filename);
1797                         close (fd);
1798                 
1799                         return(INVALID_HANDLE_VALUE);
1800                 }
1801
1802                 if (((file_existing_access & GENERIC_READ) &&
1803                      !(sharemode & FILE_SHARE_READ)) ||
1804                     ((file_existing_access & GENERIC_WRITE) &&
1805                      !(sharemode & FILE_SHARE_WRITE))) {
1806                         /* New share mode doesn't match up */
1807 #ifdef DEBUG
1808                         g_message (G_GNUC_PRETTY_FUNCTION ": Access mode prevents open: requested share: 0x%x, file has access: 0x%x", sharemode, file_existing_access);
1809 #endif
1810                         SetLastError (ERROR_SHARING_VIOLATION);
1811                         g_free (filename);
1812                         close (fd);
1813                 
1814                         return(INVALID_HANDLE_VALUE);
1815                 }
1816         } else {
1817 #ifdef DEBUG
1818                 g_message (G_GNUC_PRETTY_FUNCTION ": New file!");
1819 #endif
1820         }
1821         
1822         handle=_wapi_handle_new (WAPI_HANDLE_FILE);
1823         if(handle==_WAPI_HANDLE_INVALID) {
1824                 g_warning (G_GNUC_PRETTY_FUNCTION
1825                            ": error creating file handle");
1826                 g_free (filename);
1827                 close (fd);
1828                 
1829                 SetLastError (ERROR_GEN_FAILURE);
1830                 return(INVALID_HANDLE_VALUE);
1831         }
1832
1833         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
1834                               handle);
1835         thr_ret = _wapi_handle_lock_handle (handle);
1836         g_assert (thr_ret == 0);
1837         
1838         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1839                                 (gpointer *)&file_handle,
1840                                 (gpointer *)&file_private_handle);
1841         if(ok==FALSE) {
1842                 g_warning (G_GNUC_PRETTY_FUNCTION
1843                            ": error looking up file handle %p", handle);
1844                 SetLastError (ERROR_INVALID_HANDLE);
1845                 close (fd);
1846                 goto cleanup;
1847         }
1848
1849         _wapi_handle_fd_offset_store (fd, handle);
1850         cf_ret = GINT_TO_POINTER (fd);
1851
1852         file_private_handle->fd_mapped.fd=fd;
1853         file_private_handle->fd_mapped.assigned=TRUE;
1854         file_private_handle->async = ((attrs & FILE_FLAG_OVERLAPPED) != 0);
1855         file_handle->filename=_wapi_handle_scratch_store (filename,
1856                                                           strlen (filename));
1857         if(security!=NULL) {
1858                 file_handle->security_attributes=_wapi_handle_scratch_store (
1859                         security, sizeof(WapiSecurityAttributes));
1860         }
1861         
1862         file_handle->fileaccess=fileaccess;
1863         file_handle->sharemode=sharemode;
1864         file_handle->attrs=attrs;
1865         file_handle->device = statbuf.st_dev;
1866         file_handle->inode = statbuf.st_ino;
1867         
1868 #ifdef DEBUG
1869         g_message(G_GNUC_PRETTY_FUNCTION
1870                   ": returning handle %p with fd %d", handle,
1871                   file_private_handle->fd_mapped.fd);
1872 #endif
1873
1874 cleanup:
1875         thr_ret = _wapi_handle_unlock_handle (handle);
1876         g_assert (thr_ret == 0);
1877         pthread_cleanup_pop (0);
1878         
1879         g_free (filename);
1880         
1881         return(cf_ret);
1882 }
1883
1884 /**
1885  * DeleteFile:
1886  * @name: a pointer to a NULL-terminated unicode string, that names
1887  * the file to be deleted.
1888  *
1889  * Deletes file @name.
1890  *
1891  * Return value: %TRUE on success, %FALSE otherwise.
1892  */
1893 gboolean DeleteFile(const gunichar2 *name)
1894 {
1895         gchar *filename;
1896         int ret;
1897         
1898         if(name==NULL) {
1899 #ifdef DEBUG
1900                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1901 #endif
1902
1903                 SetLastError (ERROR_INVALID_NAME);
1904                 return(FALSE);
1905         }
1906
1907         filename=mono_unicode_to_external(name);
1908         if(filename==NULL) {
1909 #ifdef DEBUG
1910                 g_message(G_GNUC_PRETTY_FUNCTION
1911                           ": unicode conversion returned NULL");
1912 #endif
1913
1914                 SetLastError (ERROR_INVALID_NAME);
1915                 return(FALSE);
1916         }
1917         
1918         ret=unlink(filename);
1919         
1920         g_free(filename);
1921
1922         if(ret==0) {
1923                 return(TRUE);
1924         }
1925
1926         _wapi_set_last_error_from_errno ();
1927         return(FALSE);
1928 }
1929
1930 /**
1931  * MoveFile:
1932  * @name: a pointer to a NULL-terminated unicode string, that names
1933  * the file to be moved.
1934  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1935  * new name for the file.
1936  *
1937  * Renames file @name to @dest_name.
1938  * MoveFile sets ERROR_ALREADY_EXISTS if the destination exists, except
1939  * when it is the same file as the source.  In that case it silently succeeds.
1940  *
1941  * Return value: %TRUE on success, %FALSE otherwise.
1942  */
1943 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1944 {
1945         gchar *utf8_name, *utf8_dest_name;
1946         int result;
1947         struct stat stat_src, stat_dest;
1948         
1949         if(name==NULL) {
1950 #ifdef DEBUG
1951                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1952 #endif
1953
1954                 SetLastError (ERROR_INVALID_NAME);
1955                 return(FALSE);
1956         }
1957
1958         utf8_name = mono_unicode_to_external (name);
1959         if (utf8_name == NULL) {
1960 #ifdef DEBUG
1961                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1962 #endif
1963                 
1964                 SetLastError (ERROR_INVALID_NAME);
1965                 return FALSE;
1966         }
1967         
1968         if(dest_name==NULL) {
1969 #ifdef DEBUG
1970                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1971 #endif
1972
1973                 g_free (utf8_name);
1974                 SetLastError (ERROR_INVALID_NAME);
1975                 return(FALSE);
1976         }
1977
1978         utf8_dest_name = mono_unicode_to_external (dest_name);
1979         if (utf8_dest_name == NULL) {
1980 #ifdef DEBUG
1981                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1982 #endif
1983
1984                 g_free (utf8_name);
1985                 SetLastError (ERROR_INVALID_NAME);
1986                 return FALSE;
1987         }
1988
1989         /*
1990          * In C# land we check for the existence of src, but not for dest.
1991          * We check it here and return the failure if dest exists and is not
1992          * the same file as src.
1993          */
1994         if (!stat (utf8_dest_name, &stat_dest) && !stat (utf8_name, &stat_src)) {
1995                 if (stat_dest.st_dev != stat_src.st_dev || stat_dest.st_ino != stat_src.st_ino) {
1996                         SetLastError (ERROR_ALREADY_EXISTS);
1997                         return FALSE;
1998                 }       
1999         }
2000
2001         result = rename (utf8_name, utf8_dest_name);
2002         g_free (utf8_name);
2003         g_free (utf8_dest_name);
2004
2005         if (result != 0 && errno == EXDEV) {
2006                 /* Try a copy to the new location, and delete the source */
2007                 if (CopyFile (name, dest_name, TRUE)==FALSE) {
2008                         /* CopyFile will set the error */
2009                         return(FALSE);
2010                 }
2011                 
2012                 return(DeleteFile (name));
2013         }
2014
2015         if (result == 0) {
2016                 return TRUE;
2017         }
2018         
2019         switch (errno) {
2020         case EEXIST:
2021                 SetLastError (ERROR_ALREADY_EXISTS);
2022                 break;
2023         
2024         default:
2025                 _wapi_set_last_error_from_errno ();
2026                 break;
2027         }
2028
2029         return FALSE;
2030 }
2031
2032 /**
2033  * CopyFile:
2034  * @name: a pointer to a NULL-terminated unicode string, that names
2035  * the file to be copied.
2036  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
2037  * new name for the file.
2038  * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
2039  *
2040  * Copies file @name to @dest_name
2041  *
2042  * Return value: %TRUE on success, %FALSE otherwise.
2043  */
2044 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
2045                    gboolean fail_if_exists)
2046 {
2047         gchar *utf8_src, *utf8_dest;
2048         int src_fd, dest_fd;
2049         int buf_size;
2050         char *buf;
2051         int remain, n;
2052         struct stat st;
2053         
2054         if(name==NULL) {
2055 #ifdef DEBUG
2056                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2057 #endif
2058
2059                 SetLastError (ERROR_INVALID_NAME);
2060                 return(FALSE);
2061         }
2062         
2063         utf8_src = mono_unicode_to_external (name);
2064         if (utf8_src == NULL) {
2065 #ifdef DEBUG
2066                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of source returned NULL");
2067 #endif
2068
2069                 SetLastError (ERROR_INVALID_PARAMETER);
2070                 return(FALSE);
2071         }
2072         
2073         if(dest_name==NULL) {
2074 #ifdef DEBUG
2075                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2076 #endif
2077
2078                 g_free (utf8_src);
2079                 SetLastError (ERROR_INVALID_NAME);
2080                 return(FALSE);
2081         }
2082         
2083         utf8_dest = mono_unicode_to_external (dest_name);
2084         if (utf8_dest == NULL) {
2085 #ifdef DEBUG
2086                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of dest returned NULL");
2087 #endif
2088
2089                 SetLastError (ERROR_INVALID_PARAMETER);
2090
2091                 g_free (utf8_src);
2092                 
2093                 return(FALSE);
2094         }
2095         
2096         src_fd = open (utf8_src, O_RDONLY);
2097         if (src_fd < 0) {
2098                 _wapi_set_last_error_from_errno ();
2099
2100                 g_free (utf8_src);
2101                 g_free (utf8_dest);
2102                 
2103                 return(FALSE);
2104         }
2105
2106         if (fstat (src_fd, &st) < 0) {
2107                 _wapi_set_last_error_from_errno ();
2108
2109                 g_free (utf8_src);
2110                 g_free (utf8_dest);
2111                 close (src_fd);
2112                 
2113                 return(FALSE);
2114         }
2115         
2116         if (fail_if_exists) {
2117                 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
2118         } else {
2119                 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2120                 if (dest_fd < 0) {
2121                         /* O_TRUNC might cause a fail if the file
2122                          * doesn't exist
2123                          */
2124                         dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
2125                                         st.st_mode);
2126                 }
2127         }
2128         if (dest_fd < 0) {
2129                 _wapi_set_last_error_from_errno ();
2130
2131                 g_free (utf8_src);
2132                 g_free (utf8_dest);
2133                 close (src_fd);
2134                 
2135                 return(FALSE);
2136         }
2137         
2138         buf_size = st.st_blksize;
2139         buf = (char *) alloca (buf_size);
2140         
2141         for (;;) {
2142                 remain = read (src_fd, buf, buf_size);
2143                 
2144                 if (remain < 0) {
2145                         if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
2146                                 continue;
2147                         }
2148                         
2149                         _wapi_set_last_error_from_errno ();
2150
2151                         g_free (utf8_src);
2152                         g_free (utf8_dest);
2153                         close (src_fd);
2154                         close (dest_fd);
2155                         
2156                         return(FALSE);
2157                 }
2158                 
2159                 if (remain == 0) {
2160                         break;
2161                 }
2162
2163                 while (remain > 0) {
2164                         if ((n = write (dest_fd, buf, remain)) < 0) {
2165                                 if (errno == EINTR && !_wapi_thread_cur_apc_pending())
2166                                         continue;
2167
2168                                 _wapi_set_last_error_from_errno ();
2169 #ifdef DEBUG
2170                                 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
2171 #endif
2172
2173                                 g_free (utf8_src);
2174                                 g_free (utf8_dest);
2175                                 close (src_fd);
2176                                 close (dest_fd);
2177
2178                                 return (FALSE);
2179                         }
2180
2181                         remain -= n;
2182                 }
2183         }
2184
2185         g_free (utf8_src);
2186         g_free (utf8_dest);
2187         close (src_fd);
2188         close (dest_fd);
2189
2190         return(TRUE);
2191 }
2192
2193 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
2194 static gpointer stdin_handle=INVALID_HANDLE_VALUE;
2195 static gpointer stdout_handle=INVALID_HANDLE_VALUE;
2196 static gpointer stderr_handle=INVALID_HANDLE_VALUE;
2197
2198 static gpointer stdhandle_create (int fd, const guchar *name)
2199 {
2200         struct _WapiHandle_file *file_handle;
2201         struct _WapiHandlePrivate_file *file_private_handle;
2202         gboolean ok;
2203         gpointer handle, ret = INVALID_HANDLE_VALUE;
2204         int flags;
2205         int thr_ret;
2206         
2207 #ifdef DEBUG
2208         g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
2209                   name);
2210 #endif
2211         
2212         /* Check if fd is valid */
2213         do {
2214                 flags=fcntl(fd, F_GETFL);
2215         }
2216         while (flags==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
2217
2218         if(flags==-1) {
2219                 /* Invalid fd.  Not really much point checking for EBADF
2220                  * specifically
2221                  */
2222 #ifdef DEBUG
2223                 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
2224                           fd, strerror(errno));
2225 #endif
2226
2227                 _wapi_set_last_error_from_errno ();
2228                 return(INVALID_HANDLE_VALUE);
2229         }
2230
2231         handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
2232         if(handle==_WAPI_HANDLE_INVALID) {
2233                 g_warning (G_GNUC_PRETTY_FUNCTION
2234                            ": error creating file handle");
2235                 SetLastError (ERROR_GEN_FAILURE);
2236                 return(INVALID_HANDLE_VALUE);
2237         }
2238
2239         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2240                               handle);
2241         thr_ret = _wapi_handle_lock_handle (handle);
2242         g_assert (thr_ret == 0);
2243         
2244         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2245                                 (gpointer *)&file_handle,
2246                                 (gpointer *)&file_private_handle);
2247         if(ok==FALSE) {
2248                 g_warning (G_GNUC_PRETTY_FUNCTION
2249                            ": error looking up console handle %p", handle);
2250                 SetLastError (ERROR_INVALID_HANDLE);
2251                 goto cleanup;
2252         }
2253
2254         /* We know this is fd 0, 1 or 2 */
2255         _wapi_handle_fd_offset_store (fd, handle);
2256         ret = GINT_TO_POINTER (fd);
2257         
2258         file_private_handle->fd_mapped.fd=fd;
2259         file_private_handle->fd_mapped.assigned=TRUE;
2260         file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
2261         /* some default security attributes might be needed */
2262         file_handle->security_attributes=0;
2263         file_handle->fileaccess=convert_from_flags(flags);
2264         file_handle->sharemode=0;
2265         file_handle->attrs=0;
2266         
2267 #ifdef DEBUG
2268         g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
2269                   handle, file_private_handle->fd_mapped.fd);
2270 #endif
2271
2272 cleanup:
2273         thr_ret = _wapi_handle_unlock_handle (handle);
2274         g_assert (thr_ret == 0);
2275         pthread_cleanup_pop (0);
2276
2277         return(ret);
2278 }
2279
2280 static void stdhandle_init (void)
2281 {
2282         stdin_handle=stdhandle_create (0, "<stdin>");
2283         stdout_handle=stdhandle_create (1, "<stdout>");
2284         stderr_handle=stdhandle_create (2, "<stderr>");
2285 }
2286
2287 /**
2288  * GetStdHandle:
2289  * @stdhandle: specifies the file descriptor
2290  *
2291  * Returns a handle for stdin, stdout, or stderr.  Always returns the
2292  * same handle for the same @stdhandle.
2293  *
2294  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2295  */
2296
2297 gpointer GetStdHandle(WapiStdHandle stdhandle)
2298 {
2299         gpointer handle;
2300         
2301         mono_once (&io_ops_once, io_ops_init);
2302         mono_once (&stdhandle_once, stdhandle_init);
2303         
2304         switch(stdhandle) {
2305         case STD_INPUT_HANDLE:
2306                 handle=stdin_handle;
2307                 break;
2308
2309         case STD_OUTPUT_HANDLE:
2310                 handle=stdout_handle;
2311                 break;
2312
2313         case STD_ERROR_HANDLE:
2314                 handle=stderr_handle;
2315                 break;
2316
2317         default:
2318 #ifdef DEBUG
2319                 g_message(G_GNUC_PRETTY_FUNCTION
2320                           ": unknown standard handle type");
2321 #endif
2322
2323                 SetLastError (ERROR_INVALID_PARAMETER);
2324                 return(INVALID_HANDLE_VALUE);
2325         }
2326
2327         if (handle == INVALID_HANDLE_VALUE) {
2328                 SetLastError (ERROR_NO_MORE_FILES);
2329                 return(INVALID_HANDLE_VALUE);
2330         }
2331         
2332         /* Add a reference to this handle */
2333         _wapi_handle_ref (_wapi_handle_fd_offset_to_handle (handle));
2334         
2335         return(handle);
2336 }
2337
2338 /**
2339  * ReadFile:
2340  * @handle: The file handle to read from.  The handle must have
2341  * %GENERIC_READ access.
2342  * @buffer: The buffer to store read data in
2343  * @numbytes: The maximum number of bytes to read
2344  * @bytesread: The actual number of bytes read is stored here.  This
2345  * value can be zero if the handle is positioned at the end of the
2346  * file.
2347  * @overlapped: points to a required %WapiOverlapped structure if
2348  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2349  * otherwise.
2350  *
2351  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2352  * function reads up to @numbytes bytes from the file from the current
2353  * file position, and stores them in @buffer.  If there are not enough
2354  * bytes left in the file, just the amount available will be read.
2355  * The actual number of bytes read is stored in @bytesread.
2356
2357  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2358  * file position is ignored and the read position is taken from data
2359  * in the @overlapped structure.
2360  *
2361  * Return value: %TRUE if the read succeeds (even if no bytes were
2362  * read due to an attempt to read past the end of the file), %FALSE on
2363  * error.
2364  */
2365 gboolean ReadFile(gpointer fd_handle, gpointer buffer, guint32 numbytes,
2366                   guint32 *bytesread, WapiOverlapped *overlapped)
2367 {
2368         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2369         WapiHandleType type;
2370
2371         if (handle == NULL) {
2372                 SetLastError (ERROR_INVALID_HANDLE);
2373                 return(FALSE);
2374         }
2375         
2376         type = _wapi_handle_type (handle);
2377         
2378         if(io_ops[type].readfile==NULL) {
2379                 SetLastError (ERROR_INVALID_HANDLE);
2380                 return(FALSE);
2381         }
2382         
2383         return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2384                                       overlapped));
2385 }
2386
2387 /**
2388  * WriteFile:
2389  * @handle: The file handle to write to.  The handle must have
2390  * %GENERIC_WRITE access.
2391  * @buffer: The buffer to read data from.
2392  * @numbytes: The maximum number of bytes to write.
2393  * @byteswritten: The actual number of bytes written is stored here.
2394  * If the handle is positioned at the file end, the length of the file
2395  * is extended.  This parameter may be %NULL.
2396  * @overlapped: points to a required %WapiOverlapped structure if
2397  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2398  * otherwise.
2399  *
2400  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2401  * function writes up to @numbytes bytes from @buffer to the file at
2402  * the current file position.  If @handle is positioned at the end of
2403  * the file, the file is extended.  The actual number of bytes written
2404  * is stored in @byteswritten.
2405  *
2406  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2407  * file position is ignored and the write position is taken from data
2408  * in the @overlapped structure.
2409  *
2410  * Return value: %TRUE if the write succeeds, %FALSE on error.
2411  */
2412 gboolean WriteFile(gpointer fd_handle, gconstpointer buffer, guint32 numbytes,
2413                    guint32 *byteswritten, WapiOverlapped *overlapped)
2414 {
2415         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2416         WapiHandleType type;
2417
2418         if (handle == NULL) {
2419                 SetLastError (ERROR_INVALID_HANDLE);
2420                 return(FALSE);
2421         }
2422         
2423         type = _wapi_handle_type (handle);
2424         
2425         if(io_ops[type].writefile==NULL) {
2426                 SetLastError (ERROR_INVALID_HANDLE);
2427                 return(FALSE);
2428         }
2429         
2430         return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2431                                        overlapped));
2432 }
2433
2434 /**
2435  * FlushFileBuffers:
2436  * @handle: Handle to open file.  The handle must have
2437  * %GENERIC_WRITE access.
2438  *
2439  * Flushes buffers of the file and causes all unwritten data to
2440  * be written.
2441  *
2442  * Return value: %TRUE on success, %FALSE otherwise.
2443  */
2444 gboolean FlushFileBuffers(gpointer fd_handle)
2445 {
2446         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2447         WapiHandleType type;
2448
2449         if (handle == NULL) {
2450                 SetLastError (ERROR_INVALID_HANDLE);
2451                 return(FALSE);
2452         }
2453         
2454         type = _wapi_handle_type (handle);
2455         
2456         if(io_ops[type].flushfile==NULL) {
2457                 SetLastError (ERROR_INVALID_HANDLE);
2458                 return(FALSE);
2459         }
2460         
2461         return(io_ops[type].flushfile (handle));
2462 }
2463
2464 /**
2465  * SetEndOfFile:
2466  * @handle: The file handle to set.  The handle must have
2467  * %GENERIC_WRITE access.
2468  *
2469  * Moves the end-of-file position to the current position of the file
2470  * pointer.  This function is used to truncate or extend a file.
2471  *
2472  * Return value: %TRUE on success, %FALSE otherwise.
2473  */
2474 gboolean SetEndOfFile(gpointer fd_handle)
2475 {
2476         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2477         WapiHandleType type;
2478
2479         if (handle == NULL) {
2480                 SetLastError (ERROR_INVALID_HANDLE);
2481                 return(FALSE);
2482         }
2483         
2484         type = _wapi_handle_type (handle);
2485         
2486         if(io_ops[type].setendoffile==NULL) {
2487                 SetLastError (ERROR_INVALID_HANDLE);
2488                 return(FALSE);
2489         }
2490         
2491         return(io_ops[type].setendoffile (handle));
2492 }
2493
2494 /**
2495  * SetFilePointer:
2496  * @handle: The file handle to set.  The handle must have
2497  * %GENERIC_READ or %GENERIC_WRITE access.
2498  * @movedistance: Low 32 bits of a signed value that specifies the
2499  * number of bytes to move the file pointer.
2500  * @highmovedistance: Pointer to the high 32 bits of a signed value
2501  * that specifies the number of bytes to move the file pointer, or
2502  * %NULL.
2503  * @method: The starting point for the file pointer move.
2504  *
2505  * Sets the file pointer of an open file.
2506  *
2507  * The distance to move the file pointer is calculated from
2508  * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2509  * @movedistance is the 32-bit signed value; otherwise, @movedistance
2510  * is the low 32 bits and @highmovedistance a pointer to the high 32
2511  * bits of a 64 bit signed value.  A positive distance moves the file
2512  * pointer forward from the position specified by @method; a negative
2513  * distance moves the file pointer backward.
2514  *
2515  * If the library is compiled without large file support,
2516  * @highmovedistance is ignored and its value is set to zero on a
2517  * successful return.
2518  *
2519  * Return value: On success, the low 32 bits of the new file pointer.
2520  * If @highmovedistance is not %NULL, the high 32 bits of the new file
2521  * pointer are stored there.  On failure, %INVALID_SET_FILE_POINTER.
2522  */
2523 guint32 SetFilePointer(gpointer fd_handle, gint32 movedistance,
2524                        gint32 *highmovedistance, WapiSeekMethod method)
2525 {
2526         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2527         WapiHandleType type;
2528
2529         if (handle == NULL) {
2530                 SetLastError (ERROR_INVALID_HANDLE);
2531                 return(INVALID_SET_FILE_POINTER);
2532         }
2533         
2534         type = _wapi_handle_type (handle);
2535         
2536         if(io_ops[type].seek==NULL) {
2537                 SetLastError (ERROR_INVALID_HANDLE);
2538                 return(INVALID_SET_FILE_POINTER);
2539         }
2540         
2541         return(io_ops[type].seek (handle, movedistance, highmovedistance,
2542                                   method));
2543 }
2544
2545 /**
2546  * GetFileType:
2547  * @handle: The file handle to test.
2548  *
2549  * Finds the type of file @handle.
2550  *
2551  * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2552  * unknown.  %FILE_TYPE_DISK - @handle is a disk file.
2553  * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2554  * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2555  */
2556 WapiFileType GetFileType(gpointer fd_handle)
2557 {
2558         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2559         WapiHandleType type;
2560
2561         if (handle == NULL) {
2562                 SetLastError (ERROR_INVALID_HANDLE);
2563                 return(FILE_TYPE_UNKNOWN);
2564         }
2565         
2566         type = _wapi_handle_type (handle);
2567         
2568         if(io_ops[type].getfiletype==NULL) {
2569                 SetLastError (ERROR_INVALID_HANDLE);
2570                 return(FILE_TYPE_UNKNOWN);
2571         }
2572         
2573         return(io_ops[type].getfiletype ());
2574 }
2575
2576 /**
2577  * GetFileSize:
2578  * @handle: The file handle to query.  The handle must have
2579  * %GENERIC_READ or %GENERIC_WRITE access.
2580  * @highsize: If non-%NULL, the high 32 bits of the file size are
2581  * stored here.
2582  *
2583  * Retrieves the size of the file @handle.
2584  *
2585  * If the library is compiled without large file support, @highsize
2586  * has its value set to zero on a successful return.
2587  *
2588  * Return value: On success, the low 32 bits of the file size.  If
2589  * @highsize is non-%NULL then the high 32 bits of the file size are
2590  * stored here.  On failure %INVALID_FILE_SIZE is returned.
2591  */
2592 guint32 GetFileSize(gpointer fd_handle, guint32 *highsize)
2593 {
2594         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2595         WapiHandleType type;
2596
2597         if (handle == NULL) {
2598                 SetLastError (ERROR_INVALID_HANDLE);
2599                 return(INVALID_FILE_SIZE);
2600         }
2601         
2602         type = _wapi_handle_type (handle);
2603         
2604         if(io_ops[type].getfilesize==NULL) {
2605                 SetLastError (ERROR_INVALID_HANDLE);
2606                 return(INVALID_FILE_SIZE);
2607         }
2608         
2609         return(io_ops[type].getfilesize (handle, highsize));
2610 }
2611
2612 /**
2613  * GetFileTime:
2614  * @handle: The file handle to query.  The handle must have
2615  * %GENERIC_READ access.
2616  * @create_time: Points to a %WapiFileTime structure to receive the
2617  * number of ticks since the epoch that file was created.  May be
2618  * %NULL.
2619  * @last_access: Points to a %WapiFileTime structure to receive the
2620  * number of ticks since the epoch when file was last accessed.  May be
2621  * %NULL.
2622  * @last_write: Points to a %WapiFileTime structure to receive the
2623  * number of ticks since the epoch when file was last written to.  May
2624  * be %NULL.
2625  *
2626  * Finds the number of ticks since the epoch that the file referenced
2627  * by @handle was created, last accessed and last modified.  A tick is
2628  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2629  * GMT.
2630  *
2631  * Create time isn't recorded on POSIX file systems or reported by
2632  * stat(2), so that time is guessed by returning the oldest of the
2633  * other times.
2634  *
2635  * Return value: %TRUE on success, %FALSE otherwise.
2636  */
2637 gboolean GetFileTime(gpointer fd_handle, WapiFileTime *create_time,
2638                      WapiFileTime *last_access, WapiFileTime *last_write)
2639 {
2640         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2641         WapiHandleType type;
2642
2643         if (handle == NULL) {
2644                 SetLastError (ERROR_INVALID_HANDLE);
2645                 return(FALSE);
2646         }
2647         
2648         type = _wapi_handle_type (handle);
2649         
2650         if(io_ops[type].getfiletime==NULL) {
2651                 SetLastError (ERROR_INVALID_HANDLE);
2652                 return(FALSE);
2653         }
2654         
2655         return(io_ops[type].getfiletime (handle, create_time, last_access,
2656                                          last_write));
2657 }
2658
2659 /**
2660  * SetFileTime:
2661  * @handle: The file handle to set.  The handle must have
2662  * %GENERIC_WRITE access.
2663  * @create_time: Points to a %WapiFileTime structure that contains the
2664  * number of ticks since the epoch that the file was created.  May be
2665  * %NULL.
2666  * @last_access: Points to a %WapiFileTime structure that contains the
2667  * number of ticks since the epoch when the file was last accessed.
2668  * May be %NULL.
2669  * @last_write: Points to a %WapiFileTime structure that contains the
2670  * number of ticks since the epoch when the file was last written to.
2671  * May be %NULL.
2672  *
2673  * Sets the number of ticks since the epoch that the file referenced
2674  * by @handle was created, last accessed or last modified.  A tick is
2675  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2676  * GMT.
2677  *
2678  * Create time isn't recorded on POSIX file systems, and is ignored.
2679  *
2680  * Return value: %TRUE on success, %FALSE otherwise.
2681  */
2682 gboolean SetFileTime(gpointer fd_handle, const WapiFileTime *create_time,
2683                      const WapiFileTime *last_access,
2684                      const WapiFileTime *last_write)
2685 {
2686         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2687         WapiHandleType type;
2688
2689         if (handle == NULL) {
2690                 SetLastError (ERROR_INVALID_HANDLE);
2691                 return(FALSE);
2692         }
2693         
2694         type = _wapi_handle_type (handle);
2695         
2696         if(io_ops[type].setfiletime==NULL) {
2697                 SetLastError (ERROR_INVALID_HANDLE);
2698                 return(FALSE);
2699         }
2700         
2701         return(io_ops[type].setfiletime (handle, create_time, last_access,
2702                                          last_write));
2703 }
2704
2705 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
2706  * January 1 1601 GMT
2707  */
2708
2709 #define TICKS_PER_MILLISECOND 10000L
2710 #define TICKS_PER_SECOND 10000000L
2711 #define TICKS_PER_MINUTE 600000000L
2712 #define TICKS_PER_HOUR 36000000000LL
2713 #define TICKS_PER_DAY 864000000000LL
2714
2715 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2716
2717 static const guint16 mon_yday[2][13]={
2718         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2719         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2720 };
2721
2722 /**
2723  * FileTimeToSystemTime:
2724  * @file_time: Points to a %WapiFileTime structure that contains the
2725  * number of ticks to convert.
2726  * @system_time: Points to a %WapiSystemTime structure to receive the
2727  * broken-out time.
2728  *
2729  * Converts a tick count into broken-out time values.
2730  *
2731  * Return value: %TRUE on success, %FALSE otherwise.
2732  */
2733 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2734                               WapiSystemTime *system_time)
2735 {
2736         gint64 file_ticks, totaldays, rem, y;
2737         const guint16 *ip;
2738         
2739         if(system_time==NULL) {
2740 #ifdef DEBUG
2741                 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2742 #endif
2743
2744                 SetLastError (ERROR_INVALID_PARAMETER);
2745                 return(FALSE);
2746         }
2747         
2748         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2749                 file_time->dwLowDateTime;
2750         
2751         /* Really compares if file_ticks>=0x8000000000000000
2752          * (LLONG_MAX+1) but we're working with a signed value for the
2753          * year and day calculation to work later
2754          */
2755         if(file_ticks<0) {
2756 #ifdef DEBUG
2757                 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2758 #endif
2759
2760                 SetLastError (ERROR_INVALID_PARAMETER);
2761                 return(FALSE);
2762         }
2763
2764         totaldays=(file_ticks / TICKS_PER_DAY);
2765         rem = file_ticks % TICKS_PER_DAY;
2766 #ifdef DEBUG
2767         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2768                   totaldays, rem);
2769 #endif
2770
2771         system_time->wHour=rem/TICKS_PER_HOUR;
2772         rem %= TICKS_PER_HOUR;
2773 #ifdef DEBUG
2774         g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2775                   system_time->wHour, rem);
2776 #endif
2777         
2778         system_time->wMinute = rem / TICKS_PER_MINUTE;
2779         rem %= TICKS_PER_MINUTE;
2780 #ifdef DEBUG
2781         g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2782                   system_time->wMinute, rem);
2783 #endif
2784         
2785         system_time->wSecond = rem / TICKS_PER_SECOND;
2786         rem %= TICKS_PER_SECOND;
2787 #ifdef DEBUG
2788         g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2789                   system_time->wSecond, rem);
2790 #endif
2791         
2792         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2793 #ifdef DEBUG
2794         g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2795                   system_time->wMilliseconds);
2796 #endif
2797
2798         /* January 1, 1601 was a Monday, according to Emacs calendar */
2799         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2800 #ifdef DEBUG
2801         g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2802                   system_time->wDayOfWeek);
2803 #endif
2804         
2805         /* This algorithm to find year and month given days from epoch
2806          * from glibc
2807          */
2808         y=1601;
2809         
2810 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2811 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2812
2813         while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2814                 /* Guess a corrected year, assuming 365 days per year */
2815                 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2816 #ifdef DEBUG
2817                 g_message(G_GNUC_PRETTY_FUNCTION
2818                           ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2819                           y);
2820                 g_message(G_GNUC_PRETTY_FUNCTION
2821                           ": LEAPS(yg): %lld LEAPS(y): %lld",
2822                           LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2823 #endif
2824                 
2825                 /* Adjust days and y to match the guessed year. */
2826                 totaldays -= ((yg - y) * 365
2827                               + LEAPS_THRU_END_OF (yg - 1)
2828                               - LEAPS_THRU_END_OF (y - 1));
2829 #ifdef DEBUG
2830                 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2831                           totaldays);
2832 #endif
2833                 y = yg;
2834 #ifdef DEBUG
2835                 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2836 #endif
2837         }
2838         
2839         system_time->wYear = y;
2840 #ifdef DEBUG
2841         g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2842 #endif
2843
2844         ip = mon_yday[isleap(y)];
2845         
2846         for(y=11; totaldays < ip[y]; --y) {
2847                 continue;
2848         }
2849         totaldays-=ip[y];
2850 #ifdef DEBUG
2851         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2852 #endif
2853         
2854         system_time->wMonth = y + 1;
2855 #ifdef DEBUG
2856         g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2857 #endif
2858
2859         system_time->wDay = totaldays + 1;
2860 #ifdef DEBUG
2861         g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2862 #endif
2863         
2864         return(TRUE);
2865 }
2866
2867 static gint
2868 file_compare (gconstpointer a, gconstpointer b)
2869 {
2870         gchar *astr = *(gchar **) a;
2871         gchar *bstr = *(gchar **) b;
2872
2873         return strcmp (astr, bstr);
2874 }
2875
2876 static gint
2877 get_errno_from_g_file_error (gint error)
2878 {
2879         switch (error) {
2880 #ifdef EACCESS
2881         case G_FILE_ERROR_ACCES:
2882                 error = EACCES;
2883                 break;
2884 #endif
2885 #ifdef ENAMETOOLONG
2886         case G_FILE_ERROR_NAMETOOLONG:
2887                 error = ENAMETOOLONG;
2888                 break;
2889 #endif
2890 #ifdef ENOENT
2891         case G_FILE_ERROR_NOENT:
2892                 error = ENOENT;
2893                 break;
2894 #endif
2895 #ifdef ENOTDIR
2896         case G_FILE_ERROR_NOTDIR:
2897                 error = ENOTDIR;
2898                 break;
2899 #endif
2900 #ifdef ENXIO
2901         case G_FILE_ERROR_NXIO:
2902                 error = ENXIO;
2903                 break;
2904 #endif
2905 #ifdef ENODEV
2906         case G_FILE_ERROR_NODEV:
2907                 error = ENODEV;
2908                 break;
2909 #endif
2910 #ifdef EROFS
2911         case G_FILE_ERROR_ROFS:
2912                 error = EROFS;
2913                 break;
2914 #endif
2915 #ifdef ETXTBSY
2916         case G_FILE_ERROR_TXTBSY:
2917                 error = ETXTBSY;
2918                 break;
2919 #endif
2920 #ifdef EFAULT
2921         case G_FILE_ERROR_FAULT:
2922                 error = EFAULT;
2923                 break;
2924 #endif
2925 #ifdef ELOOP
2926         case G_FILE_ERROR_LOOP:
2927                 error = ELOOP;
2928                 break;
2929 #endif
2930 #ifdef ENOSPC
2931         case G_FILE_ERROR_NOSPC:
2932                 error = ENOSPC;
2933                 break;
2934 #endif
2935 #ifdef ENOMEM
2936         case G_FILE_ERROR_NOMEM:
2937                 error = ENOMEM;
2938                 break;
2939 #endif
2940 #ifdef EMFILE
2941         case G_FILE_ERROR_MFILE:
2942                 error = EMFILE;
2943                 break;
2944 #endif
2945 #ifdef ENFILE
2946         case G_FILE_ERROR_NFILE:
2947                 error = ENFILE;
2948                 break;
2949 #endif
2950 #ifdef EBADF
2951         case G_FILE_ERROR_BADF:
2952                 error = EBADF;
2953                 break;
2954 #endif
2955 #ifdef EINVAL
2956         case G_FILE_ERROR_INVAL:
2957                 error = EINVAL;
2958                 break;
2959 #endif
2960 #ifdef EPIPE
2961         case G_FILE_ERROR_PIPE:
2962                 error = EPIPE;
2963                 break;
2964 #endif
2965 #ifdef EAGAIN
2966         case G_FILE_ERROR_AGAIN:
2967                 error = EAGAIN;
2968                 break;
2969 #endif
2970 #ifdef EINTR
2971         case G_FILE_ERROR_INTR:
2972                 error = EINTR;
2973                 break;
2974 #endif
2975 #ifdef EWIO
2976         case G_FILE_ERROR_IO:
2977                 error = EIO;
2978                 break;
2979 #endif
2980 #ifdef EPERM
2981         case G_FILE_ERROR_PERM:
2982                 error = EPERM;
2983                 break;
2984 #endif
2985         case G_FILE_ERROR_FAILED:
2986                 error = ERROR_INVALID_PARAMETER;
2987                 break;
2988         }
2989
2990         return error;
2991 }
2992
2993 /* scandir using glib */
2994 static gint
2995 mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
2996 {
2997         GError *error = NULL;
2998         GDir *dir;
2999         GPtrArray *names;
3000         const gchar *name;
3001         gint result;
3002         GPatternSpec *patspec;
3003
3004         dir = g_dir_open (dirname, 0, &error);
3005         if (dir == NULL) {
3006                 /* g_dir_open returns ENOENT on directories on which we don't
3007                  * have read/x permission */
3008                 gint errnum = get_errno_from_g_file_error (error->code);
3009                 g_error_free (error);
3010                 if (errnum == ENOENT && g_file_test (dirname, G_FILE_TEST_IS_DIR))
3011                         errnum = EACCES;
3012
3013                 errno = errnum;
3014                 return -1;
3015         }
3016
3017         patspec = g_pattern_spec_new (pattern);
3018         names = g_ptr_array_new ();
3019         while ((name = g_dir_read_name (dir)) != NULL) {
3020                 if (g_pattern_match_string (patspec, name))
3021                         g_ptr_array_add (names, g_strdup (name));
3022         }
3023         
3024         g_pattern_spec_free (patspec);
3025         g_dir_close (dir);
3026         result = names->len;
3027         if (result > 0) {
3028                 g_ptr_array_sort (names, file_compare);
3029                 g_ptr_array_set_size (names, result + 1);
3030
3031                 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
3032         } else {
3033                 g_ptr_array_free (names, TRUE);
3034         }
3035
3036         return result;
3037 }
3038
3039 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
3040 {
3041         struct _WapiHandlePrivate_find *find_handle;
3042         gpointer handle, find_ret = INVALID_HANDLE_VALUE;
3043         gboolean ok;
3044         gchar *utf8_pattern = NULL, *dir_part, *entry_part;
3045         int result;
3046         int thr_ret;
3047         gboolean unref = FALSE;
3048         
3049         if (pattern == NULL) {
3050 #ifdef DEBUG
3051                 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
3052 #endif
3053
3054                 SetLastError (ERROR_PATH_NOT_FOUND);
3055                 return(INVALID_HANDLE_VALUE);
3056         }
3057
3058         utf8_pattern = mono_unicode_to_external (pattern);
3059         if (utf8_pattern == NULL) {
3060 #ifdef DEBUG
3061                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3062 #endif
3063                 
3064                 SetLastError (ERROR_INVALID_NAME);
3065                 return(INVALID_HANDLE_VALUE);
3066         }
3067
3068 #ifdef DEBUG
3069         g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
3070                 utf8_pattern);
3071 #endif
3072         
3073         /* Figure out which bit of the pattern is the directory */
3074         dir_part=g_path_get_dirname (utf8_pattern);
3075         entry_part=g_path_get_basename (utf8_pattern);
3076
3077 #if 0
3078         /* Don't do this check for now, it breaks if directories
3079          * really do have metachars in their names (see bug 58116).
3080          * FIXME: Figure out a better solution to keep some checks...
3081          */
3082         if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3083                 SetLastError (ERROR_INVALID_NAME);
3084                 g_free (dir_part);
3085                 g_free (entry_part);
3086                 g_free (utf8_pattern);
3087                 return(INVALID_HANDLE_VALUE);
3088         }
3089 #endif
3090         
3091         handle=_wapi_handle_new (WAPI_HANDLE_FIND);
3092         if(handle==_WAPI_HANDLE_INVALID) {
3093                 g_warning (G_GNUC_PRETTY_FUNCTION ": error creating find handle");
3094                 g_free (dir_part);
3095                 g_free (entry_part);
3096                 g_free (utf8_pattern);
3097                 SetLastError (ERROR_GEN_FAILURE);
3098                 
3099                 return(INVALID_HANDLE_VALUE);
3100         }
3101
3102         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3103                               handle);
3104         thr_ret = _wapi_handle_lock_handle (handle);
3105         g_assert (thr_ret == 0);
3106         
3107         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3108                                 (gpointer *)&find_handle);
3109         if(ok==FALSE) {
3110                 g_warning (G_GNUC_PRETTY_FUNCTION
3111                            ": error looking up find handle %p", handle);
3112                 g_free (dir_part);
3113                 dir_part = NULL;
3114                 g_free (entry_part);
3115                 entry_part = NULL;
3116                 g_free (utf8_pattern);
3117                 utf8_pattern = NULL;
3118                 SetLastError (ERROR_INVALID_HANDLE);
3119                 goto cleanup;
3120         }
3121
3122         /* The pattern can specify a directory or a set of files.
3123          *
3124          * The pattern can have wildcard characters ? and *, but only
3125          * in the section after the last directory delimiter.  (Return
3126          * ERROR_INVALID_NAME if there are wildcards in earlier path
3127          * sections.)  "*" has the usual 0-or-more chars meaning.  "?" 
3128          * means "match one character", "??" seems to mean "match one
3129          * or two characters", "???" seems to mean "match one, two or
3130          * three characters", etc.  Windows will also try and match
3131          * the mangled "short name" of files, so 8 character patterns
3132          * with wildcards will show some surprising results.
3133          *
3134          * All the written documentation I can find says that '?' 
3135          * should only match one character, and doesn't mention '??',
3136          * '???' etc.  I'm going to assume that the strict behaviour
3137          * (ie '???' means three and only three characters) is the
3138          * correct one, because that lets me use fnmatch(3) rather
3139          * than mess around with regexes.
3140          */
3141
3142         find_handle->namelist = NULL;
3143         result = mono_io_scandir (dir_part, entry_part, &find_handle->namelist);
3144         
3145         if (result < 0) {
3146 #ifdef DEBUG
3147                 gint errnum = errno;
3148 #endif
3149                 _wapi_set_last_error_from_errno ();
3150 #ifdef DEBUG
3151                 g_message (G_GNUC_PRETTY_FUNCTION ": scandir error: %s", g_strerror (errnum));
3152 #endif
3153                 g_free (utf8_pattern);
3154                 g_free (entry_part);
3155                 g_free (dir_part);
3156                 unref = TRUE;
3157                 goto cleanup;
3158         }
3159
3160         g_free (utf8_pattern);
3161         g_free (entry_part);
3162         
3163 #ifdef DEBUG
3164         g_message (G_GNUC_PRETTY_FUNCTION ": Got %d matches", result);
3165 #endif
3166
3167         find_handle->dir_part = dir_part;
3168         find_handle->num = result;
3169         find_handle->count = 0;
3170
3171         find_ret = handle;
3172
3173 cleanup:
3174         thr_ret = _wapi_handle_unlock_handle (handle);
3175         g_assert (thr_ret == 0);
3176         pthread_cleanup_pop (0);
3177
3178         /* FindNextFile has to be called after unlocking the handle,
3179          * because it wants to lock the handle itself
3180          */
3181         if (find_ret != INVALID_HANDLE_VALUE &&
3182             !FindNextFile (handle, find_data)) {
3183                 FindClose (handle);
3184                 SetLastError (ERROR_NO_MORE_FILES);
3185                 find_ret = INVALID_HANDLE_VALUE;
3186         }
3187
3188         /* Must not call _wapi_handle_unref() with the handle already
3189          * locked
3190          */
3191         if (unref) {
3192                 _wapi_handle_unref (handle);
3193         }
3194         
3195         return (find_ret);
3196 }
3197
3198 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
3199 {
3200         struct _WapiHandlePrivate_find *find_handle;
3201         gboolean ok;
3202         struct stat buf;
3203         gchar *filename;
3204         gchar *utf8_filename, *utf8_basename;
3205         gunichar2 *utf16_basename;
3206         time_t create_time;
3207         glong bytes;
3208         int thr_ret;
3209         gboolean ret = FALSE;
3210         
3211         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3212                                 (gpointer *)&find_handle);
3213         if(ok==FALSE) {
3214                 g_warning (G_GNUC_PRETTY_FUNCTION
3215                            ": error looking up find handle %p", handle);
3216                 SetLastError (ERROR_INVALID_HANDLE);
3217                 return(FALSE);
3218         }
3219
3220         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3221                               handle);
3222         thr_ret = _wapi_handle_lock_handle (handle);
3223         g_assert (thr_ret == 0);
3224         
3225 retry:
3226         if (find_handle->count >= find_handle->num) {
3227                 SetLastError (ERROR_NO_MORE_FILES);
3228                 goto cleanup;
3229         }
3230
3231         /* stat next match */
3232
3233         filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3234         if (lstat (filename, &buf) != 0) {
3235 #ifdef DEBUG
3236                 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
3237 #endif
3238
3239                 g_free (filename);
3240                 goto retry;
3241         }
3242
3243         /* Check for dangling symlinks, and ignore them (principle of
3244          * least surprise, avoiding confusion where we report the file
3245          * exists, but when someone tries to open it we would report
3246          * it isn't there.)
3247          */
3248         if(S_ISLNK (buf.st_mode)) {
3249                 if(stat (filename, &buf) != 0) {
3250                         g_free (filename);
3251                         goto retry;
3252                 }
3253         }
3254         
3255         utf8_filename=mono_utf8_from_external (filename);
3256         if(utf8_filename==NULL) {
3257                 /* We couldn't turn this filename into utf8 (eg the
3258                  * encoding of the name wasn't convertible), so just
3259                  * ignore it.
3260                  */
3261                 g_free (filename);
3262                 goto retry;
3263         }
3264         g_free (filename);
3265         
3266 #ifdef DEBUG
3267         g_message (G_GNUC_PRETTY_FUNCTION ": Found [%s]", utf8_filename);
3268 #endif
3269         
3270         /* fill data block */
3271
3272         if (buf.st_mtime < buf.st_ctime)
3273                 create_time = buf.st_mtime;
3274         else
3275                 create_time = buf.st_ctime;
3276         
3277         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3278
3279         _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
3280         _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3281         _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3282
3283         if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3284                 find_data->nFileSizeHigh = 0;
3285                 find_data->nFileSizeLow = 0;
3286         }
3287         else {
3288                 find_data->nFileSizeHigh = buf.st_size >> 32;
3289                 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3290         }
3291
3292         find_data->dwReserved0 = 0;
3293         find_data->dwReserved1 = 0;
3294
3295         utf8_basename = g_path_get_basename (utf8_filename);
3296         utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3297                                           NULL);
3298         if(utf16_basename==NULL) {
3299                 g_free (utf8_basename);
3300                 g_free (utf8_filename);
3301                 goto retry;
3302         }
3303         ret = TRUE;
3304         
3305         /* utf16 is 2 * utf8 */
3306         bytes *= 2;
3307
3308         memset (find_data->cFileName, '\0', (MAX_PATH*2));
3309
3310         /* Truncating a utf16 string like this might leave the last
3311          * char incomplete
3312          */
3313         memcpy (find_data->cFileName, utf16_basename,
3314                 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3315
3316         find_data->cAlternateFileName [0] = 0;  /* not used */
3317
3318         g_free (utf8_basename);
3319         g_free (utf8_filename);
3320         g_free (utf16_basename);
3321
3322 cleanup:
3323         thr_ret = _wapi_handle_unlock_handle (handle);
3324         g_assert (thr_ret == 0);
3325         pthread_cleanup_pop (0);
3326         
3327         return(ret);
3328 }
3329
3330 /**
3331  * FindClose:
3332  * @wapi_handle: the find handle to close.
3333  *
3334  * Closes find handle @wapi_handle
3335  *
3336  * Return value: %TRUE on success, %FALSE otherwise.
3337  */
3338 gboolean FindClose (gpointer handle)
3339 {
3340         struct _WapiHandlePrivate_find *find_handle;
3341         gboolean ok;
3342         int thr_ret;
3343
3344         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3345                                 (gpointer *)&find_handle);
3346         if(ok==FALSE) {
3347                 g_warning (G_GNUC_PRETTY_FUNCTION
3348                            ": error looking up find handle %p", handle);
3349                 SetLastError (ERROR_INVALID_HANDLE);
3350                 return(FALSE);
3351         }
3352
3353         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3354                               handle);
3355         thr_ret = _wapi_handle_lock_handle (handle);
3356         g_assert (thr_ret == 0);
3357         
3358         g_strfreev (find_handle->namelist);
3359         g_free (find_handle->dir_part);
3360
3361         thr_ret = _wapi_handle_unlock_handle (handle);
3362         g_assert (thr_ret == 0);
3363         pthread_cleanup_pop (0);
3364         
3365         _wapi_handle_unref (handle);
3366         
3367         return(TRUE);
3368 }
3369
3370 /**
3371  * CreateDirectory:
3372  * @name: a pointer to a NULL-terminated unicode string, that names
3373  * the directory to be created.
3374  * @security: ignored for now
3375  *
3376  * Creates directory @name
3377  *
3378  * Return value: %TRUE on success, %FALSE otherwise.
3379  */
3380 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
3381 {
3382         gchar *utf8_name;
3383         int result;
3384         struct stat buf;
3385         guint32 attrs;
3386         
3387         if (name == NULL) {
3388 #ifdef DEBUG
3389                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3390 #endif
3391
3392                 SetLastError (ERROR_INVALID_NAME);
3393                 return(FALSE);
3394         }
3395         
3396         utf8_name = mono_unicode_to_external (name);
3397         if (utf8_name == NULL) {
3398 #ifdef DEBUG
3399                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3400 #endif
3401         
3402                 SetLastError (ERROR_INVALID_NAME);
3403                 return FALSE;
3404         }
3405
3406         result = mkdir (utf8_name, 0777);
3407
3408         if (result == 0) {
3409                 g_free (utf8_name);
3410                 return TRUE;
3411         }
3412
3413         if (errno == EEXIST) {
3414                 result = stat (utf8_name, &buf);
3415                 if (result == -1) {
3416                         _wapi_set_last_error_from_errno ();
3417                         g_free (utf8_name);
3418                         return FALSE;
3419                 }
3420
3421                 g_free (utf8_name);
3422                 attrs = _wapi_stat_to_file_attributes (&buf);
3423                 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
3424                         return TRUE;
3425
3426                 errno = EEXIST;
3427                 _wapi_set_last_error_from_errno ();
3428                 return FALSE;
3429         }
3430
3431         _wapi_set_last_error_from_errno ();
3432         g_free (utf8_name);
3433         return FALSE;
3434 }
3435
3436 /**
3437  * RemoveDirectory:
3438  * @name: a pointer to a NULL-terminated unicode string, that names
3439  * the directory to be removed.
3440  *
3441  * Removes directory @name
3442  *
3443  * Return value: %TRUE on success, %FALSE otherwise.
3444  */
3445 gboolean RemoveDirectory (const gunichar2 *name)
3446 {
3447         gchar *utf8_name;
3448         int result;
3449         
3450         if (name == NULL) {
3451 #ifdef DEBUG
3452                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3453 #endif
3454
3455                 SetLastError (ERROR_INVALID_NAME);
3456                 return(FALSE);
3457         }
3458
3459         utf8_name = mono_unicode_to_external (name);
3460         if (utf8_name == NULL) {
3461 #ifdef DEBUG
3462                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3463 #endif
3464                 
3465                 SetLastError (ERROR_INVALID_NAME);
3466                 return FALSE;
3467         }
3468
3469         result = rmdir (utf8_name);
3470         g_free (utf8_name);
3471
3472         if (result == 0)
3473                 return TRUE;
3474         
3475         _wapi_set_last_error_from_errno ();
3476         return FALSE;
3477 }
3478
3479 /**
3480  * GetFileAttributes:
3481  * @name: a pointer to a NULL-terminated unicode filename.
3482  *
3483  * Gets the attributes for @name;
3484  *
3485  * Return value: %INVALID_FILE_ATTRIBUTES on failure
3486  */
3487 guint32 GetFileAttributes (const gunichar2 *name)
3488 {
3489         gchar *utf8_name;
3490         struct stat buf;
3491         int result;
3492         
3493         if (name == NULL) {
3494 #ifdef DEBUG
3495                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3496 #endif
3497
3498                 SetLastError (ERROR_INVALID_NAME);
3499                 return(FALSE);
3500         }
3501         
3502         utf8_name = mono_unicode_to_external (name);
3503         if (utf8_name == NULL) {
3504 #ifdef DEBUG
3505                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3506 #endif
3507
3508                 SetLastError (ERROR_INVALID_PARAMETER);
3509                 return (INVALID_FILE_ATTRIBUTES);
3510         }
3511
3512         result = stat (utf8_name, &buf);
3513
3514         if (result != 0) {
3515                 _wapi_set_last_error_from_errno ();
3516                 g_free (utf8_name);
3517                 return (INVALID_FILE_ATTRIBUTES);
3518         }
3519         
3520         g_free (utf8_name);
3521         return _wapi_stat_to_file_attributes (&buf);
3522 }
3523
3524 /**
3525  * GetFileAttributesEx:
3526  * @name: a pointer to a NULL-terminated unicode filename.
3527  * @level: must be GetFileExInfoStandard
3528  * @info: pointer to a WapiFileAttributesData structure
3529  *
3530  * Gets attributes, size and filetimes for @name;
3531  *
3532  * Return value: %TRUE on success, %FALSE on failure
3533  */
3534 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3535 {
3536         gchar *utf8_name;
3537         WapiFileAttributesData *data;
3538
3539         struct stat buf;
3540         time_t create_time;
3541         int result;
3542         
3543         if (level != GetFileExInfoStandard) {
3544 #ifdef DEBUG
3545                 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
3546 #endif
3547
3548                 SetLastError (ERROR_INVALID_PARAMETER);
3549                 return FALSE;
3550         }
3551         
3552         if (name == NULL) {
3553 #ifdef DEBUG
3554                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3555 #endif
3556
3557                 SetLastError (ERROR_INVALID_NAME);
3558                 return(FALSE);
3559         }
3560
3561         utf8_name = mono_unicode_to_external (name);
3562         if (utf8_name == NULL) {
3563 #ifdef DEBUG
3564                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3565 #endif
3566
3567                 SetLastError (ERROR_INVALID_PARAMETER);
3568                 return FALSE;
3569         }
3570
3571         result = stat (utf8_name, &buf);
3572         g_free (utf8_name);
3573
3574         if (result != 0) {
3575                 SetLastError (ERROR_FILE_NOT_FOUND);
3576                 return FALSE;
3577         }
3578
3579         /* fill data block */
3580
3581         data = (WapiFileAttributesData *)info;
3582
3583         if (buf.st_mtime < buf.st_ctime)
3584                 create_time = buf.st_mtime;
3585         else
3586                 create_time = buf.st_ctime;
3587         
3588         data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3589
3590         _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3591         _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3592         _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3593
3594         if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3595                 data->nFileSizeHigh = 0;
3596                 data->nFileSizeLow = 0;
3597         }
3598         else {
3599                 data->nFileSizeHigh = buf.st_size >> 32;
3600                 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3601         }
3602
3603         return TRUE;
3604 }
3605
3606 /**
3607  * SetFileAttributes
3608  * @name: name of file
3609  * @attrs: attributes to set
3610  *
3611  * Changes the attributes on a named file.
3612  *
3613  * Return value: %TRUE on success, %FALSE on failure.
3614  */
3615 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3616 {
3617         /* FIXME: think of something clever to do on unix */
3618         gchar *utf8_name;
3619         struct stat buf;
3620         int result;
3621
3622         /*
3623          * Currently we only handle one *internal* case, with a value that is
3624          * not standard: 0x80000000, which means `set executable bit'
3625          */
3626         
3627         if (name == NULL) {
3628 #ifdef DEBUG
3629                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3630 #endif
3631
3632                 SetLastError (ERROR_INVALID_NAME);
3633                 return(FALSE);
3634         }
3635
3636         utf8_name = mono_unicode_to_external (name);
3637         if (utf8_name == NULL) {
3638 #ifdef DEBUG
3639                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3640 #endif
3641
3642                 SetLastError (ERROR_INVALID_NAME);
3643                 return FALSE;
3644         }
3645
3646         result = stat (utf8_name, &buf);
3647         if (result != 0) {
3648                 g_free (utf8_name);
3649                 SetLastError (ERROR_FILE_NOT_FOUND);
3650                 return FALSE;
3651         }
3652
3653         /* Contrary to the documentation, ms allows NORMAL to be
3654          * specified along with other attributes, so dont bother to
3655          * catch that case here.
3656          */
3657         if (attrs & FILE_ATTRIBUTE_READONLY) {
3658                 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3659         } else {
3660                 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
3661         }
3662
3663         /* Ignore the other attributes for now */
3664
3665         if (attrs & 0x80000000){
3666                 mode_t exec_mask = 0;
3667
3668                 if ((buf.st_mode & S_IRUSR) != 0)
3669                         exec_mask |= S_IXUSR;
3670
3671                 if ((buf.st_mode & S_IRGRP) != 0)
3672                         exec_mask |= S_IXGRP;
3673
3674                 if ((buf.st_mode & S_IROTH) != 0)
3675                         exec_mask |= S_IXOTH;
3676
3677                 result = chmod (utf8_name, buf.st_mode | exec_mask);
3678         }
3679         /* Don't bother to reset executable (might need to change this
3680          * policy)
3681          */
3682         
3683         g_free (utf8_name);
3684
3685         return(TRUE);
3686 }
3687
3688 /**
3689  * GetCurrentDirectory
3690  * @length: size of the buffer
3691  * @buffer: pointer to buffer that recieves path
3692  *
3693  * Retrieves the current directory for the current process.
3694  *
3695  * Return value: number of characters in buffer on success, zero on failure
3696  */
3697 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3698 {
3699         gchar *path;
3700         gunichar2 *utf16_path;
3701         glong count;
3702         gsize bytes;
3703         
3704         path = g_get_current_dir ();
3705         if (path == NULL)
3706                 return 0;
3707
3708         utf16_path=mono_unicode_from_external (path, &bytes);
3709         
3710         /* if buffer too small, return number of characters required.
3711          * this is plain dumb.
3712          */
3713         
3714         count = (bytes/2)+1;
3715         if (count > length) {
3716                 g_free(path);
3717                 g_free (utf16_path);
3718                 
3719                 return (count);
3720         }
3721
3722         /* Add the terminator */
3723         memset (buffer, '\0', bytes+2);
3724         memcpy (buffer, utf16_path, bytes);
3725         
3726         g_free (utf16_path);
3727         g_free (path);
3728
3729         return count;
3730 }
3731
3732 /**
3733  * SetCurrentDirectory
3734  * @path: path to new directory
3735  *
3736  * Changes the directory path for the current process.
3737  *
3738  * Return value: %TRUE on success, %FALSE on failure.
3739  */
3740 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3741 {
3742         gchar *utf8_path;
3743         gboolean result;
3744
3745         utf8_path = mono_unicode_to_external (path);
3746         if (chdir (utf8_path) != 0) {
3747                 _wapi_set_last_error_from_errno ();
3748                 result = FALSE;
3749         }
3750         else
3751                 result = TRUE;
3752
3753         g_free (utf8_path);
3754         return result;
3755 }
3756
3757 /* When we're confident there are no more bugs in the fd->handle
3758  * mapping, this can be replaced as a no-op: GPOINTER_TO_INT(fd_handle) == fd
3759  */
3760 int _wapi_file_handle_to_fd (gpointer fd_handle)
3761 {
3762         struct _WapiHandlePrivate_file *file_private_handle;
3763         gboolean ok;
3764         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3765         
3766 #ifdef DEBUG
3767         g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
3768 #endif
3769
3770         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
3771                                 (gpointer *)&file_private_handle);
3772         if(ok==FALSE) {
3773                 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
3774                                         (gpointer *)&file_private_handle);
3775                 if(ok==FALSE) {
3776                         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
3777                                                 (gpointer *)&file_private_handle);
3778                         if(ok==FALSE) {
3779 #ifdef DEBUG
3780                                 g_message (G_GNUC_PRETTY_FUNCTION
3781                                            ": returning -1");
3782 #endif
3783                                 SetLastError (ERROR_INVALID_HANDLE);
3784                                 return(-1);
3785                         }
3786                 }
3787         }
3788         
3789         if (file_private_handle->fd_mapped.assigned == FALSE) {
3790                 SetLastError (ERROR_INVALID_HANDLE);
3791                 return(-1);
3792         }
3793         
3794 #ifdef DEBUG
3795         g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
3796                    file_private_handle->fd_mapped.fd);
3797 #endif
3798         
3799         g_assert (file_private_handle->fd_mapped.fd == GPOINTER_TO_INT (fd_handle));
3800         
3801         return(file_private_handle->fd_mapped.fd);
3802 }
3803
3804 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3805                      WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3806 {
3807         struct _WapiHandle_file *pipe_read_handle;
3808         struct _WapiHandle_file *pipe_write_handle;
3809         struct _WapiHandlePrivate_file *pipe_read_private_handle;
3810         struct _WapiHandlePrivate_file *pipe_write_private_handle;
3811         gpointer read_handle;
3812         gpointer write_handle;
3813         gboolean ok;
3814         int filedes[2];
3815         int ret;
3816         int thr_ret;
3817         gboolean unref_read = FALSE, unref_write = FALSE;
3818         gboolean cp_ret = FALSE;
3819         
3820         mono_once (&io_ops_once, io_ops_init);
3821         
3822 #ifdef DEBUG
3823         g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
3824 #endif
3825
3826         ret=pipe (filedes);
3827         if(ret==-1) {
3828 #ifdef DEBUG
3829                 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
3830                            strerror (errno));
3831 #endif
3832                 
3833                 _wapi_set_last_error_from_errno ();
3834                 return(FALSE);
3835         }
3836
3837         if (filedes[0] >= _wapi_fd_offset_table_size ||
3838             filedes[1] >= _wapi_fd_offset_table_size) {
3839 #ifdef DEBUG
3840                 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
3841 #endif
3842
3843                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3844                 
3845                 close (filedes[0]);
3846                 close (filedes[1]);
3847                 
3848                 return(FALSE);
3849         }
3850         
3851         /* filedes[0] is open for reading, filedes[1] for writing */
3852
3853         read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3854         if(read_handle==_WAPI_HANDLE_INVALID) {
3855                 g_warning (G_GNUC_PRETTY_FUNCTION
3856                            ": error creating pipe read handle");
3857                 close (filedes[0]);
3858                 close (filedes[1]);
3859                 SetLastError (ERROR_GEN_FAILURE);
3860                 
3861                 return(FALSE);
3862         }
3863         
3864         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3865                               read_handle);
3866         thr_ret = _wapi_handle_lock_handle (read_handle);
3867         g_assert (thr_ret == 0);
3868
3869         ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
3870                                 (gpointer *)&pipe_read_handle,
3871                                 (gpointer *)&pipe_read_private_handle);
3872         if(ok==FALSE) {
3873                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3874                 close (filedes[0]);
3875                 close (filedes[1]);
3876                 SetLastError (ERROR_INVALID_HANDLE);
3877                 goto cleanup;
3878         }
3879         
3880         write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3881         if(write_handle==_WAPI_HANDLE_INVALID) {
3882                 g_warning (G_GNUC_PRETTY_FUNCTION
3883                            ": error creating pipe write handle");
3884                 unref_read = TRUE;
3885                 
3886                 close (filedes[0]);
3887                 close (filedes[1]);
3888                 SetLastError (ERROR_GEN_FAILURE);
3889                 
3890                 goto cleanup;
3891         }
3892         
3893         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3894                               write_handle);
3895         thr_ret = _wapi_handle_lock_handle (write_handle);
3896         g_assert (thr_ret == 0);
3897
3898         ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
3899                                 (gpointer *)&pipe_write_handle,
3900                                 (gpointer *)&pipe_write_private_handle);
3901         if(ok==FALSE) {
3902                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3903                 unref_read = TRUE;
3904                 unref_write = TRUE;
3905                 
3906                 close (filedes[0]);
3907                 close (filedes[1]);
3908                 SetLastError (ERROR_INVALID_HANDLE);
3909                 goto write_cleanup;
3910         }
3911         cp_ret = TRUE;
3912         
3913         pipe_read_private_handle->fd_mapped.fd=filedes[0];
3914         pipe_read_private_handle->fd_mapped.assigned=TRUE;
3915         pipe_read_handle->fileaccess=GENERIC_READ;
3916         
3917         _wapi_handle_fd_offset_store (filedes[0], read_handle);
3918         *readpipe=GINT_TO_POINTER (filedes[0]);
3919
3920         pipe_write_private_handle->fd_mapped.fd=filedes[1];
3921         pipe_write_private_handle->fd_mapped.assigned=TRUE;
3922         pipe_write_handle->fileaccess=GENERIC_WRITE;
3923         
3924         _wapi_handle_fd_offset_store (filedes[1], write_handle);
3925         *writepipe=GINT_TO_POINTER (filedes[1]);
3926
3927 #ifdef DEBUG
3928         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]);
3929 #endif
3930
3931 write_cleanup:
3932         thr_ret =_wapi_handle_unlock_handle (write_handle);
3933         g_assert (thr_ret == 0);
3934         pthread_cleanup_pop (0);
3935
3936         if (unref_write) {
3937                 _wapi_handle_unref (write_handle);
3938         }
3939
3940 cleanup:
3941         thr_ret =_wapi_handle_unlock_handle (read_handle);
3942         g_assert (thr_ret == 0);
3943         pthread_cleanup_pop (0);
3944
3945         /* Must not call _wapi_handle_unref() with the handle already
3946          * locked
3947          */
3948         if (unref_read) {
3949                 _wapi_handle_unref (read_handle);
3950         }
3951         
3952         return(cp_ret);
3953 }
3954
3955 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3956 {
3957         gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3958         gunichar2 *tmpdir16=NULL;
3959         glong dirlen;
3960         gsize bytes;
3961         guint32 ret;
3962         
3963         if(tmpdir[strlen (tmpdir)]!='/') {
3964                 g_free (tmpdir);
3965                 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3966         }
3967         
3968         tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3969         if(tmpdir16==NULL) {
3970                 g_free (tmpdir);
3971                 return(0);
3972         } else {
3973                 dirlen=(bytes/2);
3974                 
3975                 if(dirlen+1>len) {
3976 #ifdef DEBUG
3977                         g_message (G_GNUC_PRETTY_FUNCTION
3978                                    ": Size %d smaller than needed (%ld)", len,
3979                                    dirlen+1);
3980 #endif
3981                 
3982                         ret=dirlen+1;
3983                 } else {
3984                         /* Add the terminator */
3985                         memset (buf, '\0', bytes+2);
3986                         memcpy (buf, tmpdir16, bytes);
3987                 
3988                         ret=dirlen;
3989                 }
3990         }
3991
3992         if(tmpdir16!=NULL) {
3993                 g_free (tmpdir16);
3994         }
3995         g_free (tmpdir);
3996         
3997         return(ret);
3998 }
3999
4000 gboolean
4001 _wapi_io_add_callback (gpointer fd_handle,
4002                        WapiOverlappedCB callback,
4003                        guint64 flags G_GNUC_UNUSED)
4004 {
4005         struct _WapiHandle_file *file_handle;
4006         struct _WapiHandlePrivate_file *file_private_handle;
4007         gboolean ok;
4008         int thr_ret;
4009         gboolean ret = FALSE;
4010         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4011         
4012         if (handle == NULL) {
4013                 SetLastError (ERROR_INVALID_HANDLE);
4014                 return(FALSE);
4015         }
4016         
4017         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4018                                   (gpointer *) &file_handle,
4019                                   (gpointer *) &file_private_handle);
4020
4021         if (ok == FALSE) {
4022                 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
4023                                           (gpointer *) &file_handle,
4024                                           (gpointer *) &file_private_handle);
4025
4026         }
4027
4028         if (ok == FALSE || file_private_handle->async == FALSE) {
4029                 SetLastError (ERROR_INVALID_HANDLE);
4030                 return FALSE;
4031         }
4032
4033         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
4034                               handle);
4035         thr_ret = _wapi_handle_lock_handle (handle);
4036         g_assert (thr_ret == 0);
4037         
4038         if (file_private_handle->callback != NULL) {
4039                 SetLastError (ERROR_INVALID_PARAMETER);
4040                 goto cleanup;
4041         }
4042         ret = TRUE;
4043         
4044         file_private_handle->callback = callback;
4045
4046 cleanup:
4047         thr_ret = _wapi_handle_unlock_handle (handle);
4048         g_assert (thr_ret == 0);
4049         pthread_cleanup_pop (0);
4050
4051         return(ret);
4052 }
4053
4054 gint32
4055 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
4056 {
4057         FILE *fp;
4058         gunichar2 *ptr, *dir;
4059         glong length, total = 0;
4060         gchar buffer [512];
4061         gchar **splitted;
4062
4063         memset (buf, 0, sizeof (gunichar2) * (len + 1)); 
4064         buf [0] = '/';
4065         buf [1] = 0;
4066         buf [2] = 0;
4067
4068         /* Sigh, mntent and friends don't work well.
4069          * It stops on the first line that doesn't begin with a '/'.
4070          * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4071         fp = fopen ("/etc/mtab", "rt");
4072         if (fp == NULL) {
4073                 fp = fopen ("/etc/mnttab", "rt");
4074                 if (fp == NULL)
4075                         return 1;
4076         }
4077
4078         ptr = buf;
4079         while (fgets (buffer, 512, fp) != NULL) {
4080                 if (*buffer != '/')
4081                         continue;
4082
4083                 splitted = g_strsplit (buffer, " ", 0);
4084                 if (!*splitted || !*(splitted + 1))
4085                         continue;
4086
4087                 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
4088                 g_strfreev (splitted);
4089                 if (total + length + 1 > len) {
4090                         return len * 2; /* guess */
4091                 }
4092
4093                 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4094                 g_free (dir);
4095                 total += length + 1;
4096         }
4097
4098         fclose (fp);
4099         return total;
4100 /* Commented out, does not work with my mtab!!! - Gonz */
4101 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4102 {
4103         FILE *fp;
4104         struct mntent *mnt;
4105         gunichar2 *ptr, *dir;
4106         glong len, total = 0;
4107         
4108
4109         fp = setmntent ("/etc/mtab", "rt");
4110         if (fp == NULL) {
4111                 fp = setmntent ("/etc/mnttab", "rt");
4112                 if (fp == NULL)
4113                         return;
4114         }
4115
4116         ptr = buf;
4117         while ((mnt = getmntent (fp)) != NULL) {
4118                 g_print ("GOT %s\n", mnt->mnt_dir);
4119                 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4120                 if (total + len + 1 > len) {
4121                         return len * 2; /* guess */
4122                 }
4123
4124                 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4125                 g_free (dir);
4126                 total += len + 1;
4127         }
4128
4129         endmntent (fp);
4130         return total;
4131 }
4132 #endif
4133 }
4134
4135 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
4136 {
4137         struct flock lock_data;
4138         int ret;
4139
4140         lock_data.l_type = F_WRLCK;
4141         lock_data.l_whence = SEEK_SET;
4142         lock_data.l_start = offset;
4143         lock_data.l_len = length;
4144         
4145         do {
4146                 ret = fcntl (fd, F_SETLK, &lock_data);
4147         }
4148         while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
4149         
4150 #ifdef DEBUG
4151         g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4152 #endif
4153
4154         if (ret == -1) {
4155                 SetLastError (ERROR_LOCK_VIOLATION);
4156                 return(FALSE);
4157         }
4158
4159         return(TRUE);
4160 }
4161
4162 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
4163 {
4164         struct flock lock_data;
4165         int ret;
4166
4167         lock_data.l_type = F_UNLCK;
4168         lock_data.l_whence = SEEK_SET;
4169         lock_data.l_start = offset;
4170         lock_data.l_len = length;
4171         
4172         do {
4173                 ret = fcntl (fd, F_SETLK, &lock_data);
4174         }
4175         while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
4176         
4177 #ifdef DEBUG
4178         g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4179 #endif
4180         
4181         if (ret == -1) {
4182                 SetLastError (ERROR_LOCK_VIOLATION);
4183                 return(FALSE);
4184         }
4185
4186         return(TRUE);
4187 }
4188
4189 gboolean LockFile (gpointer fd_handle, guint32 offset_low, guint32 offset_high,
4190                    guint32 length_low, guint32 length_high)
4191 {
4192         struct _WapiHandle_file *file_handle;
4193         struct _WapiHandlePrivate_file *file_private_handle;
4194         gboolean ok;
4195         off_t offset, length;
4196         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4197
4198         if (handle == NULL) {
4199                 SetLastError (ERROR_INVALID_HANDLE);
4200                 return(FALSE);
4201         }
4202         
4203         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4204                                   (gpointer *)&file_handle,
4205                                   (gpointer *)&file_private_handle);
4206         if (ok == FALSE) {
4207                 g_warning (G_GNUC_PRETTY_FUNCTION
4208                            ": error looking up file handle %p", handle);
4209                 SetLastError (ERROR_INVALID_HANDLE);
4210                 return(FALSE);
4211         }
4212
4213         if (file_private_handle->fd_mapped.assigned == FALSE) {
4214                 SetLastError (ERROR_INVALID_HANDLE);
4215                 return(FALSE);
4216         }
4217
4218         if (!(file_handle->fileaccess & GENERIC_READ) &&
4219             !(file_handle->fileaccess & GENERIC_WRITE) &&
4220             !(file_handle->fileaccess & GENERIC_ALL)) {
4221 #ifdef DEBUG
4222                 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
4223 #endif
4224                 SetLastError (ERROR_ACCESS_DENIED);
4225                 return(FALSE);
4226         }
4227
4228 #ifdef HAVE_LARGE_FILE_SUPPORT
4229         offset = ((gint64)offset_high << 32) | offset_low;
4230         length = ((gint64)length_high << 32) | length_low;
4231
4232 #ifdef DEBUG
4233         g_message (G_GNUC_PRETTY_FUNCTION
4234                    ": Locking handle %p fd %d, offset %lld, length %lld",
4235                    handle, file_private_handle->fd_mapped.fd, offset, length);
4236 #endif
4237 #else
4238         offset = offset_low;
4239         length = length_low;
4240
4241 #ifdef DEBUG
4242         g_message (G_GNUC_PRETTY_FUNCTION
4243                    ": Locking handle %p fd %d, offset %ld, length %ld",
4244                    handle, file_private_handle->fd_mapped.fd, offset, length);
4245 #endif
4246 #endif
4247
4248         return(_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
4249                                        offset, length));
4250 }
4251
4252 gboolean UnlockFile (gpointer fd_handle, guint32 offset_low,
4253                      guint32 offset_high, guint32 length_low,
4254                      guint32 length_high)
4255 {
4256         struct _WapiHandle_file *file_handle;
4257         struct _WapiHandlePrivate_file *file_private_handle;
4258         gboolean ok;
4259         off_t offset, length;
4260         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4261
4262         if (handle == NULL) {
4263                 SetLastError (ERROR_INVALID_HANDLE);
4264                 return(FALSE);
4265         }
4266         
4267         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4268                                   (gpointer *)&file_handle,
4269                                   (gpointer *)&file_private_handle);
4270         if (ok == FALSE) {
4271                 g_warning (G_GNUC_PRETTY_FUNCTION
4272                            ": error looking up file handle %p", handle);
4273                 SetLastError (ERROR_INVALID_HANDLE);
4274                 return(FALSE);
4275         }
4276
4277         if (file_private_handle->fd_mapped.assigned == FALSE) {
4278                 SetLastError (ERROR_INVALID_HANDLE);
4279                 return(FALSE);
4280         }
4281         
4282         if (!(file_handle->fileaccess & GENERIC_READ) &&
4283             !(file_handle->fileaccess & GENERIC_WRITE) &&
4284             !(file_handle->fileaccess & GENERIC_ALL)) {
4285 #ifdef DEBUG
4286                 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
4287 #endif
4288                 SetLastError (ERROR_ACCESS_DENIED);
4289                 return(FALSE);
4290         }
4291
4292 #ifdef HAVE_LARGE_FILE_SUPPORT
4293         offset = ((gint64)offset_high << 32) | offset_low;
4294         length = ((gint64)length_high << 32) | length_low;
4295
4296 #ifdef DEBUG
4297         g_message (G_GNUC_PRETTY_FUNCTION
4298                    ": Unlocking handle %p fd %d, offset %lld, length %lld",
4299                    handle, file_private_handle->fd_mapped.fd, offset, length);
4300 #endif
4301 #else
4302         offset = offset_low;
4303         length = length_low;
4304
4305 #ifdef DEBUG
4306         g_message (G_GNUC_PRETTY_FUNCTION
4307                    ": Unlocking handle %p fd %d, offset %ld, length %ld",
4308                    handle, file_private_handle->fd_mapped.fd, offset, length);
4309 #endif
4310 #endif
4311
4312         return(_wapi_unlock_file_region (file_private_handle->fd_mapped.fd,
4313                                          offset, length));
4314 }